]>
Commit | Line | Data |
---|---|---|
bcd4f083 GKH |
1 | From 8acfe468b0384e834a303f08ebc4953d72fb690a Mon Sep 17 00:00:00 2001 |
2 | From: David S. Miller <davem@davemloft.net> | |
3 | Date: Thu, 28 Oct 2010 11:41:55 -0700 | |
4 | Subject: net: Limit socket I/O iovec total length to INT_MAX. | |
5 | ||
6 | From: David S. Miller <davem@davemloft.net> | |
7 | ||
8 | commit 8acfe468b0384e834a303f08ebc4953d72fb690a upstream. | |
9 | ||
10 | This helps protect us from overflow issues down in the | |
11 | individual protocol sendmsg/recvmsg handlers. Once | |
12 | we hit INT_MAX we truncate out the rest of the iovec | |
13 | by setting the iov_len members to zero. | |
14 | ||
15 | This works because: | |
16 | ||
17 | 1) For SOCK_STREAM and SOCK_SEQPACKET sockets, partial | |
18 | writes are allowed and the application will just continue | |
19 | with another write to send the rest of the data. | |
20 | ||
21 | 2) For datagram oriented sockets, where there must be a | |
22 | one-to-one correspondance between write() calls and | |
23 | packets on the wire, INT_MAX is going to be far larger | |
24 | than the packet size limit the protocol is going to | |
25 | check for and signal with -EMSGSIZE. | |
26 | ||
27 | Based upon a patch by Linus Torvalds. | |
28 | ||
29 | Signed-off-by: David S. Miller <davem@davemloft.net> | |
30 | Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> | |
31 | ||
32 | --- | |
33 | include/linux/socket.h | 2 +- | |
34 | net/compat.c | 10 ++++++---- | |
35 | net/core/iovec.c | 20 +++++++++----------- | |
36 | 3 files changed, 16 insertions(+), 16 deletions(-) | |
37 | ||
38 | --- a/include/linux/socket.h | |
39 | +++ b/include/linux/socket.h | |
40 | @@ -322,7 +322,7 @@ extern int csum_partial_copy_fromiovecen | |
41 | int offset, | |
42 | unsigned int len, __wsum *csump); | |
43 | ||
44 | -extern long verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode); | |
45 | +extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode); | |
46 | extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len); | |
47 | extern int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata, | |
48 | int offset, int len); | |
49 | --- a/net/compat.c | |
50 | +++ b/net/compat.c | |
51 | @@ -41,10 +41,12 @@ static inline int iov_from_user_compat_t | |
52 | compat_size_t len; | |
53 | ||
54 | if (get_user(len, &uiov32->iov_len) || | |
55 | - get_user(buf, &uiov32->iov_base)) { | |
56 | - tot_len = -EFAULT; | |
57 | - break; | |
58 | - } | |
59 | + get_user(buf, &uiov32->iov_base)) | |
60 | + return -EFAULT; | |
61 | + | |
62 | + if (len > INT_MAX - tot_len) | |
63 | + len = INT_MAX - tot_len; | |
64 | + | |
65 | tot_len += len; | |
66 | kiov->iov_base = compat_ptr(buf); | |
67 | kiov->iov_len = (__kernel_size_t) len; | |
68 | --- a/net/core/iovec.c | |
69 | +++ b/net/core/iovec.c | |
70 | @@ -35,10 +35,9 @@ | |
71 | * in any case. | |
72 | */ | |
73 | ||
74 | -long verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode) | |
75 | +int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode) | |
76 | { | |
77 | - int size, ct; | |
78 | - long err; | |
79 | + int size, ct, err; | |
80 | ||
81 | if (m->msg_namelen) { | |
82 | if (mode == VERIFY_READ) { | |
83 | @@ -60,14 +59,13 @@ long verify_iovec(struct msghdr *m, stru | |
84 | err = 0; | |
85 | ||
86 | for (ct = 0; ct < m->msg_iovlen; ct++) { | |
87 | - err += iov[ct].iov_len; | |
88 | - /* | |
89 | - * Goal is not to verify user data, but to prevent returning | |
90 | - * negative value, which is interpreted as errno. | |
91 | - * Overflow is still possible, but it is harmless. | |
92 | - */ | |
93 | - if (err < 0) | |
94 | - return -EMSGSIZE; | |
95 | + size_t len = iov[ct].iov_len; | |
96 | + | |
97 | + if (len > INT_MAX - err) { | |
98 | + len = INT_MAX - err; | |
99 | + iov[ct].iov_len = len; | |
100 | + } | |
101 | + err += len; | |
102 | } | |
103 | ||
104 | return err; |