diff options
Diffstat (limited to 'drivers/net/tun.c')
-rw-r--r-- | drivers/net/tun.c | 13 |
1 files changed, 8 insertions, 5 deletions
diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 3b513e29d392..589f0ca668d6 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c | |||
@@ -540,31 +540,34 @@ static inline struct sk_buff *tun_alloc_skb(struct tun_struct *tun, | |||
540 | 540 | ||
541 | /* Get packet from user space buffer */ | 541 | /* Get packet from user space buffer */ |
542 | static __inline__ ssize_t tun_get_user(struct tun_struct *tun, | 542 | static __inline__ ssize_t tun_get_user(struct tun_struct *tun, |
543 | struct iovec *iv, size_t count, | 543 | const struct iovec *iv, size_t count, |
544 | int noblock) | 544 | int noblock) |
545 | { | 545 | { |
546 | struct tun_pi pi = { 0, cpu_to_be16(ETH_P_IP) }; | 546 | struct tun_pi pi = { 0, cpu_to_be16(ETH_P_IP) }; |
547 | struct sk_buff *skb; | 547 | struct sk_buff *skb; |
548 | size_t len = count, align = 0; | 548 | size_t len = count, align = 0; |
549 | struct virtio_net_hdr gso = { 0 }; | 549 | struct virtio_net_hdr gso = { 0 }; |
550 | int offset = 0; | ||
550 | 551 | ||
551 | if (!(tun->flags & TUN_NO_PI)) { | 552 | if (!(tun->flags & TUN_NO_PI)) { |
552 | if ((len -= sizeof(pi)) > count) | 553 | if ((len -= sizeof(pi)) > count) |
553 | return -EINVAL; | 554 | return -EINVAL; |
554 | 555 | ||
555 | if(memcpy_fromiovec((void *)&pi, iv, sizeof(pi))) | 556 | if (memcpy_fromiovecend((void *)&pi, iv, 0, sizeof(pi))) |
556 | return -EFAULT; | 557 | return -EFAULT; |
558 | offset += sizeof(pi); | ||
557 | } | 559 | } |
558 | 560 | ||
559 | if (tun->flags & TUN_VNET_HDR) { | 561 | if (tun->flags & TUN_VNET_HDR) { |
560 | if ((len -= sizeof(gso)) > count) | 562 | if ((len -= sizeof(gso)) > count) |
561 | return -EINVAL; | 563 | return -EINVAL; |
562 | 564 | ||
563 | if (memcpy_fromiovec((void *)&gso, iv, sizeof(gso))) | 565 | if (memcpy_fromiovecend((void *)&gso, iv, offset, sizeof(gso))) |
564 | return -EFAULT; | 566 | return -EFAULT; |
565 | 567 | ||
566 | if (gso.hdr_len > len) | 568 | if (gso.hdr_len > len) |
567 | return -EINVAL; | 569 | return -EINVAL; |
570 | offset += sizeof(pi); | ||
568 | } | 571 | } |
569 | 572 | ||
570 | if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) { | 573 | if ((tun->flags & TUN_TYPE_MASK) == TUN_TAP_DEV) { |
@@ -581,7 +584,7 @@ static __inline__ ssize_t tun_get_user(struct tun_struct *tun, | |||
581 | return PTR_ERR(skb); | 584 | return PTR_ERR(skb); |
582 | } | 585 | } |
583 | 586 | ||
584 | if (skb_copy_datagram_from_iovec(skb, 0, iv, len)) { | 587 | if (skb_copy_datagram_from_iovec(skb, 0, iv, offset, len)) { |
585 | tun->dev->stats.rx_dropped++; | 588 | tun->dev->stats.rx_dropped++; |
586 | kfree_skb(skb); | 589 | kfree_skb(skb); |
587 | return -EFAULT; | 590 | return -EFAULT; |
@@ -673,7 +676,7 @@ static ssize_t tun_chr_aio_write(struct kiocb *iocb, const struct iovec *iv, | |||
673 | 676 | ||
674 | DBG(KERN_INFO "%s: tun_chr_write %ld\n", tun->dev->name, count); | 677 | DBG(KERN_INFO "%s: tun_chr_write %ld\n", tun->dev->name, count); |
675 | 678 | ||
676 | result = tun_get_user(tun, (struct iovec *)iv, iov_length(iv, count), | 679 | result = tun_get_user(tun, iv, iov_length(iv, count), |
677 | file->f_flags & O_NONBLOCK); | 680 | file->f_flags & O_NONBLOCK); |
678 | 681 | ||
679 | tun_put(tun); | 682 | tun_put(tun); |