Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
On Mon, Jan 25, 2016 at 02:16:00PM -0200, Marcelo Ricardo Leitner wrote: > Something like this. Builds, but UNTESTED. > Uses union sizeof where possible but when reading from a buffer that is > not aligned to it, like that user supplied one. Then relies on > af->sockaddr_len > > --8<-- > > --- > include/net/sctp/structs.h | 2 +- > net/sctp/bind_addr.c | 14 -- > net/sctp/protocol.c| 1 + > net/sctp/sm_make_chunk.c | 2 +- > net/sctp/socket.c | 5 +++-- > 5 files changed, 14 insertions(+), 10 deletions(-) > > diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h > index > 20e72129be1ce0063eeafcbaadcee1f37e0c614c..97ba8a8c466f5c50bdc87ec578792e56553baa91 > 100644 > --- a/include/net/sctp/structs.h > +++ b/include/net/sctp/structs.h > @@ -1099,7 +1099,7 @@ int sctp_bind_addr_dup(struct sctp_bind_addr *dest, > const struct sctp_bind_addr *src, > gfp_t gfp); > int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *, > -__u8 addr_state, gfp_t gfp); > +int new_size, __u8 addr_state, gfp_t gfp); > int sctp_del_bind_addr(struct sctp_bind_addr *, union sctp_addr *); > int sctp_bind_addr_match(struct sctp_bind_addr *, const union sctp_addr *, >struct sctp_sock *); > diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c > index > 871cdf9567e6bc9c13cb1077dc6866a67e6e4367..80129d10a0af9c33e7348b79d010b9e5e948e584 > 100644 > --- a/net/sctp/bind_addr.c > +++ b/net/sctp/bind_addr.c > @@ -111,7 +111,8 @@ int sctp_bind_addr_dup(struct sctp_bind_addr *dest, > dest->port = src->port; > > list_for_each_entry(addr, >address_list, list) { > - error = sctp_add_bind_addr(dest, >a, 1, gfp); > + error = sctp_add_bind_addr(dest, >a, sizeof(addr->a), > +1, gfp); > if (error < 0) > break; > } > @@ -150,7 +151,7 @@ void sctp_bind_addr_free(struct sctp_bind_addr *bp) > > /* Add an address to the bind address list in the SCTP_bind_addr structure. > */ > int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, > -__u8 addr_state, gfp_t gfp) > +int new_size, __u8 addr_state, gfp_t gfp) > { > struct sctp_sockaddr_entry *addr; > > @@ -159,7 +160,7 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union > sctp_addr *new, > if (!addr) > return -ENOMEM; > > - memcpy(>a, new, sizeof(*new)); > + memcpy(>a, new, min_t(size_t, sizeof(*new), new_size)); > > /* Fix up the port if it has not yet been set. >* Both v4 and v6 have the port at the same offset. > @@ -291,7 +292,8 @@ int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, > __u8 *raw_addr_list, > } > > af->from_addr_param(, rawaddr, htons(port), 0); > - retval = sctp_add_bind_addr(bp, , SCTP_ADDR_SRC, gfp); > + retval = sctp_add_bind_addr(bp, , sizeof(addr), > + SCTP_ADDR_SRC, gfp); > if (retval) { > /* Can't finish building the list, clean up. */ > sctp_bind_addr_clean(bp); > @@ -453,8 +455,8 @@ static int sctp_copy_one_addr(struct net *net, struct > sctp_bind_addr *dest, > (((AF_INET6 == addr->sa.sa_family) && > (flags & SCTP_ADDR6_ALLOWED) && > (flags & SCTP_ADDR6_PEERSUPP > - error = sctp_add_bind_addr(dest, addr, SCTP_ADDR_SRC, > - gfp); > + error = sctp_add_bind_addr(dest, addr, sizeof(addr), > +SCTP_ADDR_SRC, gfp); > } > > return error; > diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c > index > ab0d538a74ed593571cfaef02cd1bb7ce872abe6..2fb609008311f51344704d82f21b4de9f08253da > 100644 > --- a/net/sctp/protocol.c > +++ b/net/sctp/protocol.c > @@ -214,6 +214,7 @@ int sctp_copy_local_addr_list(struct net *net, struct > sctp_bind_addr *bp, > (copy_flags & SCTP_ADDR6_ALLOWED) && > (copy_flags & SCTP_ADDR6_PEERSUPP { > error = sctp_add_bind_addr(bp, >a, > + sizeof(addr->a), > SCTP_ADDR_SRC, GFP_ATOMIC); > if (error) > goto end_copy; > diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c > index > 5d6a03fad3789a12290f5f14c5a7efa69c98f41a..1b91e9760fe514db6d89457e1d5da9e02800745e > 100644 > --- a/net/sctp/sm_make_chunk.c > +++ b/net/sctp/sm_make_chunk.c > @@ -1830,7 +1830,7 @@ no_hmac: > /* Also, add the destination address. */ >
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
Something like this. Builds, but UNTESTED. Uses union sizeof where possible but when reading from a buffer that is not aligned to it, like that user supplied one. Then relies on af->sockaddr_len --8<-- --- include/net/sctp/structs.h | 2 +- net/sctp/bind_addr.c | 14 -- net/sctp/protocol.c| 1 + net/sctp/sm_make_chunk.c | 2 +- net/sctp/socket.c | 5 +++-- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 20e72129be1ce0063eeafcbaadcee1f37e0c614c..97ba8a8c466f5c50bdc87ec578792e56553baa91 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1099,7 +1099,7 @@ int sctp_bind_addr_dup(struct sctp_bind_addr *dest, const struct sctp_bind_addr *src, gfp_t gfp); int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *, - __u8 addr_state, gfp_t gfp); + int new_size, __u8 addr_state, gfp_t gfp); int sctp_del_bind_addr(struct sctp_bind_addr *, union sctp_addr *); int sctp_bind_addr_match(struct sctp_bind_addr *, const union sctp_addr *, struct sctp_sock *); diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 871cdf9567e6bc9c13cb1077dc6866a67e6e4367..80129d10a0af9c33e7348b79d010b9e5e948e584 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -111,7 +111,8 @@ int sctp_bind_addr_dup(struct sctp_bind_addr *dest, dest->port = src->port; list_for_each_entry(addr, >address_list, list) { - error = sctp_add_bind_addr(dest, >a, 1, gfp); + error = sctp_add_bind_addr(dest, >a, sizeof(addr->a), + 1, gfp); if (error < 0) break; } @@ -150,7 +151,7 @@ void sctp_bind_addr_free(struct sctp_bind_addr *bp) /* Add an address to the bind address list in the SCTP_bind_addr structure. */ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, - __u8 addr_state, gfp_t gfp) + int new_size, __u8 addr_state, gfp_t gfp) { struct sctp_sockaddr_entry *addr; @@ -159,7 +160,7 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, if (!addr) return -ENOMEM; - memcpy(>a, new, sizeof(*new)); + memcpy(>a, new, min_t(size_t, sizeof(*new), new_size)); /* Fix up the port if it has not yet been set. * Both v4 and v6 have the port at the same offset. @@ -291,7 +292,8 @@ int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw_addr_list, } af->from_addr_param(, rawaddr, htons(port), 0); - retval = sctp_add_bind_addr(bp, , SCTP_ADDR_SRC, gfp); + retval = sctp_add_bind_addr(bp, , sizeof(addr), + SCTP_ADDR_SRC, gfp); if (retval) { /* Can't finish building the list, clean up. */ sctp_bind_addr_clean(bp); @@ -453,8 +455,8 @@ static int sctp_copy_one_addr(struct net *net, struct sctp_bind_addr *dest, (((AF_INET6 == addr->sa.sa_family) && (flags & SCTP_ADDR6_ALLOWED) && (flags & SCTP_ADDR6_PEERSUPP - error = sctp_add_bind_addr(dest, addr, SCTP_ADDR_SRC, - gfp); + error = sctp_add_bind_addr(dest, addr, sizeof(addr), + SCTP_ADDR_SRC, gfp); } return error; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index ab0d538a74ed593571cfaef02cd1bb7ce872abe6..2fb609008311f51344704d82f21b4de9f08253da 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -214,6 +214,7 @@ int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *bp, (copy_flags & SCTP_ADDR6_ALLOWED) && (copy_flags & SCTP_ADDR6_PEERSUPP { error = sctp_add_bind_addr(bp, >a, + sizeof(addr->a), SCTP_ADDR_SRC, GFP_ATOMIC); if (error) goto end_copy; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 5d6a03fad3789a12290f5f14c5a7efa69c98f41a..1b91e9760fe514db6d89457e1d5da9e02800745e 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1830,7 +1830,7 @@ no_hmac: /* Also, add the destination address. */ if (list_empty(>base.bind_addr.address_list)) { sctp_add_bind_addr(>base.bind_addr, >dest, - SCTP_ADDR_SRC, GFP_ATOMIC); +
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
On Mon, Jan 25, 2016 at 12:48:02PM -0200, Marcelo Ricardo Leitner wrote: > On Mon, Jan 25, 2016 at 03:42:14PM +0100, Dmitry Vyukov wrote: > > On Mon, Jan 25, 2016 at 3:31 PM, Neil Horman wrote: > > > On Mon, Jan 25, 2016 at 03:02:38PM +0100, Dmitry Vyukov wrote: > > >> Hello, > > >> > > >> I've git the following error report while running syzkaller fuzzer: > > >> > > >> == > > >> BUG: KASAN: slab-out-of-bounds in memcpy+0x1d/0x40 at addr > > >> 88006c6361e8 > > >> Read of size 28 by task syz-executor/12551 > > >> = > > >> BUG kmalloc-16 (Not tainted): kasan: bad access detected > > >> - > > >> > > >> INFO: Allocated in sctp_setsockopt_bindx+0xd2/0x3e0 age=12 cpu=2 > > >> pid=12551 > > >> [< inline >] kmalloc include/linux/slab.h:468 > > >> [< none >] sctp_setsockopt_bindx+0xd2/0x3e0 > > >> net/sctp/socket.c:975 > > >> [< none >] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 > > >> [< none >] sock_common_setsockopt+0x97/0xd0 > > >> net/core/sock.c:2620 > > >> [< inline >] SYSC_setsockopt net/socket.c:1752 > > >> [< none >] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > > >> [< none >] entry_SYSCALL_64_fastpath+0x16/0x7a > > >> arch/x86/entry/entry_64.S:185 > > >> > > >> INFO: Slab 0xea0001b18d80 objects=16 used=4 fp=0x88006c6376e0 > > >> flags=0x5fffc004080 > > >> INFO: Object 0x88006c6361e8 @offset=488 fp=0x0002 > > >> Bytes b4 88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff > > >> ff ff /.4. > > >> Object 88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00 > > >> 01 > > >> CPU: 2 PID: 12551 Comm: syz-executor Tainted: GB > > >> 4.5.0-rc1+ #278 > > >> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs > > >> 01/01/2011 > > >> 880036397928 8299a02d 88003e807900 > > >> 88006c6361e8 88006c636000 880036397958 81752814 > > >> 88003e807900 ea0001b18d80 88006c6361e8 88006c6361e8 > > >> > > >> Call Trace: > > >> [] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512 > > >> [] memcpy+0x1d/0x40 mm/kasan/kasan.c:297 > > >> [] sctp_add_bind_addr+0xa9/0x270 > > >> net/sctp/bind_addr.c:162 > > >> [] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389 > > >> [] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471 > > >> [] sctp_setsockopt_bindx+0x2f8/0x3e0 > > >> net/sctp/socket.c:1010 > > >> [] sctp_setsockopt+0x1493/0x3630 > > >> net/sctp/socket.c:3711 > > >> [] sock_common_setsockopt+0x97/0xd0 > > >> net/core/sock.c:2620 > > >> [< inline >] SYSC_setsockopt net/socket.c:1752 > > >> [] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > > >> [] entry_SYSCALL_64_fastpath+0x16/0x7a > > >> arch/x86/entry/entry_64.S:185 > > >> > > >> Memory state around the buggy address: > > >> 88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > > >> 88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > > >> >88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc > > >> ^ > > >> 88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > > >> 88006c636280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > > >> == > > >> > > >> > > >> sctp_setsockopt_bindx verifies that the user-passed address has valid > > >> len for the specified family, but then sctp_add_bind_addr copies whole > > >> sctp_addr from there. This causes heap out-of-bounds access and can > > >> crash kernel. Not sure if it is possible to copy out the trailing > > >> garbage to user-space later. > > >> > > > > > > It does more than that though. sctp_setsockopt_bindx checks the > > > following: > > > 1) That passed addr_size is greater than zero > > > 2) that the entire range of memory between addrs and addrs+addr_size is > > > readable > > > 3) That at least one address structure worth of data is available > > > (implicit in > > > the while (walk_size < addr_size) loop). > > > > > > Could one of the sockaddr_len fields in one of the addresses have been > > > mangled > > > so that it appeared shorter in the the while loop from (3), so that a > > > copy of > > > sizeof(sctp_addr in sctp_add_bind_addr overrun the allocated memory? > > > > I may be missing something, but what I see is: > > > > 1. we check that there is at least family: > > if (walk_size + sizeof(sa_family_t) > addrs_size) { > > > > 2. get family descriptor: > > af = sctp_get_af_specific(sa_addr->sa_family); > > > > 3. check that the address size is enough to hold the declared family: > > if (!af || (walk_size + af->sockaddr_len) > addrs_size) {
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
On Mon, Jan 25, 2016 at 03:42:14PM +0100, Dmitry Vyukov wrote: > On Mon, Jan 25, 2016 at 3:31 PM, Neil Horman wrote: > > On Mon, Jan 25, 2016 at 03:02:38PM +0100, Dmitry Vyukov wrote: > >> Hello, > >> > >> I've git the following error report while running syzkaller fuzzer: > >> > >> == > >> BUG: KASAN: slab-out-of-bounds in memcpy+0x1d/0x40 at addr 88006c6361e8 > >> Read of size 28 by task syz-executor/12551 > >> = > >> BUG kmalloc-16 (Not tainted): kasan: bad access detected > >> - > >> > >> INFO: Allocated in sctp_setsockopt_bindx+0xd2/0x3e0 age=12 cpu=2 pid=12551 > >> [< inline >] kmalloc include/linux/slab.h:468 > >> [< none >] sctp_setsockopt_bindx+0xd2/0x3e0 net/sctp/socket.c:975 > >> [< none >] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 > >> [< none >] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 > >> [< inline >] SYSC_setsockopt net/socket.c:1752 > >> [< none >] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > >> [< none >] entry_SYSCALL_64_fastpath+0x16/0x7a > >> arch/x86/entry/entry_64.S:185 > >> > >> INFO: Slab 0xea0001b18d80 objects=16 used=4 fp=0x88006c6376e0 > >> flags=0x5fffc004080 > >> INFO: Object 0x88006c6361e8 @offset=488 fp=0x0002 > >> Bytes b4 88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff > >> ff ff /.4. > >> Object 88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00 > >> 01 > >> CPU: 2 PID: 12551 Comm: syz-executor Tainted: GB 4.5.0-rc1+ > >> #278 > >> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs > >> 01/01/2011 > >> 880036397928 8299a02d 88003e807900 > >> 88006c6361e8 88006c636000 880036397958 81752814 > >> 88003e807900 ea0001b18d80 88006c6361e8 88006c6361e8 > >> > >> Call Trace: > >> [] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512 > >> [] memcpy+0x1d/0x40 mm/kasan/kasan.c:297 > >> [] sctp_add_bind_addr+0xa9/0x270 > >> net/sctp/bind_addr.c:162 > >> [] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389 > >> [] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471 > >> [] sctp_setsockopt_bindx+0x2f8/0x3e0 > >> net/sctp/socket.c:1010 > >> [] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 > >> [] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 > >> [< inline >] SYSC_setsockopt net/socket.c:1752 > >> [] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > >> [] entry_SYSCALL_64_fastpath+0x16/0x7a > >> arch/x86/entry/entry_64.S:185 > >> > >> Memory state around the buggy address: > >> 88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >> 88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >> >88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc > >> ^ > >> 88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >> 88006c636280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >> == > >> > >> > >> sctp_setsockopt_bindx verifies that the user-passed address has valid > >> len for the specified family, but then sctp_add_bind_addr copies whole > >> sctp_addr from there. This causes heap out-of-bounds access and can > >> crash kernel. Not sure if it is possible to copy out the trailing > >> garbage to user-space later. > >> > > > > It does more than that though. sctp_setsockopt_bindx checks the following: > > 1) That passed addr_size is greater than zero > > 2) that the entire range of memory between addrs and addrs+addr_size is > > readable > > 3) That at least one address structure worth of data is available (implicit > > in > > the while (walk_size < addr_size) loop). > > > > Could one of the sockaddr_len fields in one of the addresses have been > > mangled > > so that it appeared shorter in the the while loop from (3), so that a copy > > of > > sizeof(sctp_addr in sctp_add_bind_addr overrun the allocated memory? > > I may be missing something, but what I see is: > > 1. we check that there is at least family: > if (walk_size + sizeof(sa_family_t) > addrs_size) { > > 2. get family descriptor: > af = sctp_get_af_specific(sa_addr->sa_family); > > 3. check that the address size is enough to hold the declared family: > if (!af || (walk_size + af->sockaddr_len) > addrs_size) { > > 4. then we do sctp_add_bind_addr, which copies whole sctp_addr from addr: > > int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, > ... > memcpy(>a, new, sizeof(*new)); > > Now imagine that the addr is ipv4 (16 or so bytes, that's what we > checked) and we copy 28 bytes (ipv6) from addr.
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
On Mon, Jan 25, 2016 at 3:31 PM, Neil Horman wrote: > On Mon, Jan 25, 2016 at 03:02:38PM +0100, Dmitry Vyukov wrote: >> Hello, >> >> I've git the following error report while running syzkaller fuzzer: >> >> == >> BUG: KASAN: slab-out-of-bounds in memcpy+0x1d/0x40 at addr 88006c6361e8 >> Read of size 28 by task syz-executor/12551 >> = >> BUG kmalloc-16 (Not tainted): kasan: bad access detected >> - >> >> INFO: Allocated in sctp_setsockopt_bindx+0xd2/0x3e0 age=12 cpu=2 pid=12551 >> [< inline >] kmalloc include/linux/slab.h:468 >> [< none >] sctp_setsockopt_bindx+0xd2/0x3e0 net/sctp/socket.c:975 >> [< none >] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 >> [< none >] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 >> [< inline >] SYSC_setsockopt net/socket.c:1752 >> [< none >] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 >> [< none >] entry_SYSCALL_64_fastpath+0x16/0x7a >> arch/x86/entry/entry_64.S:185 >> >> INFO: Slab 0xea0001b18d80 objects=16 used=4 fp=0x88006c6376e0 >> flags=0x5fffc004080 >> INFO: Object 0x88006c6361e8 @offset=488 fp=0x0002 >> Bytes b4 88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff >> ff ff /.4. >> Object 88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00 >> 01 >> CPU: 2 PID: 12551 Comm: syz-executor Tainted: GB 4.5.0-rc1+ >> #278 >> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 >> 880036397928 8299a02d 88003e807900 >> 88006c6361e8 88006c636000 880036397958 81752814 >> 88003e807900 ea0001b18d80 88006c6361e8 88006c6361e8 >> >> Call Trace: >> [] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512 >> [] memcpy+0x1d/0x40 mm/kasan/kasan.c:297 >> [] sctp_add_bind_addr+0xa9/0x270 net/sctp/bind_addr.c:162 >> [] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389 >> [] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471 >> [] sctp_setsockopt_bindx+0x2f8/0x3e0 >> net/sctp/socket.c:1010 >> [] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 >> [] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 >> [< inline >] SYSC_setsockopt net/socket.c:1752 >> [] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 >> [] entry_SYSCALL_64_fastpath+0x16/0x7a >> arch/x86/entry/entry_64.S:185 >> >> Memory state around the buggy address: >> 88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >> 88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >> >88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc >> ^ >> 88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >> 88006c636280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >> == >> >> >> sctp_setsockopt_bindx verifies that the user-passed address has valid >> len for the specified family, but then sctp_add_bind_addr copies whole >> sctp_addr from there. This causes heap out-of-bounds access and can >> crash kernel. Not sure if it is possible to copy out the trailing >> garbage to user-space later. >> > > It does more than that though. sctp_setsockopt_bindx checks the following: > 1) That passed addr_size is greater than zero > 2) that the entire range of memory between addrs and addrs+addr_size is > readable > 3) That at least one address structure worth of data is available (implicit in > the while (walk_size < addr_size) loop). > > Could one of the sockaddr_len fields in one of the addresses have been mangled > so that it appeared shorter in the the while loop from (3), so that a copy of > sizeof(sctp_addr in sctp_add_bind_addr overrun the allocated memory? I may be missing something, but what I see is: 1. we check that there is at least family: if (walk_size + sizeof(sa_family_t) > addrs_size) { 2. get family descriptor: af = sctp_get_af_specific(sa_addr->sa_family); 3. check that the address size is enough to hold the declared family: if (!af || (walk_size + af->sockaddr_len) > addrs_size) { 4. then we do sctp_add_bind_addr, which copies whole sctp_addr from addr: int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, ... memcpy(>a, new, sizeof(*new)); Now imagine that the addr is ipv4 (16 or so bytes, that's what we checked) and we copy 28 bytes (ipv6) from addr.
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
On Mon, Jan 25, 2016 at 03:02:38PM +0100, Dmitry Vyukov wrote: > Hello, > > I've git the following error report while running syzkaller fuzzer: > > == > BUG: KASAN: slab-out-of-bounds in memcpy+0x1d/0x40 at addr 88006c6361e8 > Read of size 28 by task syz-executor/12551 > = > BUG kmalloc-16 (Not tainted): kasan: bad access detected > - > > INFO: Allocated in sctp_setsockopt_bindx+0xd2/0x3e0 age=12 cpu=2 pid=12551 > [< inline >] kmalloc include/linux/slab.h:468 > [< none >] sctp_setsockopt_bindx+0xd2/0x3e0 net/sctp/socket.c:975 > [< none >] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 > [< none >] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 > [< inline >] SYSC_setsockopt net/socket.c:1752 > [< none >] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > [< none >] entry_SYSCALL_64_fastpath+0x16/0x7a > arch/x86/entry/entry_64.S:185 > > INFO: Slab 0xea0001b18d80 objects=16 used=4 fp=0x88006c6376e0 > flags=0x5fffc004080 > INFO: Object 0x88006c6361e8 @offset=488 fp=0x0002 > Bytes b4 88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff > ff ff /.4. > Object 88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00 > 01 > CPU: 2 PID: 12551 Comm: syz-executor Tainted: GB 4.5.0-rc1+ #278 > Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 > 880036397928 8299a02d 88003e807900 > 88006c6361e8 88006c636000 880036397958 81752814 > 88003e807900 ea0001b18d80 88006c6361e8 88006c6361e8 > > Call Trace: > [] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512 > [] memcpy+0x1d/0x40 mm/kasan/kasan.c:297 > [] sctp_add_bind_addr+0xa9/0x270 net/sctp/bind_addr.c:162 > [] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389 > [] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471 > [] sctp_setsockopt_bindx+0x2f8/0x3e0 net/sctp/socket.c:1010 > [] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 > [] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 > [< inline >] SYSC_setsockopt net/socket.c:1752 > [] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > [] entry_SYSCALL_64_fastpath+0x16/0x7a > arch/x86/entry/entry_64.S:185 > > Memory state around the buggy address: > 88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > 88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc > ^ > 88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > 88006c636280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > == > > > sctp_setsockopt_bindx verifies that the user-passed address has valid > len for the specified family, but then sctp_add_bind_addr copies whole > sctp_addr from there. This causes heap out-of-bounds access and can > crash kernel. Not sure if it is possible to copy out the trailing > garbage to user-space later. > It does more than that though. sctp_setsockopt_bindx checks the following: 1) That passed addr_size is greater than zero 2) that the entire range of memory between addrs and addrs+addr_size is readable 3) That at least one address structure worth of data is available (implicit in the while (walk_size < addr_size) loop). Could one of the sockaddr_len fields in one of the addresses have been mangled so that it appeared shorter in the the while loop from (3), so that a copy of sizeof(sctp_addr in sctp_add_bind_addr overrun the allocated memory? Neil > On commit 92e963f50fc74041b5e9e744c330dca48e04f08d (Jan 25). > -- > To unsubscribe from this list: send the line "unsubscribe linux-sctp" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html >
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
On Mon, Jan 25, 2016 at 03:02:38PM +0100, Dmitry Vyukov wrote: > Hello, > > I've git the following error report while running syzkaller fuzzer: > > == > BUG: KASAN: slab-out-of-bounds in memcpy+0x1d/0x40 at addr 88006c6361e8 > Read of size 28 by task syz-executor/12551 > = > BUG kmalloc-16 (Not tainted): kasan: bad access detected > - > > INFO: Allocated in sctp_setsockopt_bindx+0xd2/0x3e0 age=12 cpu=2 pid=12551 > [< inline >] kmalloc include/linux/slab.h:468 > [< none >] sctp_setsockopt_bindx+0xd2/0x3e0 net/sctp/socket.c:975 > [< none >] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 > [< none >] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 > [< inline >] SYSC_setsockopt net/socket.c:1752 > [< none >] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > [< none >] entry_SYSCALL_64_fastpath+0x16/0x7a > arch/x86/entry/entry_64.S:185 > > INFO: Slab 0xea0001b18d80 objects=16 used=4 fp=0x88006c6376e0 > flags=0x5fffc004080 > INFO: Object 0x88006c6361e8 @offset=488 fp=0x0002 > Bytes b4 88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff > ff ff /.4. > Object 88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00 > 01 > CPU: 2 PID: 12551 Comm: syz-executor Tainted: GB 4.5.0-rc1+ #278 > Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 > 880036397928 8299a02d 88003e807900 > 88006c6361e8 88006c636000 880036397958 81752814 > 88003e807900 ea0001b18d80 88006c6361e8 88006c6361e8 > > Call Trace: > [] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512 > [] memcpy+0x1d/0x40 mm/kasan/kasan.c:297 > [] sctp_add_bind_addr+0xa9/0x270 net/sctp/bind_addr.c:162 > [] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389 > [] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471 > [] sctp_setsockopt_bindx+0x2f8/0x3e0 net/sctp/socket.c:1010 > [] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 > [] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 > [< inline >] SYSC_setsockopt net/socket.c:1752 > [] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > [] entry_SYSCALL_64_fastpath+0x16/0x7a > arch/x86/entry/entry_64.S:185 > > Memory state around the buggy address: > 88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > 88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc > ^ > 88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > 88006c636280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > == > > > sctp_setsockopt_bindx verifies that the user-passed address has valid > len for the specified family, but then sctp_add_bind_addr copies whole > sctp_addr from there. This causes heap out-of-bounds access and can > crash kernel. Not sure if it is possible to copy out the trailing > garbage to user-space later. > It does more than that though. sctp_setsockopt_bindx checks the following: 1) That passed addr_size is greater than zero 2) that the entire range of memory between addrs and addrs+addr_size is readable 3) That at least one address structure worth of data is available (implicit in the while (walk_size < addr_size) loop). Could one of the sockaddr_len fields in one of the addresses have been mangled so that it appeared shorter in the the while loop from (3), so that a copy of sizeof(sctp_addr in sctp_add_bind_addr overrun the allocated memory? Neil > On commit 92e963f50fc74041b5e9e744c330dca48e04f08d (Jan 25). > -- > To unsubscribe from this list: send the line "unsubscribe linux-sctp" in > the body of a message to majord...@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html >
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
On Mon, Jan 25, 2016 at 03:42:14PM +0100, Dmitry Vyukov wrote: > On Mon, Jan 25, 2016 at 3:31 PM, Neil Hormanwrote: > > On Mon, Jan 25, 2016 at 03:02:38PM +0100, Dmitry Vyukov wrote: > >> Hello, > >> > >> I've git the following error report while running syzkaller fuzzer: > >> > >> == > >> BUG: KASAN: slab-out-of-bounds in memcpy+0x1d/0x40 at addr 88006c6361e8 > >> Read of size 28 by task syz-executor/12551 > >> = > >> BUG kmalloc-16 (Not tainted): kasan: bad access detected > >> - > >> > >> INFO: Allocated in sctp_setsockopt_bindx+0xd2/0x3e0 age=12 cpu=2 pid=12551 > >> [< inline >] kmalloc include/linux/slab.h:468 > >> [< none >] sctp_setsockopt_bindx+0xd2/0x3e0 net/sctp/socket.c:975 > >> [< none >] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 > >> [< none >] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 > >> [< inline >] SYSC_setsockopt net/socket.c:1752 > >> [< none >] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > >> [< none >] entry_SYSCALL_64_fastpath+0x16/0x7a > >> arch/x86/entry/entry_64.S:185 > >> > >> INFO: Slab 0xea0001b18d80 objects=16 used=4 fp=0x88006c6376e0 > >> flags=0x5fffc004080 > >> INFO: Object 0x88006c6361e8 @offset=488 fp=0x0002 > >> Bytes b4 88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff > >> ff ff /.4. > >> Object 88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00 > >> 01 > >> CPU: 2 PID: 12551 Comm: syz-executor Tainted: GB 4.5.0-rc1+ > >> #278 > >> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs > >> 01/01/2011 > >> 880036397928 8299a02d 88003e807900 > >> 88006c6361e8 88006c636000 880036397958 81752814 > >> 88003e807900 ea0001b18d80 88006c6361e8 88006c6361e8 > >> > >> Call Trace: > >> [] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512 > >> [] memcpy+0x1d/0x40 mm/kasan/kasan.c:297 > >> [] sctp_add_bind_addr+0xa9/0x270 > >> net/sctp/bind_addr.c:162 > >> [] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389 > >> [] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471 > >> [] sctp_setsockopt_bindx+0x2f8/0x3e0 > >> net/sctp/socket.c:1010 > >> [] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 > >> [] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 > >> [< inline >] SYSC_setsockopt net/socket.c:1752 > >> [] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > >> [] entry_SYSCALL_64_fastpath+0x16/0x7a > >> arch/x86/entry/entry_64.S:185 > >> > >> Memory state around the buggy address: > >> 88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >> 88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >> >88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc > >> ^ > >> 88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >> 88006c636280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >> == > >> > >> > >> sctp_setsockopt_bindx verifies that the user-passed address has valid > >> len for the specified family, but then sctp_add_bind_addr copies whole > >> sctp_addr from there. This causes heap out-of-bounds access and can > >> crash kernel. Not sure if it is possible to copy out the trailing > >> garbage to user-space later. > >> > > > > It does more than that though. sctp_setsockopt_bindx checks the following: > > 1) That passed addr_size is greater than zero > > 2) that the entire range of memory between addrs and addrs+addr_size is > > readable > > 3) That at least one address structure worth of data is available (implicit > > in > > the while (walk_size < addr_size) loop). > > > > Could one of the sockaddr_len fields in one of the addresses have been > > mangled > > so that it appeared shorter in the the while loop from (3), so that a copy > > of > > sizeof(sctp_addr in sctp_add_bind_addr overrun the allocated memory? > > I may be missing something, but what I see is: > > 1. we check that there is at least family: > if (walk_size + sizeof(sa_family_t) > addrs_size) { > > 2. get family descriptor: > af = sctp_get_af_specific(sa_addr->sa_family); > > 3. check that the address size is enough to hold the declared family: > if (!af || (walk_size + af->sockaddr_len) > addrs_size) { > > 4. then we do sctp_add_bind_addr, which copies whole sctp_addr from addr: > > int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, > ... > memcpy(>a, new, sizeof(*new)); > > Now imagine that the addr is ipv4 (16 or so bytes, that's what we > checked) and we copy 28 bytes
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
On Mon, Jan 25, 2016 at 3:31 PM, Neil Hormanwrote: > On Mon, Jan 25, 2016 at 03:02:38PM +0100, Dmitry Vyukov wrote: >> Hello, >> >> I've git the following error report while running syzkaller fuzzer: >> >> == >> BUG: KASAN: slab-out-of-bounds in memcpy+0x1d/0x40 at addr 88006c6361e8 >> Read of size 28 by task syz-executor/12551 >> = >> BUG kmalloc-16 (Not tainted): kasan: bad access detected >> - >> >> INFO: Allocated in sctp_setsockopt_bindx+0xd2/0x3e0 age=12 cpu=2 pid=12551 >> [< inline >] kmalloc include/linux/slab.h:468 >> [< none >] sctp_setsockopt_bindx+0xd2/0x3e0 net/sctp/socket.c:975 >> [< none >] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 >> [< none >] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 >> [< inline >] SYSC_setsockopt net/socket.c:1752 >> [< none >] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 >> [< none >] entry_SYSCALL_64_fastpath+0x16/0x7a >> arch/x86/entry/entry_64.S:185 >> >> INFO: Slab 0xea0001b18d80 objects=16 used=4 fp=0x88006c6376e0 >> flags=0x5fffc004080 >> INFO: Object 0x88006c6361e8 @offset=488 fp=0x0002 >> Bytes b4 88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff >> ff ff /.4. >> Object 88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00 >> 01 >> CPU: 2 PID: 12551 Comm: syz-executor Tainted: GB 4.5.0-rc1+ >> #278 >> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 >> 880036397928 8299a02d 88003e807900 >> 88006c6361e8 88006c636000 880036397958 81752814 >> 88003e807900 ea0001b18d80 88006c6361e8 88006c6361e8 >> >> Call Trace: >> [] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512 >> [] memcpy+0x1d/0x40 mm/kasan/kasan.c:297 >> [] sctp_add_bind_addr+0xa9/0x270 net/sctp/bind_addr.c:162 >> [] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389 >> [] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471 >> [] sctp_setsockopt_bindx+0x2f8/0x3e0 >> net/sctp/socket.c:1010 >> [] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 >> [] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 >> [< inline >] SYSC_setsockopt net/socket.c:1752 >> [] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 >> [] entry_SYSCALL_64_fastpath+0x16/0x7a >> arch/x86/entry/entry_64.S:185 >> >> Memory state around the buggy address: >> 88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >> 88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >> >88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc >> ^ >> 88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >> 88006c636280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc >> == >> >> >> sctp_setsockopt_bindx verifies that the user-passed address has valid >> len for the specified family, but then sctp_add_bind_addr copies whole >> sctp_addr from there. This causes heap out-of-bounds access and can >> crash kernel. Not sure if it is possible to copy out the trailing >> garbage to user-space later. >> > > It does more than that though. sctp_setsockopt_bindx checks the following: > 1) That passed addr_size is greater than zero > 2) that the entire range of memory between addrs and addrs+addr_size is > readable > 3) That at least one address structure worth of data is available (implicit in > the while (walk_size < addr_size) loop). > > Could one of the sockaddr_len fields in one of the addresses have been mangled > so that it appeared shorter in the the while loop from (3), so that a copy of > sizeof(sctp_addr in sctp_add_bind_addr overrun the allocated memory? I may be missing something, but what I see is: 1. we check that there is at least family: if (walk_size + sizeof(sa_family_t) > addrs_size) { 2. get family descriptor: af = sctp_get_af_specific(sa_addr->sa_family); 3. check that the address size is enough to hold the declared family: if (!af || (walk_size + af->sockaddr_len) > addrs_size) { 4. then we do sctp_add_bind_addr, which copies whole sctp_addr from addr: int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, ... memcpy(>a, new, sizeof(*new)); Now imagine that the addr is ipv4 (16 or so bytes, that's what we checked) and we copy 28 bytes (ipv6) from addr.
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
Something like this. Builds, but UNTESTED. Uses union sizeof where possible but when reading from a buffer that is not aligned to it, like that user supplied one. Then relies on af->sockaddr_len --8<-- --- include/net/sctp/structs.h | 2 +- net/sctp/bind_addr.c | 14 -- net/sctp/protocol.c| 1 + net/sctp/sm_make_chunk.c | 2 +- net/sctp/socket.c | 5 +++-- 5 files changed, 14 insertions(+), 10 deletions(-) diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 20e72129be1ce0063eeafcbaadcee1f37e0c614c..97ba8a8c466f5c50bdc87ec578792e56553baa91 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1099,7 +1099,7 @@ int sctp_bind_addr_dup(struct sctp_bind_addr *dest, const struct sctp_bind_addr *src, gfp_t gfp); int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *, - __u8 addr_state, gfp_t gfp); + int new_size, __u8 addr_state, gfp_t gfp); int sctp_del_bind_addr(struct sctp_bind_addr *, union sctp_addr *); int sctp_bind_addr_match(struct sctp_bind_addr *, const union sctp_addr *, struct sctp_sock *); diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 871cdf9567e6bc9c13cb1077dc6866a67e6e4367..80129d10a0af9c33e7348b79d010b9e5e948e584 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -111,7 +111,8 @@ int sctp_bind_addr_dup(struct sctp_bind_addr *dest, dest->port = src->port; list_for_each_entry(addr, >address_list, list) { - error = sctp_add_bind_addr(dest, >a, 1, gfp); + error = sctp_add_bind_addr(dest, >a, sizeof(addr->a), + 1, gfp); if (error < 0) break; } @@ -150,7 +151,7 @@ void sctp_bind_addr_free(struct sctp_bind_addr *bp) /* Add an address to the bind address list in the SCTP_bind_addr structure. */ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, - __u8 addr_state, gfp_t gfp) + int new_size, __u8 addr_state, gfp_t gfp) { struct sctp_sockaddr_entry *addr; @@ -159,7 +160,7 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, if (!addr) return -ENOMEM; - memcpy(>a, new, sizeof(*new)); + memcpy(>a, new, min_t(size_t, sizeof(*new), new_size)); /* Fix up the port if it has not yet been set. * Both v4 and v6 have the port at the same offset. @@ -291,7 +292,8 @@ int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw_addr_list, } af->from_addr_param(, rawaddr, htons(port), 0); - retval = sctp_add_bind_addr(bp, , SCTP_ADDR_SRC, gfp); + retval = sctp_add_bind_addr(bp, , sizeof(addr), + SCTP_ADDR_SRC, gfp); if (retval) { /* Can't finish building the list, clean up. */ sctp_bind_addr_clean(bp); @@ -453,8 +455,8 @@ static int sctp_copy_one_addr(struct net *net, struct sctp_bind_addr *dest, (((AF_INET6 == addr->sa.sa_family) && (flags & SCTP_ADDR6_ALLOWED) && (flags & SCTP_ADDR6_PEERSUPP - error = sctp_add_bind_addr(dest, addr, SCTP_ADDR_SRC, - gfp); + error = sctp_add_bind_addr(dest, addr, sizeof(addr), + SCTP_ADDR_SRC, gfp); } return error; diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index ab0d538a74ed593571cfaef02cd1bb7ce872abe6..2fb609008311f51344704d82f21b4de9f08253da 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -214,6 +214,7 @@ int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *bp, (copy_flags & SCTP_ADDR6_ALLOWED) && (copy_flags & SCTP_ADDR6_PEERSUPP { error = sctp_add_bind_addr(bp, >a, + sizeof(addr->a), SCTP_ADDR_SRC, GFP_ATOMIC); if (error) goto end_copy; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 5d6a03fad3789a12290f5f14c5a7efa69c98f41a..1b91e9760fe514db6d89457e1d5da9e02800745e 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1830,7 +1830,7 @@ no_hmac: /* Also, add the destination address. */ if (list_empty(>base.bind_addr.address_list)) { sctp_add_bind_addr(>base.bind_addr, >dest, - SCTP_ADDR_SRC, GFP_ATOMIC); +
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
On Mon, Jan 25, 2016 at 12:48:02PM -0200, Marcelo Ricardo Leitner wrote: > On Mon, Jan 25, 2016 at 03:42:14PM +0100, Dmitry Vyukov wrote: > > On Mon, Jan 25, 2016 at 3:31 PM, Neil Hormanwrote: > > > On Mon, Jan 25, 2016 at 03:02:38PM +0100, Dmitry Vyukov wrote: > > >> Hello, > > >> > > >> I've git the following error report while running syzkaller fuzzer: > > >> > > >> == > > >> BUG: KASAN: slab-out-of-bounds in memcpy+0x1d/0x40 at addr > > >> 88006c6361e8 > > >> Read of size 28 by task syz-executor/12551 > > >> = > > >> BUG kmalloc-16 (Not tainted): kasan: bad access detected > > >> - > > >> > > >> INFO: Allocated in sctp_setsockopt_bindx+0xd2/0x3e0 age=12 cpu=2 > > >> pid=12551 > > >> [< inline >] kmalloc include/linux/slab.h:468 > > >> [< none >] sctp_setsockopt_bindx+0xd2/0x3e0 > > >> net/sctp/socket.c:975 > > >> [< none >] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 > > >> [< none >] sock_common_setsockopt+0x97/0xd0 > > >> net/core/sock.c:2620 > > >> [< inline >] SYSC_setsockopt net/socket.c:1752 > > >> [< none >] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > > >> [< none >] entry_SYSCALL_64_fastpath+0x16/0x7a > > >> arch/x86/entry/entry_64.S:185 > > >> > > >> INFO: Slab 0xea0001b18d80 objects=16 used=4 fp=0x88006c6376e0 > > >> flags=0x5fffc004080 > > >> INFO: Object 0x88006c6361e8 @offset=488 fp=0x0002 > > >> Bytes b4 88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff > > >> ff ff /.4. > > >> Object 88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00 > > >> 01 > > >> CPU: 2 PID: 12551 Comm: syz-executor Tainted: GB > > >> 4.5.0-rc1+ #278 > > >> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs > > >> 01/01/2011 > > >> 880036397928 8299a02d 88003e807900 > > >> 88006c6361e8 88006c636000 880036397958 81752814 > > >> 88003e807900 ea0001b18d80 88006c6361e8 88006c6361e8 > > >> > > >> Call Trace: > > >> [] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512 > > >> [] memcpy+0x1d/0x40 mm/kasan/kasan.c:297 > > >> [] sctp_add_bind_addr+0xa9/0x270 > > >> net/sctp/bind_addr.c:162 > > >> [] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389 > > >> [] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471 > > >> [] sctp_setsockopt_bindx+0x2f8/0x3e0 > > >> net/sctp/socket.c:1010 > > >> [] sctp_setsockopt+0x1493/0x3630 > > >> net/sctp/socket.c:3711 > > >> [] sock_common_setsockopt+0x97/0xd0 > > >> net/core/sock.c:2620 > > >> [< inline >] SYSC_setsockopt net/socket.c:1752 > > >> [] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > > >> [] entry_SYSCALL_64_fastpath+0x16/0x7a > > >> arch/x86/entry/entry_64.S:185 > > >> > > >> Memory state around the buggy address: > > >> 88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > > >> 88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > > >> >88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc > > >> ^ > > >> 88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > > >> 88006c636280: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > > >> == > > >> > > >> > > >> sctp_setsockopt_bindx verifies that the user-passed address has valid > > >> len for the specified family, but then sctp_add_bind_addr copies whole > > >> sctp_addr from there. This causes heap out-of-bounds access and can > > >> crash kernel. Not sure if it is possible to copy out the trailing > > >> garbage to user-space later. > > >> > > > > > > It does more than that though. sctp_setsockopt_bindx checks the > > > following: > > > 1) That passed addr_size is greater than zero > > > 2) that the entire range of memory between addrs and addrs+addr_size is > > > readable > > > 3) That at least one address structure worth of data is available > > > (implicit in > > > the while (walk_size < addr_size) loop). > > > > > > Could one of the sockaddr_len fields in one of the addresses have been > > > mangled > > > so that it appeared shorter in the the while loop from (3), so that a > > > copy of > > > sizeof(sctp_addr in sctp_add_bind_addr overrun the allocated memory? > > > > I may be missing something, but what I see is: > > > > 1. we check that there is at least family: > > if (walk_size + sizeof(sa_family_t) > addrs_size) { > > > > 2. get family descriptor: > > af = sctp_get_af_specific(sa_addr->sa_family); > > > > 3. check that the address size is enough to hold the declared family: > > if (!af || (walk_size +
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr
On Mon, Jan 25, 2016 at 02:16:00PM -0200, Marcelo Ricardo Leitner wrote: > Something like this. Builds, but UNTESTED. > Uses union sizeof where possible but when reading from a buffer that is > not aligned to it, like that user supplied one. Then relies on > af->sockaddr_len > > --8<-- > > --- > include/net/sctp/structs.h | 2 +- > net/sctp/bind_addr.c | 14 -- > net/sctp/protocol.c| 1 + > net/sctp/sm_make_chunk.c | 2 +- > net/sctp/socket.c | 5 +++-- > 5 files changed, 14 insertions(+), 10 deletions(-) > > diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h > index > 20e72129be1ce0063eeafcbaadcee1f37e0c614c..97ba8a8c466f5c50bdc87ec578792e56553baa91 > 100644 > --- a/include/net/sctp/structs.h > +++ b/include/net/sctp/structs.h > @@ -1099,7 +1099,7 @@ int sctp_bind_addr_dup(struct sctp_bind_addr *dest, > const struct sctp_bind_addr *src, > gfp_t gfp); > int sctp_add_bind_addr(struct sctp_bind_addr *, union sctp_addr *, > -__u8 addr_state, gfp_t gfp); > +int new_size, __u8 addr_state, gfp_t gfp); > int sctp_del_bind_addr(struct sctp_bind_addr *, union sctp_addr *); > int sctp_bind_addr_match(struct sctp_bind_addr *, const union sctp_addr *, >struct sctp_sock *); > diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c > index > 871cdf9567e6bc9c13cb1077dc6866a67e6e4367..80129d10a0af9c33e7348b79d010b9e5e948e584 > 100644 > --- a/net/sctp/bind_addr.c > +++ b/net/sctp/bind_addr.c > @@ -111,7 +111,8 @@ int sctp_bind_addr_dup(struct sctp_bind_addr *dest, > dest->port = src->port; > > list_for_each_entry(addr, >address_list, list) { > - error = sctp_add_bind_addr(dest, >a, 1, gfp); > + error = sctp_add_bind_addr(dest, >a, sizeof(addr->a), > +1, gfp); > if (error < 0) > break; > } > @@ -150,7 +151,7 @@ void sctp_bind_addr_free(struct sctp_bind_addr *bp) > > /* Add an address to the bind address list in the SCTP_bind_addr structure. > */ > int sctp_add_bind_addr(struct sctp_bind_addr *bp, union sctp_addr *new, > -__u8 addr_state, gfp_t gfp) > +int new_size, __u8 addr_state, gfp_t gfp) > { > struct sctp_sockaddr_entry *addr; > > @@ -159,7 +160,7 @@ int sctp_add_bind_addr(struct sctp_bind_addr *bp, union > sctp_addr *new, > if (!addr) > return -ENOMEM; > > - memcpy(>a, new, sizeof(*new)); > + memcpy(>a, new, min_t(size_t, sizeof(*new), new_size)); > > /* Fix up the port if it has not yet been set. >* Both v4 and v6 have the port at the same offset. > @@ -291,7 +292,8 @@ int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, > __u8 *raw_addr_list, > } > > af->from_addr_param(, rawaddr, htons(port), 0); > - retval = sctp_add_bind_addr(bp, , SCTP_ADDR_SRC, gfp); > + retval = sctp_add_bind_addr(bp, , sizeof(addr), > + SCTP_ADDR_SRC, gfp); > if (retval) { > /* Can't finish building the list, clean up. */ > sctp_bind_addr_clean(bp); > @@ -453,8 +455,8 @@ static int sctp_copy_one_addr(struct net *net, struct > sctp_bind_addr *dest, > (((AF_INET6 == addr->sa.sa_family) && > (flags & SCTP_ADDR6_ALLOWED) && > (flags & SCTP_ADDR6_PEERSUPP > - error = sctp_add_bind_addr(dest, addr, SCTP_ADDR_SRC, > - gfp); > + error = sctp_add_bind_addr(dest, addr, sizeof(addr), > +SCTP_ADDR_SRC, gfp); > } > > return error; > diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c > index > ab0d538a74ed593571cfaef02cd1bb7ce872abe6..2fb609008311f51344704d82f21b4de9f08253da > 100644 > --- a/net/sctp/protocol.c > +++ b/net/sctp/protocol.c > @@ -214,6 +214,7 @@ int sctp_copy_local_addr_list(struct net *net, struct > sctp_bind_addr *bp, > (copy_flags & SCTP_ADDR6_ALLOWED) && > (copy_flags & SCTP_ADDR6_PEERSUPP { > error = sctp_add_bind_addr(bp, >a, > + sizeof(addr->a), > SCTP_ADDR_SRC, GFP_ATOMIC); > if (error) > goto end_copy; > diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c > index > 5d6a03fad3789a12290f5f14c5a7efa69c98f41a..1b91e9760fe514db6d89457e1d5da9e02800745e > 100644 > --- a/net/sctp/sm_make_chunk.c > +++ b/net/sctp/sm_make_chunk.c > @@ -1830,7 +1830,7 @@ no_hmac: > /* Also, add the destination address. */ >