diff options
author | Al Viro <viro@zeniv.linux.org.uk> | 2014-11-09 22:33:45 -0500 |
---|---|---|
committer | Al Viro <viro@zeniv.linux.org.uk> | 2014-11-19 16:23:16 -0500 |
commit | 0844932009e1656726c6e9c369e694017b129378 (patch) | |
tree | 4de5bd394278e3cdf5f53c9cbc9513d1dc6fb469 | |
parent | 666547ff591cebdedc4679bf6b1b3f3383a8dea3 (diff) |
{compat_,}verify_iovec(): switch to generic copying of iovecs
use {compat_,}rw_copy_check_uvector(). As the result, we are
guaranteed that all iovecs seen in ->msg_iov by ->sendmsg()
and ->recvmsg() will pass access_ok().
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
-rw-r--r-- | net/compat.c | 51 | ||||
-rw-r--r-- | net/core/iovec.c | 37 | ||||
-rw-r--r-- | net/socket.c | 38 |
3 files changed, 37 insertions, 89 deletions
diff --git a/net/compat.c b/net/compat.c index 562e920b07f0..7b4b6ad13235 100644 --- a/net/compat.c +++ b/net/compat.c | |||
@@ -31,33 +31,6 @@ | |||
31 | #include <asm/uaccess.h> | 31 | #include <asm/uaccess.h> |
32 | #include <net/compat.h> | 32 | #include <net/compat.h> |
33 | 33 | ||
34 | static inline int iov_from_user_compat_to_kern(struct iovec *kiov, | ||
35 | struct compat_iovec __user *uiov32, | ||
36 | int niov) | ||
37 | { | ||
38 | int tot_len = 0; | ||
39 | |||
40 | while (niov > 0) { | ||
41 | compat_uptr_t buf; | ||
42 | compat_size_t len; | ||
43 | |||
44 | if (get_user(len, &uiov32->iov_len) || | ||
45 | get_user(buf, &uiov32->iov_base)) | ||
46 | return -EFAULT; | ||
47 | |||
48 | if (len > INT_MAX - tot_len) | ||
49 | len = INT_MAX - tot_len; | ||
50 | |||
51 | tot_len += len; | ||
52 | kiov->iov_base = compat_ptr(buf); | ||
53 | kiov->iov_len = (__kernel_size_t) len; | ||
54 | uiov32++; | ||
55 | kiov++; | ||
56 | niov--; | ||
57 | } | ||
58 | return tot_len; | ||
59 | } | ||
60 | |||
61 | int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg) | 34 | int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg) |
62 | { | 35 | { |
63 | compat_uptr_t tmp1, tmp2, tmp3; | 36 | compat_uptr_t tmp1, tmp2, tmp3; |
@@ -80,13 +53,15 @@ int get_compat_msghdr(struct msghdr *kmsg, struct compat_msghdr __user *umsg) | |||
80 | } | 53 | } |
81 | 54 | ||
82 | /* I've named the args so it is easy to tell whose space the pointers are in. */ | 55 | /* I've named the args so it is easy to tell whose space the pointers are in. */ |
83 | int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, | 56 | int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *iov, |
84 | struct sockaddr_storage *kern_address, int mode) | 57 | struct sockaddr_storage *kern_address, int mode) |
85 | { | 58 | { |
86 | int tot_len; | 59 | struct compat_iovec __user *p; |
60 | struct iovec *res; | ||
61 | int err; | ||
87 | 62 | ||
88 | if (kern_msg->msg_name && kern_msg->msg_namelen) { | 63 | if (kern_msg->msg_name && kern_msg->msg_namelen) { |
89 | if (mode == VERIFY_READ) { | 64 | if (mode == WRITE) { |
90 | int err = move_addr_to_kernel(kern_msg->msg_name, | 65 | int err = move_addr_to_kernel(kern_msg->msg_name, |
91 | kern_msg->msg_namelen, | 66 | kern_msg->msg_namelen, |
92 | kern_address); | 67 | kern_address); |
@@ -99,13 +74,17 @@ int verify_compat_iovec(struct msghdr *kern_msg, struct iovec *kern_iov, | |||
99 | kern_msg->msg_namelen = 0; | 74 | kern_msg->msg_namelen = 0; |
100 | } | 75 | } |
101 | 76 | ||
102 | tot_len = iov_from_user_compat_to_kern(kern_iov, | 77 | if (kern_msg->msg_iovlen > UIO_MAXIOV) |
103 | (struct compat_iovec __user *)kern_msg->msg_iov, | 78 | return -EMSGSIZE; |
104 | kern_msg->msg_iovlen); | ||
105 | if (tot_len >= 0) | ||
106 | kern_msg->msg_iov = kern_iov; | ||
107 | 79 | ||
108 | return tot_len; | 80 | p = (struct compat_iovec __user *)kern_msg->msg_iov; |
81 | err = compat_rw_copy_check_uvector(mode, p, kern_msg->msg_iovlen, | ||
82 | UIO_FASTIOV, iov, &res); | ||
83 | if (err >= 0) | ||
84 | kern_msg->msg_iov = res; | ||
85 | else if (res != iov) | ||
86 | kfree(res); | ||
87 | return err; | ||
109 | } | 88 | } |
110 | 89 | ||
111 | /* Bleech... */ | 90 | /* Bleech... */ |
diff --git a/net/core/iovec.c b/net/core/iovec.c index e1ec45ab1e63..86beeea61d72 100644 --- a/net/core/iovec.c +++ b/net/core/iovec.c | |||
@@ -37,13 +37,13 @@ | |||
37 | 37 | ||
38 | int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode) | 38 | int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *address, int mode) |
39 | { | 39 | { |
40 | int size, ct, err; | 40 | struct iovec *res; |
41 | int err; | ||
41 | 42 | ||
42 | if (m->msg_name && m->msg_namelen) { | 43 | if (m->msg_name && m->msg_namelen) { |
43 | if (mode == VERIFY_READ) { | 44 | if (mode == WRITE) { |
44 | void __user *namep; | 45 | void __user *namep = (void __user __force *)m->msg_name; |
45 | namep = (void __user __force *) m->msg_name; | 46 | int err = move_addr_to_kernel(namep, m->msg_namelen, |
46 | err = move_addr_to_kernel(namep, m->msg_namelen, | ||
47 | address); | 47 | address); |
48 | if (err < 0) | 48 | if (err < 0) |
49 | return err; | 49 | return err; |
@@ -53,24 +53,15 @@ int verify_iovec(struct msghdr *m, struct iovec *iov, struct sockaddr_storage *a | |||
53 | m->msg_name = NULL; | 53 | m->msg_name = NULL; |
54 | m->msg_namelen = 0; | 54 | m->msg_namelen = 0; |
55 | } | 55 | } |
56 | 56 | if (m->msg_iovlen > UIO_MAXIOV) | |
57 | size = m->msg_iovlen * sizeof(struct iovec); | 57 | return -EMSGSIZE; |
58 | if (copy_from_user(iov, (void __user __force *) m->msg_iov, size)) | 58 | |
59 | return -EFAULT; | 59 | err = rw_copy_check_uvector(mode, (void __user __force *)m->msg_iov, |
60 | 60 | m->msg_iovlen, UIO_FASTIOV, iov, &res); | |
61 | m->msg_iov = iov; | 61 | if (err >= 0) |
62 | err = 0; | 62 | m->msg_iov = res; |
63 | 63 | else if (res != iov) | |
64 | for (ct = 0; ct < m->msg_iovlen; ct++) { | 64 | kfree(res); |
65 | size_t len = iov[ct].iov_len; | ||
66 | |||
67 | if (len > INT_MAX - err) { | ||
68 | len = INT_MAX - err; | ||
69 | iov[ct].iov_len = len; | ||
70 | } | ||
71 | err += len; | ||
72 | } | ||
73 | |||
74 | return err; | 65 | return err; |
75 | } | 66 | } |
76 | 67 | ||
diff --git a/net/socket.c b/net/socket.c index 0ae8147e3acc..59020f0b583b 100644 --- a/net/socket.c +++ b/net/socket.c | |||
@@ -2032,24 +2032,14 @@ static int ___sys_sendmsg(struct socket *sock, struct user_msghdr __user *msg, | |||
2032 | return err; | 2032 | return err; |
2033 | } | 2033 | } |
2034 | 2034 | ||
2035 | if (msg_sys->msg_iovlen > UIO_FASTIOV) { | ||
2036 | err = -EMSGSIZE; | ||
2037 | if (msg_sys->msg_iovlen > UIO_MAXIOV) | ||
2038 | goto out; | ||
2039 | err = -ENOMEM; | ||
2040 | iov = kmalloc(msg_sys->msg_iovlen * sizeof(struct iovec), | ||
2041 | GFP_KERNEL); | ||
2042 | if (!iov) | ||
2043 | goto out; | ||
2044 | } | ||
2045 | |||
2046 | /* This will also move the address data into kernel space */ | 2035 | /* This will also move the address data into kernel space */ |
2047 | if (MSG_CMSG_COMPAT & flags) { | 2036 | if (MSG_CMSG_COMPAT & flags) |
2048 | err = verify_compat_iovec(msg_sys, iov, &address, VERIFY_READ); | 2037 | err = verify_compat_iovec(msg_sys, iovstack, &address, WRITE); |
2049 | } else | 2038 | else |
2050 | err = verify_iovec(msg_sys, iov, &address, VERIFY_READ); | 2039 | err = verify_iovec(msg_sys, iovstack, &address, WRITE); |
2051 | if (err < 0) | 2040 | if (err < 0) |
2052 | goto out_freeiov; | 2041 | goto out_freeiov; |
2042 | iov = msg_sys->msg_iov; | ||
2053 | total_len = err; | 2043 | total_len = err; |
2054 | 2044 | ||
2055 | err = -ENOBUFS; | 2045 | err = -ENOBUFS; |
@@ -2118,7 +2108,6 @@ out_freectl: | |||
2118 | out_freeiov: | 2108 | out_freeiov: |
2119 | if (iov != iovstack) | 2109 | if (iov != iovstack) |
2120 | kfree(iov); | 2110 | kfree(iov); |
2121 | out: | ||
2122 | return err; | 2111 | return err; |
2123 | } | 2112 | } |
2124 | 2113 | ||
@@ -2244,28 +2233,18 @@ static int ___sys_recvmsg(struct socket *sock, struct user_msghdr __user *msg, | |||
2244 | return err; | 2233 | return err; |
2245 | } | 2234 | } |
2246 | 2235 | ||
2247 | if (msg_sys->msg_iovlen > UIO_FASTIOV) { | ||
2248 | err = -EMSGSIZE; | ||
2249 | if (msg_sys->msg_iovlen > UIO_MAXIOV) | ||
2250 | goto out; | ||
2251 | err = -ENOMEM; | ||
2252 | iov = kmalloc(msg_sys->msg_iovlen * sizeof(struct iovec), | ||
2253 | GFP_KERNEL); | ||
2254 | if (!iov) | ||
2255 | goto out; | ||
2256 | } | ||
2257 | |||
2258 | /* Save the user-mode address (verify_iovec will change the | 2236 | /* Save the user-mode address (verify_iovec will change the |
2259 | * kernel msghdr to use the kernel address space) | 2237 | * kernel msghdr to use the kernel address space) |
2260 | */ | 2238 | */ |
2261 | uaddr = (__force void __user *)msg_sys->msg_name; | 2239 | uaddr = (__force void __user *)msg_sys->msg_name; |
2262 | uaddr_len = COMPAT_NAMELEN(msg); | 2240 | uaddr_len = COMPAT_NAMELEN(msg); |
2263 | if (MSG_CMSG_COMPAT & flags) | 2241 | if (MSG_CMSG_COMPAT & flags) |
2264 | err = verify_compat_iovec(msg_sys, iov, &addr, VERIFY_WRITE); | 2242 | err = verify_compat_iovec(msg_sys, iovstack, &addr, READ); |
2265 | else | 2243 | else |
2266 | err = verify_iovec(msg_sys, iov, &addr, VERIFY_WRITE); | 2244 | err = verify_iovec(msg_sys, iovstack, &addr, READ); |
2267 | if (err < 0) | 2245 | if (err < 0) |
2268 | goto out_freeiov; | 2246 | goto out_freeiov; |
2247 | iov = msg_sys->msg_iov; | ||
2269 | total_len = err; | 2248 | total_len = err; |
2270 | 2249 | ||
2271 | cmsg_ptr = (unsigned long)msg_sys->msg_control; | 2250 | cmsg_ptr = (unsigned long)msg_sys->msg_control; |
@@ -2306,7 +2285,6 @@ static int ___sys_recvmsg(struct socket *sock, struct user_msghdr __user *msg, | |||
2306 | out_freeiov: | 2285 | out_freeiov: |
2307 | if (iov != iovstack) | 2286 | if (iov != iovstack) |
2308 | kfree(iov); | 2287 | kfree(iov); |
2309 | out: | ||
2310 | return err; | 2288 | return err; |
2311 | } | 2289 | } |
2312 | 2290 | ||