Blob Blame History Raw
From: Julian Wiedmann <jwi@linux.ibm.com>
Date: Thu, 5 Dec 2019 14:33:03 +0100
Subject: s390/qeth: ensure linear access to packet headers
Git-commit: f677fcb9aeb60c523ee36c1061ef2249b558d1b5
Patch-mainline: v5.5-rc1
References: git-fixes

When the RX path builds non-linear skbs, the packet headers can
currently spill over into page fragments. Depending on the packet type
and what fields we need to access in the headers, this could cause us
to go past the end of skb->data.

So for non-linear packets, copy precisely the length of the necessary
headers ('linear_len') into skb->data.
And don't copy more, upper-level protocols will peel whatever additional
packet headers they need.

Fixes: 4a71df50047f ("qeth: new qeth device driver")
Signed-off-by: Julian Wiedmann <jwi@linux.ibm.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Petr Tesarik <ptesarik@suse.com>
---
 drivers/s390/net/qeth_core_main.c |   64 ++++++++++++++++++--------------------
 1 file changed, 31 insertions(+), 33 deletions(-)

--- a/drivers/s390/net/qeth_core_main.c
+++ b/drivers/s390/net/qeth_core_main.c
@@ -5234,27 +5234,15 @@ out:
 }
 EXPORT_SYMBOL_GPL(qeth_core_hardsetup_card);
 
-static void qeth_create_skb_frag(struct qdio_buffer_element *element,
-				 struct sk_buff *skb, int offset, int data_len)
+static void qeth_create_skb_frag(struct sk_buff *skb, char *data, int data_len)
 {
-	struct page *page = virt_to_page(element->addr);
+	struct page *page = virt_to_page(data);
 	unsigned int next_frag;
 
-	/* first fill the linear space */
-	if (!skb->len) {
-		unsigned int linear = min(data_len, skb_tailroom(skb));
-
-		skb_put_data(skb, element->addr + offset, linear);
-		data_len -= linear;
-		if (!data_len)
-			return;
-		offset += linear;
-		/* fall through to add page frag for remaining data */
-	}
-
 	next_frag = skb_shinfo(skb)->nr_frags;
 	get_page(page);
-	skb_add_rx_frag(skb, next_frag, page, offset, data_len, data_len);
+	skb_add_rx_frag(skb, next_frag, page, offset_in_page(data), data_len,
+			data_len);
 }
 
 static inline int qeth_is_last_sbale(struct qdio_buffer_element *sbale)
@@ -5269,12 +5257,10 @@ struct sk_buff *qeth_core_get_next_skb(s
 {
 	struct qdio_buffer_element *element = *__element;
 	struct qdio_buffer *buffer = qethbuffer->buffer;
-	unsigned int linear_len;
+	unsigned int linear_len = 0;
 	int offset = *__offset;
 	struct sk_buff *skb;
 	int skb_len = 0;
-	void *data_ptr;
-	int data_len;
 	int headroom = 0;
 	int use_rx_sg = 0;
 
@@ -5336,26 +5322,41 @@ struct sk_buff *qeth_core_get_next_skb(s
 		skb = qethbuffer->rx_skb;
 		qethbuffer->rx_skb = NULL;
 	} else {
-		unsigned int linear = (use_rx_sg) ? QETH_RX_PULL_LEN : skb_len;
-
-		skb = napi_alloc_skb(&card->napi, linear + headroom);
+		if (!use_rx_sg)
+			linear_len = skb_len;
+		skb = napi_alloc_skb(&card->napi, linear_len + headroom);
 	}
 	if (!skb)
 		goto no_mem;
 	if (headroom)
 		skb_reserve(skb, headroom);
 
-	data_ptr = element->addr + offset;
 	while (skb_len) {
-		data_len = min(skb_len, (int)(element->length - offset));
+		int data_len = min(skb_len, (int)(element->length - offset));
+		char *data = element->addr + offset;
+
+		skb_len -= data_len;
+		offset += data_len;
+
+		/* Extract data from current element: */
 		if (data_len) {
-			if (use_rx_sg)
-				qeth_create_skb_frag(element, skb, offset,
-						     data_len);
-			else
-				skb_put_data(skb, data_ptr, data_len);
+			if (linear_len) {
+				unsigned int copy_len;
+
+				copy_len = min_t(unsigned int, linear_len,
+						 data_len);
+
+				skb_put_data(skb, data, copy_len);
+				linear_len -= copy_len;
+				data_len -= copy_len;
+				data += copy_len;
+			}
+
+			if (data_len)
+				qeth_create_skb_frag(skb, data, data_len);
 		}
-		skb_len -= data_len;
+
+		/* Step forward to next element: */
 		if (skb_len) {
 			if (qeth_is_last_sbale(element)) {
 				QETH_CARD_TEXT(card, 4, "unexeob");
@@ -5366,9 +5367,6 @@ struct sk_buff *qeth_core_get_next_skb(s
 			}
 			element++;
 			offset = 0;
-			data_ptr = element->addr;
-		} else {
-			offset += data_len;
 		}
 	}
 	*__element = element;