aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid S. Miller <davem@davemloft.net>2010-10-28 14:41:55 -0400
committerDavid S. Miller <davem@davemloft.net>2010-10-28 14:47:52 -0400
commit8acfe468b0384e834a303f08ebc4953d72fb690a (patch)
tree7ecee335efdbd283a122bcba1d5d9b533906142a
parent349f6c5c5d827db909a69e5b9e844e8623c8e881 (diff)
net: Limit socket I/O iovec total length to INT_MAX.
This helps protect us from overflow issues down in the individual protocol sendmsg/recvmsg handlers. Once we hit INT_MAX we truncate out the rest of the iovec by setting the iov_len members to zero. This works because: 1) For SOCK_STREAM and SOCK_SEQPACKET sockets, partial writes are allowed and the application will just continue with another write to send the rest of the data. 2) For datagram oriented sockets, where there must be a one-to-one correspondance between write() calls and packets on the wire, INT_MAX is going to be far larger than the packet size limit the protocol is going to check for and signal with -EMSGSIZE. Based upon a patch by Linus Torvalds. Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/socket.h2
-rw-r--r--net/compat.c10
-rw-r--r--net/core/iovec.c20
3 files changed, 16 insertions, 16 deletions
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 5146b50202ce..86b652fabf6e 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -322,7 +322,7 @@ extern int csum_partial_copy_fromiovecend(unsigned char *kdata,
322 int offset, 322 int offset,
323 unsigned int len, __wsum *csump); 323 unsigned int len, __wsum *csump);
324 324
325extern long verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode); 325extern int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode);
326extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len); 326extern int memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len);
327extern int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata, 327extern int memcpy_toiovecend(const struct iovec *v, unsigned char *kdata,
328 int offset, int len); 328 int offset, int len);
diff --git a/net/compat.c b/net/compat.c
index 63d260e81472..3649d5895361 100644
--- a/net/compat.c
+++ b/net/compat.c
@@ -41,10 +41,12 @@ static inline int iov_from_user_compat_to_kern(struct iovec *kiov,
41 compat_size_t len; 41 compat_size_t len;
42 42
43 if (get_user(len, &uiov32->iov_len) || 43 if (get_user(len, &uiov32->iov_len) ||
44 get_user(buf, &uiov32->iov_base)) { 44 get_user(buf, &uiov32->iov_base))
45 tot_len = -EFAULT; 45 return -EFAULT;
46 break; 46
47 } 47 if (len > INT_MAX - tot_len)
48 len = INT_MAX - tot_len;
49
48 tot_len += len; 50 tot_len += len;
49 kiov->iov_base = compat_ptr(buf); 51 kiov->iov_base = compat_ptr(buf);
50 kiov->iov_len = (__kernel_size_t) len; 52 kiov->iov_len = (__kernel_size_t) len;
diff --git a/net/core/iovec.c b/net/core/iovec.c
index 72aceb1fe4fa..c40f27e7d208 100644
--- a/net/core/iovec.c
+++ b/net/core/iovec.c
@@ -35,10 +35,9 @@
35 * in any case. 35 * in any case.
36 */ 36 */
37 37
38long verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode) 38int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address, int mode)
39{ 39{
40 int size, ct; 40 int size, ct, err;
41 long err;
42 41
43 if (m->msg_namelen) { 42 if (m->msg_namelen) {
44 if (mode == VERIFY_READ) { 43 if (mode == VERIFY_READ) {
@@ -62,14 +61,13 @@ long verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr *address,
62 err = 0; 61 err = 0;
63 62
64 for (ct = 0; ct < m->msg_iovlen; ct++) { 63 for (ct = 0; ct < m->msg_iovlen; ct++) {
65 err += iov[ct].iov_len; 64 size_t len = iov[ct].iov_len;
66 /* 65
67 * Goal is not to verify user data, but to prevent returning 66 if (len > INT_MAX - err) {
68 * negative value, which is interpreted as errno. 67 len = INT_MAX - err;
69 * Overflow is still possible, but it is harmless. 68 iov[ct].iov_len = len;
70 */ 69 }
71 if (err < 0) 70 err += len;
72 return -EMSGSIZE;
73 } 71 }
74 72
75 return err; 73 return err;