From c31c151b1c4a29da4dc92212aa8648fb4f8557b9 Mon Sep 17 00:00:00 2001 From: Haiyang Zhang Date: Thu, 2 Feb 2012 07:18:00 +0000 Subject: [PATCH] net/hyperv: Fix the page buffer when an RNDIS message goes beyond page boundary There is a possible data corruption if an RNDIS message goes beyond page boundary in the sending code path. This patch fixes the problem. Signed-off-by: Haiyang Zhang Signed-off-by: K. Y. Srinivasan Signed-off-by: David S. Miller --- drivers/net/hyperv/netvsc_drv.c | 8 ++++---- drivers/net/hyperv/rndis_filter.c | 13 +++++++++++++ include/linux/hyperv.h | 2 +- 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/drivers/net/hyperv/netvsc_drv.c b/drivers/net/hyperv/netvsc_drv.c index 69193fcb7648..466c58a7353d 100644 --- a/drivers/net/hyperv/netvsc_drv.c +++ b/drivers/net/hyperv/netvsc_drv.c @@ -151,10 +151,10 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) int ret; unsigned int i, num_pages, npg_data; - /* Add multipage for skb->data and additional one for RNDIS */ + /* Add multipages for skb->data and additional 2 for RNDIS */ npg_data = (((unsigned long)skb->data + skb_headlen(skb) - 1) >> PAGE_SHIFT) - ((unsigned long)skb->data >> PAGE_SHIFT) + 1; - num_pages = skb_shinfo(skb)->nr_frags + npg_data + 1; + num_pages = skb_shinfo(skb)->nr_frags + npg_data + 2; /* Allocate a netvsc packet based on # of frags. */ packet = kzalloc(sizeof(struct hv_netvsc_packet) + @@ -173,8 +173,8 @@ static int netvsc_start_xmit(struct sk_buff *skb, struct net_device *net) sizeof(struct hv_netvsc_packet) + (num_pages * sizeof(struct hv_page_buffer)); - /* Setup the rndis header */ - packet->page_buf_cnt = num_pages; + /* If the rndis msg goes beyond 1 page, we will add 1 later */ + packet->page_buf_cnt = num_pages - 1; /* Initialize it from the skb */ packet->total_data_buflen = skb->len; diff --git a/drivers/net/hyperv/rndis_filter.c b/drivers/net/hyperv/rndis_filter.c index dc2e3849573b..133b7fbf8595 100644 --- a/drivers/net/hyperv/rndis_filter.c +++ b/drivers/net/hyperv/rndis_filter.c @@ -797,6 +797,19 @@ int rndis_filter_send(struct hv_device *dev, (unsigned long)rndisMessage & (PAGE_SIZE-1); pkt->page_buf[0].len = rndisMessageSize; + /* Add one page_buf if the rndis msg goes beyond page boundary */ + if (pkt->page_buf[0].offset + rndisMessageSize > PAGE_SIZE) { + int i; + for (i = pkt->page_buf_cnt; i > 1; i--) + pkt->page_buf[i] = pkt->page_buf[i-1]; + pkt->page_buf_cnt++; + pkt->page_buf[0].len = PAGE_SIZE - pkt->page_buf[0].offset; + pkt->page_buf[1].pfn = virt_to_phys((void *)((ulong) + rndisMessage + pkt->page_buf[0].len)) >> PAGE_SHIFT; + pkt->page_buf[1].offset = 0; + pkt->page_buf[1].len = rndisMessageSize - pkt->page_buf[0].len; + } + /* Save the packet send completion and context */ filterPacket->completion = pkt->completion.send.send_completion; filterPacket->completion_ctx = diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 62b908e0e591..0ae065a5fcb2 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -35,7 +35,7 @@ #include -#define MAX_PAGE_BUFFER_COUNT 18 +#define MAX_PAGE_BUFFER_COUNT 19 #define MAX_MULTIPAGE_BUFFER_COUNT 32 /* 128K */ #pragma pack(push, 1)