diff options
author | Michael S. Tsirkin <mst@redhat.com> | 2009-04-19 21:26:11 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2009-04-21 08:42:46 -0400 |
commit | 6f26c9a7555e5bcca3560919db9b852015077dae (patch) | |
tree | 9ca7509cd0cbbdae158b752c234cb23add8370f0 | |
parent | 43b39dcdbdf823a1c0ac1f2aa2d76bd2f210adc8 (diff) |
tun: fix tun_chr_aio_write so that aio works
aio_write gets const struct iovec * but tun_chr_aio_write casts this to struct
iovec * and modifies the iovec. As a result, attempts to use io_submit
to send packets to a tun device fail with weird errors such as EINVAL.
Since tun is the only user of skb_copy_datagram_from_iovec, we can
fix this simply by changing the later so that it does not
touch the iovec passed to it.
Signed-off-by: Michael S. Tsirkin <mst@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/tun.c | 13 | ||||
-rw-r--r-- | include/linux/skbuff.h | 3 | ||||
-rw-r--r-- | include/linux/socket.h | 4 | ||||
-rw-r--r-- | net/core/datagram.c | 20 | ||||
-rw-r--r-- | net/core/iovec.c | 7 |
5 files changed, 30 insertions, 17 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); |
diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index af2b21bdda83..1b5c3d298f43 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h | |||
@@ -1715,7 +1715,8 @@ extern int skb_copy_and_csum_datagram_iovec(struct sk_buff *skb, | |||
1715 | struct iovec *iov); | 1715 | struct iovec *iov); |
1716 | extern int skb_copy_datagram_from_iovec(struct sk_buff *skb, | 1716 | extern int skb_copy_datagram_from_iovec(struct sk_buff *skb, |
1717 | int offset, | 1717 | int offset, |
1718 | struct iovec *from, | 1718 | const struct iovec *from, |
1719 | int from_offset, | ||
1719 | int len); | 1720 | int len); |
1720 | extern int skb_copy_datagram_const_iovec(const struct sk_buff *from, | 1721 | extern int skb_copy_datagram_const_iovec(const struct sk_buff *from, |
1721 | int offset, | 1722 | int offset, |
diff --git a/include/linux/socket.h b/include/linux/socket.h index 171b08db9c4f..42a0396f2c59 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h | |||
@@ -309,8 +309,8 @@ struct ucred { | |||
309 | 309 | ||
310 | #ifdef __KERNEL__ | 310 | #ifdef __KERNEL__ |
311 | extern int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len); | 311 | extern int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len); |
312 | extern int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, | 312 | extern int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, |
313 | int offset, int len); | 313 | int offset, int len); |
314 | extern int csum_partial_copy_fromiovecend(unsigned char *kdata, | 314 | extern int csum_partial_copy_fromiovecend(unsigned char *kdata, |
315 | struct iovec *iov, | 315 | struct iovec *iov, |
316 | int offset, | 316 | int offset, |
diff --git a/net/core/datagram.c b/net/core/datagram.c index 4dbb05cd572b..914d5fa773b4 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c | |||
@@ -435,13 +435,15 @@ EXPORT_SYMBOL(skb_copy_datagram_const_iovec); | |||
435 | * @skb: buffer to copy | 435 | * @skb: buffer to copy |
436 | * @offset: offset in the buffer to start copying to | 436 | * @offset: offset in the buffer to start copying to |
437 | * @from: io vector to copy to | 437 | * @from: io vector to copy to |
438 | * @from_offset: offset in the io vector to start copying from | ||
438 | * @len: amount of data to copy to buffer from iovec | 439 | * @len: amount of data to copy to buffer from iovec |
439 | * | 440 | * |
440 | * Returns 0 or -EFAULT. | 441 | * Returns 0 or -EFAULT. |
441 | * Note: the iovec is modified during the copy. | 442 | * Note: the iovec is not modified during the copy. |
442 | */ | 443 | */ |
443 | int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, | 444 | int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, |
444 | struct iovec *from, int len) | 445 | const struct iovec *from, int from_offset, |
446 | int len) | ||
445 | { | 447 | { |
446 | int start = skb_headlen(skb); | 448 | int start = skb_headlen(skb); |
447 | int i, copy = start - offset; | 449 | int i, copy = start - offset; |
@@ -450,11 +452,12 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, | |||
450 | if (copy > 0) { | 452 | if (copy > 0) { |
451 | if (copy > len) | 453 | if (copy > len) |
452 | copy = len; | 454 | copy = len; |
453 | if (memcpy_fromiovec(skb->data + offset, from, copy)) | 455 | if (memcpy_fromiovecend(skb->data + offset, from, 0, copy)) |
454 | goto fault; | 456 | goto fault; |
455 | if ((len -= copy) == 0) | 457 | if ((len -= copy) == 0) |
456 | return 0; | 458 | return 0; |
457 | offset += copy; | 459 | offset += copy; |
460 | from_offset += copy; | ||
458 | } | 461 | } |
459 | 462 | ||
460 | /* Copy paged appendix. Hmm... why does this look so complicated? */ | 463 | /* Copy paged appendix. Hmm... why does this look so complicated? */ |
@@ -473,8 +476,9 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, | |||
473 | if (copy > len) | 476 | if (copy > len) |
474 | copy = len; | 477 | copy = len; |
475 | vaddr = kmap(page); | 478 | vaddr = kmap(page); |
476 | err = memcpy_fromiovec(vaddr + frag->page_offset + | 479 | err = memcpy_fromiovecend(vaddr + frag->page_offset + |
477 | offset - start, from, copy); | 480 | offset - start, |
481 | from, from_offset, copy); | ||
478 | kunmap(page); | 482 | kunmap(page); |
479 | if (err) | 483 | if (err) |
480 | goto fault; | 484 | goto fault; |
@@ -482,6 +486,7 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, | |||
482 | if (!(len -= copy)) | 486 | if (!(len -= copy)) |
483 | return 0; | 487 | return 0; |
484 | offset += copy; | 488 | offset += copy; |
489 | from_offset += copy; | ||
485 | } | 490 | } |
486 | start = end; | 491 | start = end; |
487 | } | 492 | } |
@@ -500,11 +505,14 @@ int skb_copy_datagram_from_iovec(struct sk_buff *skb, int offset, | |||
500 | copy = len; | 505 | copy = len; |
501 | if (skb_copy_datagram_from_iovec(list, | 506 | if (skb_copy_datagram_from_iovec(list, |
502 | offset - start, | 507 | offset - start, |
503 | from, copy)) | 508 | from, |
509 | from_offset, | ||
510 | copy)) | ||
504 | goto fault; | 511 | goto fault; |
505 | if ((len -= copy) == 0) | 512 | if ((len -= copy) == 0) |
506 | return 0; | 513 | return 0; |
507 | offset += copy; | 514 | offset += copy; |
515 | from_offset += copy; | ||
508 | } | 516 | } |
509 | start = end; | 517 | start = end; |
510 | } | 518 | } |
diff --git a/net/core/iovec.c b/net/core/iovec.c index a215545c0a34..40a76ce19d9f 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c | |||
@@ -147,10 +147,11 @@ int memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len) | |||
147 | } | 147 | } |
148 | 148 | ||
149 | /* | 149 | /* |
150 | * For use with ip_build_xmit | 150 | * Copy iovec from kernel. Returns -EFAULT on error. |
151 | */ | 151 | */ |
152 | int memcpy_fromiovecend(unsigned char *kdata, struct iovec *iov, int offset, | 152 | |
153 | int len) | 153 | int memcpy_fromiovecend(unsigned char *kdata, const struct iovec *iov, |
154 | int offset, int len) | ||
154 | { | 155 | { |
155 | /* Skip over the finished iovecs */ | 156 | /* Skip over the finished iovecs */ |
156 | while (offset >= iov->iov_len) { | 157 | while (offset >= iov->iov_len) { |