aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/macvtap.c
diff options
context:
space:
mode:
authorJason Wang <jasowang@redhat.com>2012-05-01 23:42:15 -0400
committerMichael S. Tsirkin <mst@redhat.com>2012-05-02 11:22:21 -0400
commitb92946e2919134ebe2a4083e4302236295ea2a73 (patch)
tree46887b70dd309f7bdd358f32b47b0aee766788d3 /drivers/net/macvtap.c
parent01d6657b388438def19c8baaea28e742b6ed32ec (diff)
macvtap: zerocopy: validate vectors before building skb
There're several reasons that the vectors need to be validated: - Return error when caller provides vectors whose num is greater than UIO_MAXIOV. - Linearize part of skb when userspace provides vectors grater than MAX_SKB_FRAGS. - Return error when userspace provides vectors whose total length may exceed - MAX_SKB_FRAGS * PAGE_SIZE. Signed-off-by: Jason Wang <jasowang@redhat.com> Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Diffstat (limited to 'drivers/net/macvtap.c')
-rw-r--r--drivers/net/macvtap.c25
1 files changed, 21 insertions, 4 deletions
diff --git a/drivers/net/macvtap.c b/drivers/net/macvtap.c
index a4ff694cea22..163559c16988 100644
--- a/drivers/net/macvtap.c
+++ b/drivers/net/macvtap.c
@@ -529,9 +529,10 @@ static int zerocopy_sg_from_iovec(struct sk_buff *skb, const struct iovec *from,
529 } 529 }
530 base = (unsigned long)from->iov_base + offset; 530 base = (unsigned long)from->iov_base + offset;
531 size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT; 531 size = ((base & ~PAGE_MASK) + len + ~PAGE_MASK) >> PAGE_SHIFT;
532 if (i + size > MAX_SKB_FRAGS)
533 return -EMSGSIZE;
532 num_pages = get_user_pages_fast(base, size, 0, &page[i]); 534 num_pages = get_user_pages_fast(base, size, 0, &page[i]);
533 if ((num_pages != size) || 535 if (num_pages != size) {
534 (num_pages > MAX_SKB_FRAGS - skb_shinfo(skb)->nr_frags)) {
535 for (i = 0; i < num_pages; i++) 536 for (i = 0; i < num_pages; i++)
536 put_page(page[i]); 537 put_page(page[i]);
537 return -EFAULT; 538 return -EFAULT;
@@ -651,7 +652,7 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
651 int err; 652 int err;
652 struct virtio_net_hdr vnet_hdr = { 0 }; 653 struct virtio_net_hdr vnet_hdr = { 0 };
653 int vnet_hdr_len = 0; 654 int vnet_hdr_len = 0;
654 int copylen; 655 int copylen = 0;
655 bool zerocopy = false; 656 bool zerocopy = false;
656 657
657 if (q->flags & IFF_VNET_HDR) { 658 if (q->flags & IFF_VNET_HDR) {
@@ -680,15 +681,31 @@ static ssize_t macvtap_get_user(struct macvtap_queue *q, struct msghdr *m,
680 if (unlikely(len < ETH_HLEN)) 681 if (unlikely(len < ETH_HLEN))
681 goto err; 682 goto err;
682 683
684 err = -EMSGSIZE;
685 if (unlikely(count > UIO_MAXIOV))
686 goto err;
687
683 if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY)) 688 if (m && m->msg_control && sock_flag(&q->sk, SOCK_ZEROCOPY))
684 zerocopy = true; 689 zerocopy = true;
685 690
686 if (zerocopy) { 691 if (zerocopy) {
692 /* Userspace may produce vectors with count greater than
693 * MAX_SKB_FRAGS, so we need to linearize parts of the skb
694 * to let the rest of data to be fit in the frags.
695 */
696 if (count > MAX_SKB_FRAGS) {
697 copylen = iov_length(iv, count - MAX_SKB_FRAGS);
698 if (copylen < vnet_hdr_len)
699 copylen = 0;
700 else
701 copylen -= vnet_hdr_len;
702 }
687 /* There are 256 bytes to be copied in skb, so there is enough 703 /* There are 256 bytes to be copied in skb, so there is enough
688 * room for skb expand head in case it is used. 704 * room for skb expand head in case it is used.
689 * The rest buffer is mapped from userspace. 705 * The rest buffer is mapped from userspace.
690 */ 706 */
691 copylen = vnet_hdr.hdr_len; 707 if (copylen < vnet_hdr.hdr_len)
708 copylen = vnet_hdr.hdr_len;
692 if (!copylen) 709 if (!copylen)
693 copylen = GOODCOPY_LEN; 710 copylen = GOODCOPY_LEN;
694 } else 711 } else