Commit aad97f38 authored by Vlad Yasevich's avatar Vlad Yasevich Committed by David S. Miller

[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: default avatarVlad Yasevich <vladislav.yasevich@hp.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5a1b5898
...@@ -3987,7 +3987,7 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len, ...@@ -3987,7 +3987,7 @@ static int sctp_getsockopt_peer_addrs(struct sock *sk, int len,
memcpy(&temp, &from->ipaddr, sizeof(temp)); memcpy(&temp, &from->ipaddr, sizeof(temp));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len; addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len;
if(space_left < addrlen) if (space_left < addrlen)
return -ENOMEM; return -ENOMEM;
if (copy_to_user(to, &temp, addrlen)) if (copy_to_user(to, &temp, addrlen))
return -EFAULT; return -EFAULT;
...@@ -4076,8 +4076,9 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len, ...@@ -4076,8 +4076,9 @@ static int sctp_getsockopt_local_addrs_num_old(struct sock *sk, int len,
/* Helper function that copies local addresses to user and returns the number /* Helper function that copies local addresses to user and returns the number
* of addresses copied. * of addresses copied.
*/ */
static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs, static int sctp_copy_laddrs_old(struct sock *sk, __u16 port,
void __user *to) int max_addrs, void *to,
int *bytes_copied)
{ {
struct list_head *pos, *next; struct list_head *pos, *next;
struct sctp_sockaddr_entry *addr; 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,10 +4095,10 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
&temp); &temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
if (copy_to_user(to, &temp, addrlen)) memcpy(to, &temp, addrlen);
return -EFAULT;
to += addrlen; to += addrlen;
*bytes_copied += addrlen;
cnt ++; cnt ++;
if (cnt >= max_addrs) break; if (cnt >= max_addrs) break;
} }
...@@ -4105,8 +4106,8 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add ...@@ -4105,8 +4106,8 @@ static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_add
return cnt; return cnt;
} }
static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to,
void __user **to, size_t space_left) size_t space_left, int *bytes_copied)
{ {
struct list_head *pos, *next; struct list_head *pos, *next;
struct sctp_sockaddr_entry *addr; struct sctp_sockaddr_entry *addr;
...@@ -4123,14 +4124,14 @@ static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, ...@@ -4123,14 +4124,14 @@ static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port,
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk),
&temp); &temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
if(space_left<addrlen) if (space_left < addrlen)
return -ENOMEM; return -ENOMEM;
if (copy_to_user(*to, &temp, addrlen)) memcpy(to, &temp, addrlen);
return -EFAULT;
*to += addrlen; to += addrlen;
cnt ++; cnt ++;
space_left -= addrlen; space_left -= addrlen;
bytes_copied += addrlen;
} }
return cnt; return cnt;
...@@ -4154,6 +4155,8 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, ...@@ -4154,6 +4155,8 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
int addrlen; int addrlen;
rwlock_t *addr_lock; rwlock_t *addr_lock;
int err = 0; int err = 0;
void *addrs;
int bytes_copied = 0;
if (len != sizeof(struct sctp_getaddrs_old)) if (len != sizeof(struct sctp_getaddrs_old))
return -EINVAL; return -EINVAL;
...@@ -4181,6 +4184,15 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, ...@@ -4181,6 +4184,15 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
to = getaddrs.addrs; to = getaddrs.addrs;
/* Allocate space for a local instance of packed array to hold all
* the data. We store addresses here first and then put write them
* to the user in one shot.
*/
addrs = kmalloc(sizeof(union sctp_addr) * getaddrs.addr_num,
GFP_KERNEL);
if (!addrs)
return -ENOMEM;
sctp_read_lock(addr_lock); sctp_read_lock(addr_lock);
/* If the endpoint is bound to 0.0.0.0 or ::0, get the valid /* 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,13 +4202,9 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
addr = list_entry(bp->address_list.next, addr = list_entry(bp->address_list.next,
struct sctp_sockaddr_entry, list); struct sctp_sockaddr_entry, list);
if (sctp_is_any(&addr->a)) { if (sctp_is_any(&addr->a)) {
cnt = sctp_copy_laddrs_to_user_old(sk, bp->port, cnt = sctp_copy_laddrs_old(sk, bp->port,
getaddrs.addr_num, getaddrs.addr_num,
to); addrs, &bytes_copied);
if (cnt < 0) {
err = cnt;
goto unlock;
}
goto copy_getaddrs; goto copy_getaddrs;
} }
} }
...@@ -4206,22 +4214,29 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len, ...@@ -4206,22 +4214,29 @@ static int sctp_getsockopt_local_addrs_old(struct sock *sk, int len,
memcpy(&temp, &addr->a, sizeof(temp)); memcpy(&temp, &addr->a, sizeof(temp));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
if (copy_to_user(to, &temp, addrlen)) { memcpy(addrs, &temp, addrlen);
err = -EFAULT;
goto unlock;
}
to += addrlen; to += addrlen;
bytes_copied += addrlen;
cnt ++; cnt ++;
if (cnt >= getaddrs.addr_num) break; if (cnt >= getaddrs.addr_num) break;
} }
copy_getaddrs: copy_getaddrs:
sctp_read_unlock(addr_lock);
/* copy the entire address list into the user provided space */
if (copy_to_user(to, addrs, bytes_copied)) {
err = -EFAULT;
goto error;
}
/* copy the leading structure back to user */
getaddrs.addr_num = cnt; getaddrs.addr_num = cnt;
if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old))) if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old)))
err = -EFAULT; err = -EFAULT;
unlock: error:
sctp_read_unlock(addr_lock); kfree(addrs);
return err; return err;
} }
...@@ -4241,7 +4256,8 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, ...@@ -4241,7 +4256,8 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
rwlock_t *addr_lock; rwlock_t *addr_lock;
int err = 0; int err = 0;
size_t space_left; size_t space_left;
int bytes_copied; int bytes_copied = 0;
void *addrs;
if (len <= sizeof(struct sctp_getaddrs)) if (len <= sizeof(struct sctp_getaddrs))
return -EINVAL; return -EINVAL;
...@@ -4269,6 +4285,9 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, ...@@ -4269,6 +4285,9 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
to = optval + offsetof(struct sctp_getaddrs,addrs); to = optval + offsetof(struct sctp_getaddrs,addrs);
space_left = len - sizeof(struct sctp_getaddrs) - space_left = len - sizeof(struct sctp_getaddrs) -
offsetof(struct sctp_getaddrs,addrs); offsetof(struct sctp_getaddrs,addrs);
addrs = kmalloc(space_left, GFP_KERNEL);
if (!addrs)
return -ENOMEM;
sctp_read_lock(addr_lock); sctp_read_lock(addr_lock);
...@@ -4279,11 +4298,11 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, ...@@ -4279,11 +4298,11 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
addr = list_entry(bp->address_list.next, addr = list_entry(bp->address_list.next,
struct sctp_sockaddr_entry, list); struct sctp_sockaddr_entry, list);
if (sctp_is_any(&addr->a)) { if (sctp_is_any(&addr->a)) {
cnt = sctp_copy_laddrs_to_user(sk, bp->port, cnt = sctp_copy_laddrs(sk, bp->port, addrs,
&to, space_left); space_left, &bytes_copied);
if (cnt < 0) { if (cnt < 0) {
err = cnt; err = cnt;
goto unlock; goto error;
} }
goto copy_getaddrs; goto copy_getaddrs;
} }
...@@ -4294,26 +4313,31 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len, ...@@ -4294,26 +4313,31 @@ static int sctp_getsockopt_local_addrs(struct sock *sk, int len,
memcpy(&temp, &addr->a, sizeof(temp)); memcpy(&temp, &addr->a, sizeof(temp));
sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp);
addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len;
if(space_left < addrlen) if (space_left < addrlen) {
return -ENOMEM; /*fixme: right error?*/ err = -ENOMEM; /*fixme: right error?*/
if (copy_to_user(to, &temp, addrlen)) { goto error;
err = -EFAULT;
goto unlock;
} }
memcpy(addrs, &temp, addrlen);
to += addrlen; to += addrlen;
bytes_copied += addrlen;
cnt ++; cnt ++;
space_left -= addrlen; space_left -= addrlen;
} }
copy_getaddrs: copy_getaddrs:
sctp_read_unlock(addr_lock);
if (copy_to_user(to, addrs, bytes_copied)) {
err = -EFAULT;
goto error;
}
if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num)) if (put_user(cnt, &((struct sctp_getaddrs __user *)optval)->addr_num))
return -EFAULT; return -EFAULT;
bytes_copied = ((char __user *)to) - optval;
if (put_user(bytes_copied, optlen)) if (put_user(bytes_copied, optlen))
return -EFAULT; return -EFAULT;
unlock: error:
sctp_read_unlock(addr_lock); kfree(addrs);
return err; return err;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment