The patch titled SCTP: fix sctp_getsockopt_local_addrs_old() to use local storage has been added to the -mm tree. Its filename is sctp-fix-sctp_getsockopt_local_addrs_old-to-use-local-storage.patch *** Remember to use Documentation/SubmitChecklist when testing your code *** See http://www.zip.com.au/~akpm/linux/patches/stuff/added-to-mm.txt to find out what to do about this ------------------------------------------------------ Subject: SCTP: fix sctp_getsockopt_local_addrs_old() to use local storage From: Vlad Yasevich <vladislav.yasevich@xxxxxx> 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@xxxxxx> Cc: "David S. Miller" <davem@xxxxxxxxxxxxx> Signed-off-by: Andrew Morton <akpm@xxxxxxxxxxxxxxxxxxxx> --- net/sctp/socket.c | 96 +++++++++++++++++++++++++++----------------- 1 files changed, 60 insertions(+), 36 deletions(-) diff -puN net/sctp/socket.c~sctp-fix-sctp_getsockopt_local_addrs_old-to-use-local-storage net/sctp/socket.c --- a/net/sctp/socket.c~sctp-fix-sctp_getsockopt_local_addrs_old-to-use-local-storage +++ a/net/sctp/socket.c @@ -3847,7 +3847,7 @@ static int sctp_getsockopt_peer_addrs(st memcpy(&temp, &from->ipaddr, sizeof(temp)); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); addrlen = sctp_get_af_specific(sk->sk_family)->sockaddr_len; - if(space_left < addrlen) + if (space_left < addrlen) return -ENOMEM; if (copy_to_user(to, &temp, addrlen)) return -EFAULT; @@ -3936,8 +3936,9 @@ done: /* Helper function that copies local addresses to user and returns the number * of addresses copied. */ -static int sctp_copy_laddrs_to_user_old(struct sock *sk, __u16 port, int max_addrs, - void __user *to) +static int sctp_copy_laddrs_old(struct sock *sk, __u16 port, + int max_addrs, void *to, + size_t *bytes_copied) { struct list_head *pos, *next; struct sctp_sockaddr_entry *addr; @@ -3954,10 +3955,10 @@ static int sctp_copy_laddrs_to_user_old( sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), &temp); addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; - if (copy_to_user(to, &temp, addrlen)) - return -EFAULT; + memcpy(to, &temp, addrlen); to += addrlen; + *bytes_copied += addrlen; cnt ++; if (cnt >= max_addrs) break; } @@ -3965,8 +3966,8 @@ static int sctp_copy_laddrs_to_user_old( return cnt; } -static int sctp_copy_laddrs_to_user(struct sock *sk, __u16 port, - void __user **to, size_t space_left) +static int sctp_copy_laddrs(struct sock *sk, __u16 port, void *to, + size_t space_left, size_t *bytes_copied) { struct list_head *pos, *next; struct sctp_sockaddr_entry *addr; @@ -3983,14 +3984,14 @@ static int sctp_copy_laddrs_to_user(stru sctp_get_pf_specific(sk->sk_family)->addr_v4map(sctp_sk(sk), &temp); addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; - if(space_left<addrlen) + if (space_left < addrlen) return -ENOMEM; - if (copy_to_user(*to, &temp, addrlen)) - return -EFAULT; + memcpy(to, &temp, addrlen); - *to += addrlen; + to += addrlen; cnt ++; space_left -= addrlen; + bytes_copied += addrlen; } return cnt; @@ -4014,6 +4015,8 @@ static int sctp_getsockopt_local_addrs_o int addrlen; rwlock_t *addr_lock; int err = 0; + void *addrs; + size_t bytes_copied = 0; if (len != sizeof(struct sctp_getaddrs_old)) return -EINVAL; @@ -4041,6 +4044,15 @@ static int sctp_getsockopt_local_addrs_o 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); /* If the endpoint is bound to 0.0.0.0 or ::0, get the valid @@ -4050,13 +4062,9 @@ static int sctp_getsockopt_local_addrs_o addr = list_entry(bp->address_list.next, struct sctp_sockaddr_entry, list); if (sctp_is_any(&addr->a)) { - cnt = sctp_copy_laddrs_to_user_old(sk, bp->port, - getaddrs.addr_num, - to); - if (cnt < 0) { - err = cnt; - goto unlock; - } + cnt = sctp_copy_laddrs_old(sk, bp->port, + getaddrs.addr_num, + addrs, &bytes_copied); goto copy_getaddrs; } } @@ -4066,22 +4074,29 @@ static int sctp_getsockopt_local_addrs_o memcpy(&temp, &addr->a, sizeof(temp)); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; - if (copy_to_user(to, &temp, addrlen)) { - err = -EFAULT; - goto unlock; - } + memcpy(addrs, &temp, addrlen); to += addrlen; + bytes_copied += addrlen; cnt ++; if (cnt >= getaddrs.addr_num) break; } 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; if (copy_to_user(optval, &getaddrs, sizeof(struct sctp_getaddrs_old))) err = -EFAULT; -unlock: - sctp_read_unlock(addr_lock); +error: + kfree(addrs); return err; } @@ -4101,7 +4116,8 @@ static int sctp_getsockopt_local_addrs(s rwlock_t *addr_lock; int err = 0; size_t space_left; - int bytes_copied; + int bytes_copied = 0; + void *addrs; if (len <= sizeof(struct sctp_getaddrs)) return -EINVAL; @@ -4129,6 +4145,9 @@ static int sctp_getsockopt_local_addrs(s to = optval + offsetof(struct sctp_getaddrs,addrs); space_left = len - sizeof(struct sctp_getaddrs) - offsetof(struct sctp_getaddrs,addrs); + addrs = kmalloc(space_left, GFP_KERNEL); + if (!addrs) + return -ENOMEM; sctp_read_lock(addr_lock); @@ -4139,11 +4158,11 @@ static int sctp_getsockopt_local_addrs(s addr = list_entry(bp->address_list.next, struct sctp_sockaddr_entry, list); if (sctp_is_any(&addr->a)) { - cnt = sctp_copy_laddrs_to_user(sk, bp->port, - &to, space_left); + cnt = sctp_copy_laddrs(sk, bp->port, addrs, + space_left, &bytes_copied); if (cnt < 0) { err = cnt; - goto unlock; + goto error; } goto copy_getaddrs; } @@ -4154,26 +4173,31 @@ static int sctp_getsockopt_local_addrs(s memcpy(&temp, &addr->a, sizeof(temp)); sctp_get_pf_specific(sk->sk_family)->addr_v4map(sp, &temp); addrlen = sctp_get_af_specific(temp.sa.sa_family)->sockaddr_len; - if(space_left < addrlen) - return -ENOMEM; /*fixme: right error?*/ - if (copy_to_user(to, &temp, addrlen)) { - err = -EFAULT; - goto unlock; + if (space_left < addrlen) { + err = -ENOMEM; /*fixme: right error?*/ + goto error; } + memcpy(addrs, &temp, addrlen); to += addrlen; + bytes_copied += addrlen; cnt ++; space_left -= addrlen; } 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)) return -EFAULT; - bytes_copied = ((char __user *)to) - optval; if (put_user(bytes_copied, optlen)) return -EFAULT; -unlock: - sctp_read_unlock(addr_lock); +error: + kfree(addrs); return err; } _ Patches currently in -mm which might be from vladislav.yasevich@xxxxxx are origin.patch sctp-fix-sctp_getsockopt_local_addrs_old-to-use-local-storage.patch sctp-fix-sctp_getsockopt_local_addrs_old-to-use-local-storage-fix.patch git-net.patch use-menuconfig-objects-sctp.patch - To unsubscribe from this list: send the line "unsubscribe mm-commits" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html