diff options
author | Vlad Yasevich <vladislav.yasevich@hp.com> | 2007-04-29 00:09:04 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2007-04-29 00:09:04 -0400 |
commit | aad97f38b71dd2ecd730b3a3dce8264d13fbcd56 (patch) | |
tree | 6eade05e50c61914d0a451745881c9d6fe41f829 /net | |
parent | 5a1b5898ee9e0bf68a86609ecb9775457b1857a5 (diff) |
[SCTP]: Fix sctp_getsockopt_local_addrs_old() to use local storage.
sctp_getsockopt_local_addrs_old() in net/sctp/socket.c calls
copy_to_user() while the spinlock addr_lock is held. this should not
be done as copy_to_user() might sleep. the call to
sctp_copy_laddrs_to_user() while holding the lock is also problematic
as it calls copy_to_user()
Signed-off-by: Vlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net')
-rw-r--r-- | net/sctp/socket.c | 96 |
1 files changed, 60 insertions, 36 deletions
diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 11938fb20395..2fc0a92caa78 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c | |||
@@ -3987,7 +3987,7 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len, | |||
3987 | memcpy(&temp, &from->ipaddr, sizeof(temp)); | 3987 | memcpy(&temp, &from->ipaddr, sizeof(temp)); |
3988 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); | 3988 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); |
3989 | addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len; | 3989 | addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len; |
3990 | if(space_left < addrlen) | 3990 | if (space_left < addrlen) |
3991 | return -ENOMEM; | 3991 | return -ENOMEM; |
3992 | if (copy_to_user(to, &temp, addrlen)) | 3992 | if (copy_to_user(to, &temp, addrlen)) |
3993 | return -EFAULT; | 3993 | return -EFAULT; |
@@ -4076,8 +4076,9 @@ done: | |||
4076 | /* Helper function that copies local addresses to user and returns the number | 4076 | /* Helper function that copies local addresses to user and returns the number |
4077 | * of addresses copied. | 4077 | * of addresses copied. |
4078 | */ | 4078 | */ |
4079 | static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs, | 4079 | static int sctp_copy_laddrs_old(struct sock *sk, __u16 port, |
4080 | void __user *to) | 4080 | int max_addrs, void *to, |
4081 | int *bytes_copied) | ||
4081 | { | 4082 | { |
4082 | struct list_head *pos, *next; | 4083 | struct list_head *pos, *next; |
4083 | struct sctp_sockaddr_entry *addr; | 4084 | struct sctp_sockaddr_entry *addr; |
@@ -4094,10 +4095,10 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add | |||
4094 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), | 4095 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), |
4095 | &temp); | 4096 | &temp); |
4096 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; | 4097 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; |
4097 | if (copy_to_user(to, &temp, addrlen)) | 4098 | memcpy(to, &temp, addrlen); |
4098 | return -EFAULT; | ||
4099 | 4099 | ||
4100 | to += addrlen; | 4100 | to += addrlen; |
4101 | *bytes_copied += addrlen; | ||
4101 | cnt ++; | 4102 | cnt ++; |
4102 | if (cnt >= max_addrs) break; | 4103 | if (cnt >= max_addrs) break; |
4103 | } | 4104 | } |
@@ -4105,8 +4106,8 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add | |||
4105 | return cnt; | 4106 | return cnt; |
4106 | } | 4107 | } |
4107 | 4108 | ||
4108 | static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, | 4109 | static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to, |
4109 | void __user **to, size_t space_left) | 4110 | size_t space_left, int *bytes_copied) |
4110 | { | 4111 | { |
4111 | struct list_head *pos, *next; | 4112 | struct list_head *pos, *next; |
4112 | struct sctp_sockaddr_entry *addr; | 4113 | struct sctp_sockaddr_entry *addr; |
@@ -4123,14 +4124,14 @@ static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, | |||
4123 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), | 4124 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), |
4124 | &temp); | 4125 | &temp); |
4125 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; | 4126 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; |
4126 | if(space_left<addrlen) | 4127 | if (space_left < addrlen) |
4127 | return -ENOMEM; | 4128 | return -ENOMEM; |
4128 | if (copy_to_user(*to, &temp, addrlen)) | 4129 | memcpy(to, &temp, addrlen); |
4129 | return -EFAULT; | ||
4130 | 4130 | ||
4131 | *to += addrlen; | 4131 | to += addrlen; |
4132 | cnt ++; | 4132 | cnt ++; |
4133 | space_left -= addrlen; | 4133 | space_left -= addrlen; |
4134 | bytes_copied += addrlen; | ||
4134 | } | 4135 | } |
4135 | 4136 | ||
4136 | return cnt; | 4137 | return cnt; |
@@ -4154,6 +4155,8 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, | |||
4154 | int addrlen; | 4155 | int addrlen; |
4155 | rwlock_t *addr_lock; | 4156 | rwlock_t *addr_lock; |
4156 | int err = 0; | 4157 | int err = 0; |
4158 | void *addrs; | ||
4159 | int bytes_copied = 0; | ||
4157 | 4160 | ||
4158 | if (len != sizeof(struct sctp_getaddrs_old)) | 4161 | if (len != sizeof(struct sctp_getaddrs_old)) |
4159 | return -EINVAL; | 4162 | return -EINVAL; |
@@ -4181,6 +4184,15 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, | |||
4181 | 4184 | ||
4182 | to = getaddrs.addrs; | 4185 | to = getaddrs.addrs; |
4183 | 4186 | ||
4187 | /* Allocate space for a local instance of packed array to hold all | ||
4188 | * the data. We store addresses here first and then put write them | ||
4189 | * to the user in one shot. | ||
4190 | */ | ||
4191 | addrs = kmalloc(sizeof(union sctp_addr) * getaddrs.addr_num, | ||
4192 | GFP_KERNEL); | ||
4193 | if (!addrs) | ||
4194 | return -ENOMEM; | ||
4195 | |||
4184 | sctp_read_lock(addr_lock); | 4196 | sctp_read_lock(addr_lock); |
4185 | 4197 | ||
4186 | /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid | 4198 | /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid |
@@ -4190,13 +4202,9 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, | |||
4190 | addr = list_entry(bp->address_list.next, | 4202 | addr = list_entry(bp->address_list.next, |
4191 | struct sctp_sockaddr_entry, list); | 4203 | struct sctp_sockaddr_entry, list); |
4192 | if (sctp_is_any(&addr->a)) { | 4204 | if (sctp_is_any(&addr->a)) { |
4193 | cnt = sctp_copy_laddrs_to_user_old(sk, bp->port, | 4205 | cnt = sctp_copy_laddrs_old(sk, bp->port, |
4194 | getaddrs.addr_num, | 4206 | getaddrs.addr_num, |
4195 | to); | 4207 | addrs, &bytes_copied); |
4196 | if (cnt < 0) { | ||
4197 | err = cnt; | ||
4198 | goto unlock; | ||
4199 | } | ||
4200 | goto copy_getaddrs; | 4208 | goto copy_getaddrs; |
4201 | } | 4209 | } |
4202 | } | 4210 | } |
@@ -4206,22 +4214,29 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, | |||
4206 | memcpy(&temp, &addr->a, sizeof(temp)); | 4214 | memcpy(&temp, &addr->a, sizeof(temp)); |
4207 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); | 4215 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); |
4208 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; | 4216 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; |
4209 | if (copy_to_user(to, &temp, addrlen)) { | 4217 | memcpy(addrs, &temp, addrlen); |
4210 | err = -EFAULT; | ||
4211 | goto unlock; | ||
4212 | } | ||
4213 | to += addrlen; | 4218 | to += addrlen; |
4219 | bytes_copied += addrlen; | ||
4214 | cnt ++; | 4220 | cnt ++; |
4215 | if (cnt >= getaddrs.addr_num) break; | 4221 | if (cnt >= getaddrs.addr_num) break; |
4216 | } | 4222 | } |
4217 | 4223 | ||
4218 | copy_getaddrs: | 4224 | copy_getaddrs: |
4225 | sctp_read_unlock(addr_lock); | ||
4226 | |||
4227 | /* copy the entire address list into the user provided space */ | ||
4228 | if (copy_to_user(to, addrs, bytes_copied)) { | ||
4229 | err = -EFAULT; | ||
4230 | goto error; | ||
4231 | } | ||
4232 | |||
4233 | /* copy the leading structure back to user */ | ||
4219 | getaddrs.addr_num = cnt; | 4234 | getaddrs.addr_num = cnt; |
4220 | if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old))) | 4235 | if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old))) |
4221 | err = -EFAULT; | 4236 | err = -EFAULT; |
4222 | 4237 | ||
4223 | unlock: | 4238 | error: |
4224 | sctp_read_unlock(addr_lock); | 4239 | kfree(addrs); |
4225 | return err; | 4240 | return err; |
4226 | } | 4241 | } |
4227 | 4242 | ||
@@ -4241,7 +4256,8 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, | |||
4241 | rwlock_t *addr_lock; | 4256 | rwlock_t *addr_lock; |
4242 | int err = 0; | 4257 | int err = 0; |
4243 | size_t space_left; | 4258 | size_t space_left; |
4244 | int bytes_copied; | 4259 | int bytes_copied = 0; |
4260 | void *addrs; | ||
4245 | 4261 | ||
4246 | if (len <= sizeof(struct sctp_getaddrs)) | 4262 | if (len <= sizeof(struct sctp_getaddrs)) |
4247 | return -EINVAL; | 4263 | return -EINVAL; |
@@ -4269,6 +4285,9 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, | |||
4269 | to = optval + offsetof(struct sctp_getaddrs,addrs); | 4285 | to = optval + offsetof(struct sctp_getaddrs,addrs); |
4270 | space_left = len - sizeof(struct sctp_getaddrs) - | 4286 | space_left = len - sizeof(struct sctp_getaddrs) - |
4271 | offsetof(struct sctp_getaddrs,addrs); | 4287 | offsetof(struct sctp_getaddrs,addrs); |
4288 | addrs = kmalloc(space_left, GFP_KERNEL); | ||
4289 | if (!addrs) | ||
4290 | return -ENOMEM; | ||
4272 | 4291 | ||
4273 | sctp_read_lock(addr_lock); | 4292 | sctp_read_lock(addr_lock); |
4274 | 4293 | ||
@@ -4279,11 +4298,11 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, | |||
4279 | addr = list_entry(bp->address_list.next, | 4298 | addr = list_entry(bp->address_list.next, |
4280 | struct sctp_sockaddr_entry, list); | 4299 | struct sctp_sockaddr_entry, list); |
4281 | if (sctp_is_any(&addr->a)) { | 4300 | if (sctp_is_any(&addr->a)) { |
4282 | cnt = sctp_copy_laddrs_to_user(sk, bp->port, | 4301 | cnt = sctp_copy_laddrs(sk, bp->port, addrs, |
4283 | &to, space_left); | 4302 | space_left, &bytes_copied); |
4284 | if (cnt < 0) { | 4303 | if (cnt < 0) { |
4285 | err = cnt; | 4304 | err = cnt; |
4286 | goto unlock; | 4305 | goto error; |
4287 | } | 4306 | } |
4288 | goto copy_getaddrs; | 4307 | goto copy_getaddrs; |
4289 | } | 4308 | } |
@@ -4294,26 +4313,31 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, | |||
4294 | memcpy(&temp, &addr->a, sizeof(temp)); | 4313 | memcpy(&temp, &addr->a, sizeof(temp)); |
4295 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); | 4314 | sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); |
4296 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; | 4315 | addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; |
4297 | if(space_left < addrlen) | 4316 | if (space_left < addrlen) { |
4298 | return -ENOMEM; /*fixme: right error?*/ | 4317 | err = -ENOMEM; /*fixme: right error?*/ |
4299 | if (copy_to_user(to, &temp, addrlen)) { | 4318 | goto error; |
4300 | err = -EFAULT; | ||
4301 | goto unlock; | ||
4302 | } | 4319 | } |
4320 | memcpy(addrs, &temp, addrlen); | ||
4303 | to += addrlen; | 4321 | to += addrlen; |
4322 | bytes_copied += addrlen; | ||
4304 | cnt ++; | 4323 | cnt ++; |
4305 | space_left -= addrlen; | 4324 | space_left -= addrlen; |
4306 | } | 4325 | } |
4307 | 4326 | ||
4308 | copy_getaddrs: | 4327 | copy_getaddrs: |
4328 | sctp_read_unlock(addr_lock); | ||
4329 | |||
4330 | if (copy_to_user(to, addrs, bytes_copied)) { | ||
4331 | err = -EFAULT; | ||
4332 | goto error; | ||
4333 | } | ||
4309 | if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) | 4334 | if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) |
4310 | return -EFAULT; | 4335 | return -EFAULT; |
4311 | bytes_copied = ((char __user *)to) - optval; | ||
4312 | if (put_user(bytes_copied, optlen)) | 4336 | if (put_user(bytes_copied, optlen)) |
4313 | return -EFAULT; | 4337 | return -EFAULT; |
4314 | 4338 | ||
4315 | unlock: | 4339 | error: |
4316 | sctp_read_unlock(addr_lock); | 4340 | kfree(addrs); |
4317 | return err; | 4341 | return err; |
4318 | } | 4342 | } |
4319 | 4343 | ||