| From 29e5342659bef7411affb2876a322da74e0ac1b8 Mon Sep 17 00:00:00 2001 |
| From: Tommi Rantala <tt.rantala@gmail.com> |
| Date: Thu, 22 Nov 2012 03:23:16 +0000 |
| Subject: sctp: fix -ENOMEM result with invalid user space pointer in sendto() syscall |
| |
| |
| From: Tommi Rantala <tt.rantala@gmail.com> |
| |
| [ Upstream commit 6e51fe7572590d8d86e93b547fab6693d305fd0d ] |
| |
| Consider the following program, that sets the second argument to the |
| sendto() syscall incorrectly: |
| |
| #include <string.h> |
| #include <arpa/inet.h> |
| #include <sys/socket.h> |
| |
| int main(void) |
| { |
| int fd; |
| struct sockaddr_in sa; |
| |
| fd = socket(AF_INET, SOCK_STREAM, 132 /*IPPROTO_SCTP*/); |
| if (fd < 0) |
| return 1; |
| |
| memset(&sa, 0, sizeof(sa)); |
| sa.sin_family = AF_INET; |
| sa.sin_addr.s_addr = inet_addr("127.0.0.1"); |
| sa.sin_port = htons(11111); |
| |
| sendto(fd, NULL, 1, 0, (struct sockaddr *)&sa, sizeof(sa)); |
| |
| return 0; |
| } |
| |
| We get -ENOMEM: |
| |
| $ strace -e sendto ./demo |
| sendto(3, NULL, 1, 0, {sa_family=AF_INET, sin_port=htons(11111), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 ENOMEM (Cannot allocate memory) |
| |
| Propagate the error code from sctp_user_addto_chunk(), so that we will |
| tell user space what actually went wrong: |
| |
| $ strace -e sendto ./demo |
| sendto(3, NULL, 1, 0, {sa_family=AF_INET, sin_port=htons(11111), sin_addr=inet_addr("127.0.0.1")}, 16) = -1 EFAULT (Bad address) |
| |
| Noticed while running Trinity (the syscall fuzzer). |
| |
| Signed-off-by: Tommi Rantala <tt.rantala@gmail.com> |
| Acked-by: Vlad Yasevich <vyasevich@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/chunk.c | 13 +++++++++---- |
| net/sctp/socket.c | 4 ++-- |
| 2 files changed, 11 insertions(+), 6 deletions(-) |
| |
| --- a/net/sctp/chunk.c |
| +++ b/net/sctp/chunk.c |
| @@ -183,7 +183,7 @@ struct sctp_datamsg *sctp_datamsg_from_u |
| |
| msg = sctp_datamsg_new(GFP_KERNEL); |
| if (!msg) |
| - return NULL; |
| + return ERR_PTR(-ENOMEM); |
| |
| /* Note: Calculate this outside of the loop, so that all fragments |
| * have the same expiration. |
| @@ -280,8 +280,11 @@ struct sctp_datamsg *sctp_datamsg_from_u |
| |
| chunk = sctp_make_datafrag_empty(asoc, sinfo, len, frag, 0); |
| |
| - if (!chunk) |
| + if (!chunk) { |
| + err = -ENOMEM; |
| goto errout; |
| + } |
| + |
| err = sctp_user_addto_chunk(chunk, offset, len, msgh->msg_iov); |
| if (err < 0) |
| goto errout_chunk_free; |
| @@ -315,8 +318,10 @@ struct sctp_datamsg *sctp_datamsg_from_u |
| |
| chunk = sctp_make_datafrag_empty(asoc, sinfo, over, frag, 0); |
| |
| - if (!chunk) |
| + if (!chunk) { |
| + err = -ENOMEM; |
| goto errout; |
| + } |
| |
| err = sctp_user_addto_chunk(chunk, offset, over,msgh->msg_iov); |
| |
| @@ -342,7 +347,7 @@ errout: |
| sctp_chunk_free(chunk); |
| } |
| sctp_datamsg_put(msg); |
| - return NULL; |
| + return ERR_PTR(err); |
| } |
| |
| /* Check whether this message has expired. */ |
| --- a/net/sctp/socket.c |
| +++ b/net/sctp/socket.c |
| @@ -1908,8 +1908,8 @@ SCTP_STATIC int sctp_sendmsg(struct kioc |
| |
| /* Break the message into multiple chunks of maximum size. */ |
| datamsg = sctp_datamsg_from_user(asoc, sinfo, msg, msg_len); |
| - if (!datamsg) { |
| - err = -ENOMEM; |
| + if (IS_ERR(datamsg)) { |
| + err = PTR_ERR(datamsg); |
| goto out_free; |
| } |
| |