This patch avoids the correctness issue on the user-space mapping by just copying the memory. diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 4c15dc4..d75cfd2 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -62,6 +62,7 @@ #include <linux/if_ether.h> #include <linux/if_tun.h> #include <linux/crc32.h> +#include <linux/highmem.h> #include <linux/virtio_net.h> #include <net/net_namespace.h> @@ -257,65 +258,55 @@ static struct sk_buff *copy_user_skb(size_t align, struct iovec *iv, size_t len) } /* This will fail if they give us a crazy iovec, but that's their own fault. */ -static int get_user_skb_frags(const struct iovec *iv, size_t count, - struct skb_frag_struct *f) +static int get_user_skb_frags(struct iovec *iv, size_t count, + struct skb_shared_info *sinfo) { - unsigned int i, j, num_pg = 0; + struct skb_frag_struct *f = sinfo->frags; + unsigned int i; int err; - struct page *pages[MAX_SKB_FRAGS]; - down_read(¤t->mm->mmap_sem); + f->page = NULL; + for (i = 0; i < count; i++) { - int n, npages; - unsigned long base, len; - base = (unsigned long)iv[i].iov_base; - len = (unsigned long)iv[i].iov_len; + unsigned int len = iv[i].iov_len; - if (len == 0) - continue; + while (len) { + void *virt; + unsigned int copy; - /* How many pages will this take? */ - npages = 1 + (base + len - 1)/PAGE_SIZE - base/PAGE_SIZE; - if (unlikely(num_pg + npages > MAX_SKB_FRAGS)) { - err = -ENOSPC; - goto fail; - } - n = get_user_pages(current, current->mm, base, npages, - 0, 0, pages, NULL); - if (unlikely(n < 0)) { - err = n; - goto fail; - } + if (!f->page) { + f->page = alloc_page(GFP_KERNEL | + __GFP_HIGHMEM); + if (!f->page) + return -ENOMEM; - /* Transfer pages to the frag array */ - for (j = 0; j < n; j++) { - f[num_pg].page = pages[j]; - if (j == 0) { - f[num_pg].page_offset = offset_in_page(base); - f[num_pg].size = min(len, PAGE_SIZE - - f[num_pg].page_offset); - } else { - f[num_pg].page_offset = 0; - f[num_pg].size = min(len, PAGE_SIZE); + f->page_offset = 0; + f->size = 0; + sinfo->nr_frags++; } - len -= f[num_pg].size; - base += f[num_pg].size; - num_pg++; - } - if (unlikely(n != npages)) { - err = -EFAULT; - goto fail; + copy = PAGE_SIZE - f->size; + if (copy > len) + copy = len; + + virt = kmap_atomic(f->page, KM_USER0); + err = memcpy_fromiovec(virt + f->size, iv, copy); + kunmap_atomic(virt, KM_USER0); + + if (err) + return err; + + f->size += copy; + if (f->size == PAGE_SIZE) { + if (sinfo->nr_frags >= MAX_SKB_FRAGS) + return -EMSGSIZE; + (++f)->page = NULL; + } + len -= copy; } } - up_read(¤t->mm->mmap_sem); - return num_pg; -fail: - for (i = 0; i < num_pg; i++) - put_page(f[i].page); - up_read(¤t->mm->mmap_sem); - return err; + return 0; } @@ -360,13 +351,13 @@ static struct sk_buff *map_user_skb(const struct virtio_net_hdr *gso, goto fail; } - err = get_user_skb_frags(iv, count, sinfo->frags); + err = get_user_skb_frags(iv, count, sinfo); if (err < 0) goto fail; - sinfo->nr_frags = err; skb->len += len; skb->data_len += len; + skb->truesize += len; return skb; -- Visit Openswan at http://www.openswan.org/ Email: Herbert Xu ~{PmV>HI~} <herbert@xxxxxxxxxxxxxxxxxxx> Home Page: http://gondor.apana.org.au/~herbert/ PGP Key: http://gondor.apana.org.au/~herbert/pubkey.txt _______________________________________________ Virtualization mailing list Virtualization@xxxxxxxxxxxxxxxxxxxxxxxxxx https://lists.linux-foundation.org/mailman/listinfo/virtualization