Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1316738 > unrolled thread
| Started by | Dmitry Vyukov <dvyukov@google.com> |
|---|---|
| First post | 2016-01-25 15:10 +0100 |
| Last post | 2016-01-26 14:30 +0100 |
| Articles | 9 — 3 participants |
Back to article view | Back to linux.kernel
net/sctp: out-of-bounds access in sctp_add_bind_addr Dmitry Vyukov <dvyukov@google.com> - 2016-01-25 15:10 +0100
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr Neil Horman <nhorman@tuxdriver.com> - 2016-01-25 15:40 +0100
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr Dmitry Vyukov <dvyukov@google.com> - 2016-01-25 15:50 +0100
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> - 2016-01-25 15:50 +0100
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr Neil Horman <nhorman@tuxdriver.com> - 2016-01-25 17:10 +0100
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> - 2016-01-25 17:20 +0100
Re: net/sctp: out-of-bounds access in sctp_add_bind_addr Neil Horman <nhorman@tuxdriver.com> - 2016-01-25 18:30 +0100
[PATCH net] sctp: fix copying more bytes than expected in sctp_add_bind_addr Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> - 2016-01-25 19:00 +0100
Re: [PATCH net] sctp: fix copying more bytes than expected in sctp_add_bind_addr Dmitry Vyukov <dvyukov@google.com> - 2016-01-26 14:30 +0100
| From | Dmitry Vyukov <dvyukov@google.com> |
|---|---|
| Date | 2016-01-25 15:10 +0100 |
| Subject | net/sctp: out-of-bounds access in sctp_add_bind_addr |
| Message-ID | <qUQ0b-54G-41@gated-at.bofh.it> |
Hello,
I've git the following error report while running syzkaller fuzzer:
==================================================================
BUG: KASAN: slab-out-of-bounds in memcpy+0x1d/0x40 at addr ffff88006c6361e8
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 0xffffea0001b18d80 objects=16 used=4 fp=0xffff88006c6376e0
flags=0x5fffc0000004080
INFO: Object 0xffff88006c6361e8 @offset=488 fp=0x0000000000000002
Bytes b4 ffff88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff
ff ff ......../.4.....
Object ffff88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00
01 ................
CPU: 2 PID: 12551 Comm: syz-executor Tainted: G B 4.5.0-rc1+ #278
Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
00000000ffffffff ffff880036397928 ffffffff8299a02d ffff88003e807900
ffff88006c6361e8 ffff88006c636000 ffff880036397958 ffffffff81752814
ffff88003e807900 ffffea0001b18d80 ffff88006c6361e8 ffff88006c6361e8
Call Trace:
[<ffffffff8175ad54>] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512
[<ffffffff8175b2dd>] memcpy+0x1d/0x40 mm/kasan/kasan.c:297
[<ffffffff85dcb249>] sctp_add_bind_addr+0xa9/0x270 net/sctp/bind_addr.c:162
[<ffffffff85dcfd66>] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389
[<ffffffff85dd16ec>] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471
[<ffffffff85dd5cc8>] sctp_setsockopt_bindx+0x2f8/0x3e0 net/sctp/socket.c:1010
[<ffffffff85dde283>] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711
[<ffffffff851f5ae7>] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620
[< inline >] SYSC_setsockopt net/socket.c:1752
[<ffffffff851f2c3b>] SyS_setsockopt+0x15b/0x250 net/socket.c:1731
[<ffffffff863595f6>] entry_SYSCALL_64_fastpath+0x16/0x7a
arch/x86/entry/entry_64.S:185
Memory state around the buggy address:
ffff88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>ffff88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc
^
ffff88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
ffff88006c636280: 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.
On commit 92e963f50fc74041b5e9e744c330dca48e04f08d (Jan 25).
[toc] | [next] | [standalone]
| From | Neil Horman <nhorman@tuxdriver.com> |
|---|---|
| Date | 2016-01-25 15:40 +0100 |
| Message-ID | <qUQtc-5oH-9@gated-at.bofh.it> |
| In reply to | #1316738 |
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 ffff88006c6361e8 > 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 0xffffea0001b18d80 objects=16 used=4 fp=0xffff88006c6376e0 > flags=0x5fffc0000004080 > INFO: Object 0xffff88006c6361e8 @offset=488 fp=0x0000000000000002 > Bytes b4 ffff88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff > ff ff ......../.4..... > Object ffff88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00 > 01 ................ > CPU: 2 PID: 12551 Comm: syz-executor Tainted: G B 4.5.0-rc1+ #278 > Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 > 00000000ffffffff ffff880036397928 ffffffff8299a02d ffff88003e807900 > ffff88006c6361e8 ffff88006c636000 ffff880036397958 ffffffff81752814 > ffff88003e807900 ffffea0001b18d80 ffff88006c6361e8 ffff88006c6361e8 > > Call Trace: > [<ffffffff8175ad54>] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512 > [<ffffffff8175b2dd>] memcpy+0x1d/0x40 mm/kasan/kasan.c:297 > [<ffffffff85dcb249>] sctp_add_bind_addr+0xa9/0x270 net/sctp/bind_addr.c:162 > [<ffffffff85dcfd66>] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389 > [<ffffffff85dd16ec>] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471 > [<ffffffff85dd5cc8>] sctp_setsockopt_bindx+0x2f8/0x3e0 net/sctp/socket.c:1010 > [<ffffffff85dde283>] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711 > [<ffffffff851f5ae7>] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620 > [< inline >] SYSC_setsockopt net/socket.c:1752 > [<ffffffff851f2c3b>] SyS_setsockopt+0x15b/0x250 net/socket.c:1731 > [<ffffffff863595f6>] entry_SYSCALL_64_fastpath+0x16/0x7a > arch/x86/entry/entry_64.S:185 > > Memory state around the buggy address: > ffff88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > ffff88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > >ffff88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc > ^ > ffff88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc > ffff88006c636280: 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 majordomo@vger.kernel.org > More majordomo info at http://vger.kernel.org/majordomo-info.html >
[toc] | [prev] | [next] | [standalone]
| From | Dmitry Vyukov <dvyukov@google.com> |
|---|---|
| Date | 2016-01-25 15:50 +0100 |
| Message-ID | <qUQCS-5sa-17@gated-at.bofh.it> |
| In reply to | #1316763 |
On Mon, Jan 25, 2016 at 3:31 PM, Neil Horman <nhorman@tuxdriver.com> 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 ffff88006c6361e8
>> 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 0xffffea0001b18d80 objects=16 used=4 fp=0xffff88006c6376e0
>> flags=0x5fffc0000004080
>> INFO: Object 0xffff88006c6361e8 @offset=488 fp=0x0000000000000002
>> Bytes b4 ffff88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff
>> ff ff ......../.4.....
>> Object ffff88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00
>> 01 ................
>> CPU: 2 PID: 12551 Comm: syz-executor Tainted: G B 4.5.0-rc1+ #278
>> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
>> 00000000ffffffff ffff880036397928 ffffffff8299a02d ffff88003e807900
>> ffff88006c6361e8 ffff88006c636000 ffff880036397958 ffffffff81752814
>> ffff88003e807900 ffffea0001b18d80 ffff88006c6361e8 ffff88006c6361e8
>>
>> Call Trace:
>> [<ffffffff8175ad54>] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512
>> [<ffffffff8175b2dd>] memcpy+0x1d/0x40 mm/kasan/kasan.c:297
>> [<ffffffff85dcb249>] sctp_add_bind_addr+0xa9/0x270 net/sctp/bind_addr.c:162
>> [<ffffffff85dcfd66>] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389
>> [<ffffffff85dd16ec>] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471
>> [<ffffffff85dd5cc8>] sctp_setsockopt_bindx+0x2f8/0x3e0 net/sctp/socket.c:1010
>> [<ffffffff85dde283>] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711
>> [<ffffffff851f5ae7>] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620
>> [< inline >] SYSC_setsockopt net/socket.c:1752
>> [<ffffffff851f2c3b>] SyS_setsockopt+0x15b/0x250 net/socket.c:1731
>> [<ffffffff863595f6>] entry_SYSCALL_64_fastpath+0x16/0x7a
>> arch/x86/entry/entry_64.S:185
>>
>> Memory state around the buggy address:
>> ffff88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>> ffff88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>> >ffff88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc
>> ^
>> ffff88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
>> ffff88006c636280: 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(&addr->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.
[toc] | [prev] | [next] | [standalone]
| From | Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> |
|---|---|
| Date | 2016-01-25 15:50 +0100 |
| Message-ID | <qUQCS-5sa-19@gated-at.bofh.it> |
| In reply to | #1316772 |
On Mon, Jan 25, 2016 at 03:42:14PM +0100, Dmitry Vyukov wrote:
> On Mon, Jan 25, 2016 at 3:31 PM, Neil Horman <nhorman@tuxdriver.com> 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 ffff88006c6361e8
> >> 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 0xffffea0001b18d80 objects=16 used=4 fp=0xffff88006c6376e0
> >> flags=0x5fffc0000004080
> >> INFO: Object 0xffff88006c6361e8 @offset=488 fp=0x0000000000000002
> >> Bytes b4 ffff88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff
> >> ff ff ......../.4.....
> >> Object ffff88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00
> >> 01 ................
> >> CPU: 2 PID: 12551 Comm: syz-executor Tainted: G B 4.5.0-rc1+ #278
> >> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
> >> 00000000ffffffff ffff880036397928 ffffffff8299a02d ffff88003e807900
> >> ffff88006c6361e8 ffff88006c636000 ffff880036397958 ffffffff81752814
> >> ffff88003e807900 ffffea0001b18d80 ffff88006c6361e8 ffff88006c6361e8
> >>
> >> Call Trace:
> >> [<ffffffff8175ad54>] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512
> >> [<ffffffff8175b2dd>] memcpy+0x1d/0x40 mm/kasan/kasan.c:297
> >> [<ffffffff85dcb249>] sctp_add_bind_addr+0xa9/0x270 net/sctp/bind_addr.c:162
> >> [<ffffffff85dcfd66>] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389
> >> [<ffffffff85dd16ec>] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471
> >> [<ffffffff85dd5cc8>] sctp_setsockopt_bindx+0x2f8/0x3e0 net/sctp/socket.c:1010
> >> [<ffffffff85dde283>] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711
> >> [<ffffffff851f5ae7>] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620
> >> [< inline >] SYSC_setsockopt net/socket.c:1752
> >> [<ffffffff851f2c3b>] SyS_setsockopt+0x15b/0x250 net/socket.c:1731
> >> [<ffffffff863595f6>] entry_SYSCALL_64_fastpath+0x16/0x7a
> >> arch/x86/entry/entry_64.S:185
> >>
> >> Memory state around the buggy address:
> >> ffff88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> >> ffff88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> >> >ffff88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc
> >> ^
> >> ffff88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> >> ffff88006c636280: 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(&addr->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.
Yes, that's pretty much it I think. That memcpy should be limited to
af->sockaddr_len, it's just that af is not readily available in that
function.
Marcelo
[toc] | [prev] | [next] | [standalone]
| From | Neil Horman <nhorman@tuxdriver.com> |
|---|---|
| Date | 2016-01-25 17:10 +0100 |
| Message-ID | <qURSi-6uy-25@gated-at.bofh.it> |
| In reply to | #1316773 |
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 <nhorman@tuxdriver.com> 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 ffff88006c6361e8
> > >> 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 0xffffea0001b18d80 objects=16 used=4 fp=0xffff88006c6376e0
> > >> flags=0x5fffc0000004080
> > >> INFO: Object 0xffff88006c6361e8 @offset=488 fp=0x0000000000000002
> > >> Bytes b4 ffff88006c6361d8: 00 00 00 00 00 00 00 00 2f 98 34 88 ff ff
> > >> ff ff ......../.4.....
> > >> Object ffff88006c6361e8: 02 00 00 00 00 00 00 00 02 00 ab 07 7f 00 00
> > >> 01 ................
> > >> CPU: 2 PID: 12551 Comm: syz-executor Tainted: G B 4.5.0-rc1+ #278
> > >> Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011
> > >> 00000000ffffffff ffff880036397928 ffffffff8299a02d ffff88003e807900
> > >> ffff88006c6361e8 ffff88006c636000 ffff880036397958 ffffffff81752814
> > >> ffff88003e807900 ffffea0001b18d80 ffff88006c6361e8 ffff88006c6361e8
> > >>
> > >> Call Trace:
> > >> [<ffffffff8175ad54>] __asan_loadN+0x124/0x1a0 mm/kasan/kasan.c:512
> > >> [<ffffffff8175b2dd>] memcpy+0x1d/0x40 mm/kasan/kasan.c:297
> > >> [<ffffffff85dcb249>] sctp_add_bind_addr+0xa9/0x270 net/sctp/bind_addr.c:162
> > >> [<ffffffff85dcfd66>] sctp_do_bind+0x336/0x580 net/sctp/socket.c:389
> > >> [<ffffffff85dd16ec>] sctp_bindx_add+0xac/0x1a0 net/sctp/socket.c:471
> > >> [<ffffffff85dd5cc8>] sctp_setsockopt_bindx+0x2f8/0x3e0 net/sctp/socket.c:1010
> > >> [<ffffffff85dde283>] sctp_setsockopt+0x1493/0x3630 net/sctp/socket.c:3711
> > >> [<ffffffff851f5ae7>] sock_common_setsockopt+0x97/0xd0 net/core/sock.c:2620
> > >> [< inline >] SYSC_setsockopt net/socket.c:1752
> > >> [<ffffffff851f2c3b>] SyS_setsockopt+0x15b/0x250 net/socket.c:1731
> > >> [<ffffffff863595f6>] entry_SYSCALL_64_fastpath+0x16/0x7a
> > >> arch/x86/entry/entry_64.S:185
> > >>
> > >> Memory state around the buggy address:
> > >> ffff88006c636080: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> > >> ffff88006c636100: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> > >> >ffff88006c636180: fc fc fc fc fc fc fc fc fc fc fc fc fc 00 00 fc
> > >> ^
> > >> ffff88006c636200: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
> > >> ffff88006c636280: 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(&addr->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.
>
> Yes, that's pretty much it I think. That memcpy should be limited to
> af->sockaddr_len, it's just that af is not readily available in that
> function.
>
Yeah, ok, we're on the same page. If the size of the sctp_addr struct is larger
than the size that the address family specifies, we're up the creek. We should
augment sctp_add_bind_addr to take the family length as a parameter and either
limit the copy to the min of the sruct size and the family size
Neil
> Marcelo
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
[toc] | [prev] | [next] | [standalone]
| From | Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> |
|---|---|
| Date | 2016-01-25 17:20 +0100 |
| Message-ID | <qUS1Y-6yn-9@gated-at.bofh.it> |
| In reply to | #1316917 |
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, &src->address_list, list) {
- error = sctp_add_bind_addr(dest, &addr->a, 1, gfp);
+ error = sctp_add_bind_addr(dest, &addr->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(&addr->a, new, sizeof(*new));
+ memcpy(&addr->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(&addr, rawaddr, htons(port), 0);
- retval = sctp_add_bind_addr(bp, &addr, SCTP_ADDR_SRC, gfp);
+ retval = sctp_add_bind_addr(bp, &addr, 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, &addr->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(&retval->base.bind_addr.address_list)) {
sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest,
- SCTP_ADDR_SRC, GFP_ATOMIC);
+ sizeof(chunk->dest), SCTP_ADDR_SRC, GFP_ATOMIC);
}
retval->next_tsn = retval->c.initial_tsn;
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 9bb80ec4c08ff06f6e629078c5a926c3def3ce23..3765f1fd06aac253ec5ee8e8bd18fffefda64d62 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -386,7 +386,8 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
/* Add the address to the bind address list.
* Use GFP_ATOMIC since BHs will be disabled.
*/
- ret = sctp_add_bind_addr(bp, addr, SCTP_ADDR_SRC, GFP_ATOMIC);
+ ret = sctp_add_bind_addr(bp, addr, af->sockaddr_len,
+ SCTP_ADDR_SRC, GFP_ATOMIC);
/* Copy back into socket for getsockname() use. */
if (!ret) {
@@ -576,7 +577,7 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
addr = addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family);
memcpy(&saveaddr, addr, af->sockaddr_len);
- retval = sctp_add_bind_addr(bp, &saveaddr,
+ retval = sctp_add_bind_addr(bp, &saveaddr, sizeof(saveaddr),
SCTP_ADDR_NEW, GFP_ATOMIC);
addr_buf += af->sockaddr_len;
}
--
2.5.0
[toc] | [prev] | [next] | [standalone]
| From | Neil Horman <nhorman@tuxdriver.com> |
|---|---|
| Date | 2016-01-25 18:30 +0100 |
| Message-ID | <qUT7I-7lX-19@gated-at.bofh.it> |
| In reply to | #1316941 |
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, &src->address_list, list) {
> - error = sctp_add_bind_addr(dest, &addr->a, 1, gfp);
> + error = sctp_add_bind_addr(dest, &addr->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(&addr->a, new, sizeof(*new));
> + memcpy(&addr->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(&addr, rawaddr, htons(port), 0);
> - retval = sctp_add_bind_addr(bp, &addr, SCTP_ADDR_SRC, gfp);
> + retval = sctp_add_bind_addr(bp, &addr, 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, &addr->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(&retval->base.bind_addr.address_list)) {
> sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest,
> - SCTP_ADDR_SRC, GFP_ATOMIC);
> + sizeof(chunk->dest), SCTP_ADDR_SRC, GFP_ATOMIC);
> }
>
> retval->next_tsn = retval->c.initial_tsn;
> diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> index 9bb80ec4c08ff06f6e629078c5a926c3def3ce23..3765f1fd06aac253ec5ee8e8bd18fffefda64d62 100644
> --- a/net/sctp/socket.c
> +++ b/net/sctp/socket.c
> @@ -386,7 +386,8 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
> /* Add the address to the bind address list.
> * Use GFP_ATOMIC since BHs will be disabled.
> */
> - ret = sctp_add_bind_addr(bp, addr, SCTP_ADDR_SRC, GFP_ATOMIC);
> + ret = sctp_add_bind_addr(bp, addr, af->sockaddr_len,
> + SCTP_ADDR_SRC, GFP_ATOMIC);
>
> /* Copy back into socket for getsockname() use. */
> if (!ret) {
> @@ -576,7 +577,7 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
> addr = addr_buf;
> af = sctp_get_af_specific(addr->v4.sin_family);
> memcpy(&saveaddr, addr, af->sockaddr_len);
> - retval = sctp_add_bind_addr(bp, &saveaddr,
> + retval = sctp_add_bind_addr(bp, &saveaddr, sizeof(saveaddr),
> SCTP_ADDR_NEW, GFP_ATOMIC);
> addr_buf += af->sockaddr_len;
> }
> --
> 2.5.0
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sctp" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
Agreed, that looks correct
Neil
[toc] | [prev] | [next] | [standalone]
| From | Marcelo Ricardo Leitner <marcelo.leitner@gmail.com> |
|---|---|
| Date | 2016-01-25 19:00 +0100 |
| Subject | [PATCH net] sctp: fix copying more bytes than expected in sctp_add_bind_addr |
| Message-ID | <qUTAK-7zc-5@gated-at.bofh.it> |
| In reply to | #1317104 |
Great. Dmitry, please give this a run. Local tests looked good but who
knows what syzkaller may find.
Thanks
--8<--
Dmitry reported that sctp_add_bind_addr may read more bytes than
expected in case the parameter is a IPv4 addr supplied by the user
through calls such as sctp_bindx_add(), because it always copies
sizeof(union sctp_addr) while the buffer may be just a struct
sockaddr_in, which is smaller.
This patch then fixes it by limiting the memcpy to the min between the
union size and a (new parameter) provided addr size. Where possible this
parameter still is the size of that union, except for reading from
user-provided buffers, which then it accounts for protocol type.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
---
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, &src->address_list, list) {
- error = sctp_add_bind_addr(dest, &addr->a, 1, gfp);
+ error = sctp_add_bind_addr(dest, &addr->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(&addr->a, new, sizeof(*new));
+ memcpy(&addr->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(&addr, rawaddr, htons(port), 0);
- retval = sctp_add_bind_addr(bp, &addr, SCTP_ADDR_SRC, gfp);
+ retval = sctp_add_bind_addr(bp, &addr, 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, &addr->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(&retval->base.bind_addr.address_list)) {
sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest,
- SCTP_ADDR_SRC, GFP_ATOMIC);
+ sizeof(chunk->dest), SCTP_ADDR_SRC, GFP_ATOMIC);
}
retval->next_tsn = retval->c.initial_tsn;
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 5ca2ebfe0be83882fcb841de6fa8029b6455ef85..213be3821a1d49e0c469c4ad4e9ff055a03205c5 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -386,7 +386,8 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
/* Add the address to the bind address list.
* Use GFP_ATOMIC since BHs will be disabled.
*/
- ret = sctp_add_bind_addr(bp, addr, SCTP_ADDR_SRC, GFP_ATOMIC);
+ ret = sctp_add_bind_addr(bp, addr, af->sockaddr_len,
+ SCTP_ADDR_SRC, GFP_ATOMIC);
/* Copy back into socket for getsockname() use. */
if (!ret) {
@@ -576,7 +577,7 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
addr = addr_buf;
af = sctp_get_af_specific(addr->v4.sin_family);
memcpy(&saveaddr, addr, af->sockaddr_len);
- retval = sctp_add_bind_addr(bp, &saveaddr,
+ retval = sctp_add_bind_addr(bp, &saveaddr, sizeof(saveaddr),
SCTP_ADDR_NEW, GFP_ATOMIC);
addr_buf += af->sockaddr_len;
}
--
2.5.0
[toc] | [prev] | [next] | [standalone]
| From | Dmitry Vyukov <dvyukov@google.com> |
|---|---|
| Date | 2016-01-26 14:30 +0100 |
| Subject | Re: [PATCH net] sctp: fix copying more bytes than expected in sctp_add_bind_addr |
| Message-ID | <qVbR1-4Hb-23@gated-at.bofh.it> |
| In reply to | #1317129 |
On Mon, Jan 25, 2016 at 6:52 PM, Marcelo Ricardo Leitner
<marcelo.leitner@gmail.com> wrote:
> Great. Dmitry, please give this a run. Local tests looked good but who
> knows what syzkaller may find.
Now running with this patch.
> Thanks
>
> --8<--
>
> Dmitry reported that sctp_add_bind_addr may read more bytes than
> expected in case the parameter is a IPv4 addr supplied by the user
> through calls such as sctp_bindx_add(), because it always copies
> sizeof(union sctp_addr) while the buffer may be just a struct
> sockaddr_in, which is smaller.
>
> This patch then fixes it by limiting the memcpy to the min between the
> union size and a (new parameter) provided addr size. Where possible this
> parameter still is the size of that union, except for reading from
> user-provided buffers, which then it accounts for protocol type.
>
> Reported-by: Dmitry Vyukov <dvyukov@google.com>
> Signed-off-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
> ---
> 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, &src->address_list, list) {
> - error = sctp_add_bind_addr(dest, &addr->a, 1, gfp);
> + error = sctp_add_bind_addr(dest, &addr->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(&addr->a, new, sizeof(*new));
> + memcpy(&addr->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(&addr, rawaddr, htons(port), 0);
> - retval = sctp_add_bind_addr(bp, &addr, SCTP_ADDR_SRC, gfp);
> + retval = sctp_add_bind_addr(bp, &addr, 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, &addr->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(&retval->base.bind_addr.address_list)) {
> sctp_add_bind_addr(&retval->base.bind_addr, &chunk->dest,
> - SCTP_ADDR_SRC, GFP_ATOMIC);
> + sizeof(chunk->dest), SCTP_ADDR_SRC, GFP_ATOMIC);
> }
>
> retval->next_tsn = retval->c.initial_tsn;
> diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> index 5ca2ebfe0be83882fcb841de6fa8029b6455ef85..213be3821a1d49e0c469c4ad4e9ff055a03205c5 100644
> --- a/net/sctp/socket.c
> +++ b/net/sctp/socket.c
> @@ -386,7 +386,8 @@ static int sctp_do_bind(struct sock *sk, union sctp_addr *addr, int len)
> /* Add the address to the bind address list.
> * Use GFP_ATOMIC since BHs will be disabled.
> */
> - ret = sctp_add_bind_addr(bp, addr, SCTP_ADDR_SRC, GFP_ATOMIC);
> + ret = sctp_add_bind_addr(bp, addr, af->sockaddr_len,
> + SCTP_ADDR_SRC, GFP_ATOMIC);
>
> /* Copy back into socket for getsockname() use. */
> if (!ret) {
> @@ -576,7 +577,7 @@ static int sctp_send_asconf_add_ip(struct sock *sk,
> addr = addr_buf;
> af = sctp_get_af_specific(addr->v4.sin_family);
> memcpy(&saveaddr, addr, af->sockaddr_len);
> - retval = sctp_add_bind_addr(bp, &saveaddr,
> + retval = sctp_add_bind_addr(bp, &saveaddr, sizeof(saveaddr),
> SCTP_ADDR_NEW, GFP_ATOMIC);
> addr_buf += af->sockaddr_len;
> }
> --
> 2.5.0
>
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web