4.19-stable review patch. If anyone has any objections, please let me know.
------------------
From: Xin Long lucien.xin@gmail.com
[ Upstream commit ef82bcfa671b9a635bab5fa669005663d8b177c5 ]
In sctp_setsockopt_bindx()/__sctp_setsockopt_connectx(), it allocates memory with addrs_size which is passed from userspace. We used flag GFP_USER to put some more restrictions on it in Commit cacc06215271 ("sctp: use GFP_USER for user-controlled kmalloc").
However, since Commit c981f254cc82 ("sctp: use vmemdup_user() rather than badly open-coding memdup_user()"), vmemdup_user() has been used, which doesn't check GFP_USER flag when goes to vmalloc_*(). So when addrs_size is a huge value, it could exhaust memory and even trigger oom killer.
This patch is to use memdup_user() instead, in which GFP_USER would work to limit the memory allocation with a huge addrs_size.
Note we can't fix it by limiting 'addrs_size', as there's no demand for it from RFC.
Reported-by: syzbot+ec1b7575afef85a0e5ca@syzkaller.appspotmail.com Fixes: c981f254cc82 ("sctp: use vmemdup_user() rather than badly open-coding memdup_user()") Signed-off-by: Xin Long lucien.xin@gmail.com Acked-by: Neil Horman nhorman@tuxdriver.com Signed-off-by: David S. Miller davem@davemloft.net Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org --- net/sctp/socket.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)
--- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1017,7 +1017,7 @@ static int sctp_setsockopt_bindx(struct if (unlikely(addrs_size <= 0)) return -EINVAL;
- kaddrs = vmemdup_user(addrs, addrs_size); + kaddrs = memdup_user(addrs, addrs_size); if (unlikely(IS_ERR(kaddrs))) return PTR_ERR(kaddrs);
@@ -1025,7 +1025,7 @@ static int sctp_setsockopt_bindx(struct addr_buf = kaddrs; while (walk_size < addrs_size) { if (walk_size + sizeof(sa_family_t) > addrs_size) { - kvfree(kaddrs); + kfree(kaddrs); return -EINVAL; }
@@ -1036,7 +1036,7 @@ static int sctp_setsockopt_bindx(struct * causes the address buffer to overflow return EINVAL. */ if (!af || (walk_size + af->sockaddr_len) > addrs_size) { - kvfree(kaddrs); + kfree(kaddrs); return -EINVAL; } addrcnt++; @@ -1072,7 +1072,7 @@ static int sctp_setsockopt_bindx(struct }
out: - kvfree(kaddrs); + kfree(kaddrs);
return err; } @@ -1347,7 +1347,7 @@ static int __sctp_setsockopt_connectx(st if (unlikely(addrs_size <= 0)) return -EINVAL;
- kaddrs = vmemdup_user(addrs, addrs_size); + kaddrs = memdup_user(addrs, addrs_size); if (unlikely(IS_ERR(kaddrs))) return PTR_ERR(kaddrs);
@@ -1367,7 +1367,7 @@ static int __sctp_setsockopt_connectx(st err = __sctp_connect(sk, kaddrs, addrs_size, flags, assoc_id);
out_free: - kvfree(kaddrs); + kfree(kaddrs);
return err; }