Message ID | 20190606074925.12375-1-naohiro.aota@wdc.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | btrfs: fix out-of-bounds access in property handling | expand |
On 6.06.19 г. 10:49 ч., Naohiro Aota wrote: > xattr value is not NULL-terminated string. When you specify "lz" as the > property value, strncmp("lzo", value, 3) will try to read one byte after > the value buffer, causing the following OOB access. Fix this out-of-bound > by explicitly check the required length. > > [ 1519.998589] ================================================================== > [ 1520.007505] BUG: KASAN: slab-out-of-bounds in strncmp+0xab/0xc0 > [ 1520.015116] Read of size 1 at addr ffff8886daec16c2 by task btrfs/15317 > > [ 1520.026628] CPU: 4 PID: 15317 Comm: btrfs Not tainted 5.1.0-rc7+ #3 > [ 1520.034635] Hardware name: Supermicro X10SLL-F/X10SLL-F, BIOS 3.0 04/24/2015 > [ 1520.043416] Call Trace: > [ 1520.047562] dump_stack+0x71/0xa0 > [ 1520.052584] print_address_description+0x65/0x229 > [ 1520.058997] ? strncmp+0xab/0xc0 > [ 1520.063929] ? strncmp+0xab/0xc0 > [ 1520.068834] kasan_report.cold+0x1a/0x38 > [ 1520.074439] ? strncmp+0xab/0xc0 > [ 1520.079343] strncmp+0xab/0xc0 > [ 1520.084110] prop_compression_validate+0x22/0x70 [btrfs] > [ 1520.091135] btrfs_xattr_handler_set_prop+0x6c/0x1f0 [btrfs] > [ 1520.098452] __vfs_setxattr+0xd8/0x130 > [ 1520.103821] ? xattr_resolve_name+0x3e0/0x3e0 > [ 1520.109812] __vfs_setxattr_noperm+0xeb/0x3b0 > [ 1520.115790] vfs_setxattr+0xa3/0xd0 > [ 1520.120898] setxattr+0x17a/0x2c0 > [ 1520.125824] ? vfs_setxattr+0xd0/0xd0 > [ 1520.131102] ? __pmd_alloc+0x560/0x560 > [ 1520.136452] ? __do_sys_newfstat+0xd3/0xe0 > [ 1520.142123] ? __ia32_sys_newfstatat+0xf0/0xf0 > [ 1520.148140] ? __kasan_slab_free+0x141/0x170 > [ 1520.153955] ? handle_mm_fault+0x1ae/0x640 > [ 1520.159564] __x64_sys_fsetxattr+0x1a0/0x220 > [ 1520.165347] do_syscall_64+0xa0/0x2e0 > [ 1520.170515] ? page_fault+0x8/0x30 > [ 1520.175408] entry_SYSCALL_64_after_hwframe+0x44/0xa9 > [ 1520.181975] RIP: 0033:0x7f84f257ae6e > [ 1520.187068] Code: 48 8b 0d 1d 70 0c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 49 89 ca b8 be 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d ea 6f 0c 00 f7 d8 64 89 01 48 > [ 1520.209083] RSP: 002b:00007fff87996fa8 EFLAGS: 00000246 ORIG_RAX: 00000000000000be > [ 1520.218336] RAX: ffffffffffffffda RBX: 000000000000000b RCX: 00007f84f257ae6e > [ 1520.227132] RDX: 00007fff8799798b RSI: 00005561caf83270 RDI: 0000000000000003 > [ 1520.235912] RBP: 00007fff8799798b R08: 0000000000000000 R09: 00005561caf83290 > [ 1520.244691] R10: 0000000000000002 R11: 0000000000000246 R12: 00005561caf83270 > [ 1520.253462] R13: 0000000000000003 R14: 00007fff87997972 R15: 00007fff8799797f > > [ 1520.265443] Allocated by task 15317: > [ 1520.270697] __kasan_kmalloc.constprop.0+0xc2/0xd0 > [ 1520.277677] setxattr+0xe8/0x2c0 > [ 1520.283084] __x64_sys_fsetxattr+0x1a0/0x220 > [ 1520.289540] do_syscall_64+0xa0/0x2e0 > [ 1520.295379] entry_SYSCALL_64_after_hwframe+0x44/0xa9 > > [ 1520.306288] Freed by task 12227: > [ 1520.311632] __kasan_slab_free+0x12c/0x170 > [ 1520.317828] kfree+0x90/0x1e0 > [ 1520.322891] btrfs_free_block_groups+0x8a1/0xd80 [btrfs] > [ 1520.330313] close_ctree+0x37e/0x650 [btrfs] > [ 1520.336627] generic_shutdown_super+0x12e/0x320 > [ 1520.343177] kill_anon_super+0x36/0x60 > [ 1520.348983] btrfs_kill_super+0x3d/0x2c0 [btrfs] > [ 1520.355636] deactivate_locked_super+0x85/0xd0 > [ 1520.362108] deactivate_super+0x122/0x140 > [ 1520.368166] cleanup_mnt+0x9f/0x130 > [ 1520.373699] task_work_run+0x131/0x1c0 > [ 1520.379490] exit_to_usermode_loop+0x133/0x160 > [ 1520.386002] do_syscall_64+0x259/0x2e0 > [ 1520.391796] entry_SYSCALL_64_after_hwframe+0x44/0xa9 > > [ 1520.402415] The buggy address belongs to the object at ffff8886daec16c0 > which belongs to the cache kmalloc-8 of size 8 > [ 1520.418769] The buggy address is located 2 bytes inside of > 8-byte region [ffff8886daec16c0, ffff8886daec16c8) > [ 1520.434407] The buggy address belongs to the page: > [ 1520.441358] page:ffffea001b6bb040 count:1 mapcount:0 mapping:ffff8886ff40fb80 index:0x0 > [ 1520.451601] flags: 0x17ffe000000200(slab) > [ 1520.457868] raw: 0017ffe000000200 ffffea001bedcec0 0000000700000007 ffff8886ff40fb80 > [ 1520.467940] raw: 0000000000000000 0000000080aa00aa 00000001ffffffff 0000000000000000 > [ 1520.478024] page dumped because: kasan: bad access detected > > [ 1520.489795] Memory state around the buggy address: > [ 1520.496963] ffff8886daec1580: fc 00 fc fc 00 fc fc 00 fc fc 00 fc fc 00 fc fc > [ 1520.506621] ffff8886daec1600: 00 fc fc 04 fc fc fb fc fc fb fc fc fb fc fc fb > [ 1520.516277] >ffff8886daec1680: fc fc 04 fc fc 00 fc fc 02 fc fc fb fc fc 00 fc > [ 1520.525915] ^ > [ 1520.533584] ffff8886daec1700: fc 00 fc fc 00 fc fc 00 fc fc 00 fc fc 04 fc fc > [ 1520.543137] ffff8886daec1780: fb fc fc 00 fc fc 00 fc fc 00 fc fc 00 fc fc 00 > [ 1520.552642] ================================================================== > > Fixes: 272e5326c783 ("btrfs: prop: fix vanished compression property after failed set") > Fixes: 50398fde997f ("btrfs: prop: fix zstd compression parameter validation") > Cc: stable@vger.kernel.org # 4.14+: 802a5c69584a: btrfs: prop: use common helper for type to string conversion > Cc: stable@vger.kernel.org # 4.14+: 3dcf96c7b9fe: btrfs: drop redundant forward declaration in props.c > Cc: stable@vger.kernel.org # 4.14+ > Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com> We caught that one yesterday and were testing various fixes for it Johannes just sent his version which IMO makes the code a bit more maintainable. > --- > fs/btrfs/props.c | 12 ++++++------ > 1 file changed, 6 insertions(+), 6 deletions(-) > > diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c > index a9e2e66152ee..428141bf545d 100644 > --- a/fs/btrfs/props.c > +++ b/fs/btrfs/props.c > @@ -257,11 +257,11 @@ static int prop_compression_validate(const char *value, size_t len) > if (!value) > return 0; > > - if (!strncmp("lzo", value, 3)) > + if (len >= 3 && !strncmp("lzo", value, 3)) > return 0; > - else if (!strncmp("zlib", value, 4)) > + else if (len >= 4 && !strncmp("zlib", value, 4)) > return 0; > - else if (!strncmp("zstd", value, 4)) > + else if (len >= 4 && !strncmp("zstd", value, 4)) > return 0; > > return -EINVAL; > @@ -281,12 +281,12 @@ static int prop_compression_apply(struct inode *inode, const char *value, > return 0; > } > > - if (!strncmp("lzo", value, 3)) { > + if (len >= 3 && !strncmp("lzo", value, 3)) { > type = BTRFS_COMPRESS_LZO; > btrfs_set_fs_incompat(fs_info, COMPRESS_LZO); > - } else if (!strncmp("zlib", value, 4)) { > + } else if (len >= 4 && !strncmp("zlib", value, 4)) { > type = BTRFS_COMPRESS_ZLIB; > - } else if (!strncmp("zstd", value, 4)) { > + } else if (len >= 4 && !strncmp("zstd", value, 4)) { > type = BTRFS_COMPRESS_ZSTD; > btrfs_set_fs_incompat(fs_info, COMPRESS_ZSTD); > } else { This seems redundant as ->validates is supposed to always be called before calling ->apply. >
On 2019/06/06 17:05, Nikolay Borisov wrote: > > > On 6.06.19 г. 10:49 ч., Naohiro Aota wrote: >> xattr value is not NULL-terminated string. When you specify "lz" as the >> property value, strncmp("lzo", value, 3) will try to read one byte after >> the value buffer, causing the following OOB access. Fix this out-of-bound >> by explicitly check the required length. >> >>(snip) >> >> Fixes: 272e5326c783 ("btrfs: prop: fix vanished compression property after failed set") >> Fixes: 50398fde997f ("btrfs: prop: fix zstd compression parameter validation") >> Cc: stable@vger.kernel.org # 4.14+: 802a5c69584a: btrfs: prop: use common helper for type to string conversion >> Cc: stable@vger.kernel.org # 4.14+: 3dcf96c7b9fe: btrfs: drop redundant forward declaration in props.c >> Cc: stable@vger.kernel.org # 4.14+ >> Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com> > > We caught that one yesterday and were testing various fixes for it > Johannes just sent his version which IMO makes the code a bit more > maintainable. > yeah, that looks good to me. It's much more easy to add new compression type. Please pick that one. > >> --- >> fs/btrfs/props.c | 12 ++++++------ >> 1 file changed, 6 insertions(+), 6 deletions(-) >> >> diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c >> index a9e2e66152ee..428141bf545d 100644 >> --- a/fs/btrfs/props.c >> +++ b/fs/btrfs/props.c >> @@ -257,11 +257,11 @@ static int prop_compression_validate(const char *value, size_t len) >> if (!value) >> return 0; >> >> - if (!strncmp("lzo", value, 3)) >> + if (len >= 3 && !strncmp("lzo", value, 3)) >> return 0; >> - else if (!strncmp("zlib", value, 4)) >> + else if (len >= 4 && !strncmp("zlib", value, 4)) >> return 0; >> - else if (!strncmp("zstd", value, 4)) >> + else if (len >= 4 && !strncmp("zstd", value, 4)) >> return 0; >> >> return -EINVAL; >> @@ -281,12 +281,12 @@ static int prop_compression_apply(struct inode *inode, const char *value, >> return 0; >> } >> >> - if (!strncmp("lzo", value, 3)) { >> + if (len >= 3 && !strncmp("lzo", value, 3)) { >> type = BTRFS_COMPRESS_LZO; >> btrfs_set_fs_incompat(fs_info, COMPRESS_LZO); >> - } else if (!strncmp("zlib", value, 4)) { >> + } else if (len >= 4 && !strncmp("zlib", value, 4)) { >> type = BTRFS_COMPRESS_ZLIB; >> - } else if (!strncmp("zstd", value, 4)) { >> + } else if (len >= 4 && !strncmp("zstd", value, 4)) { >> type = BTRFS_COMPRESS_ZSTD; >> btrfs_set_fs_incompat(fs_info, COMPRESS_ZSTD); >> } else { > > This seems redundant as ->validates is supposed to always be called > before calling ->apply. > Indeed.
diff --git a/fs/btrfs/props.c b/fs/btrfs/props.c index a9e2e66152ee..428141bf545d 100644 --- a/fs/btrfs/props.c +++ b/fs/btrfs/props.c @@ -257,11 +257,11 @@ static int prop_compression_validate(const char *value, size_t len) if (!value) return 0; - if (!strncmp("lzo", value, 3)) + if (len >= 3 && !strncmp("lzo", value, 3)) return 0; - else if (!strncmp("zlib", value, 4)) + else if (len >= 4 && !strncmp("zlib", value, 4)) return 0; - else if (!strncmp("zstd", value, 4)) + else if (len >= 4 && !strncmp("zstd", value, 4)) return 0; return -EINVAL; @@ -281,12 +281,12 @@ static int prop_compression_apply(struct inode *inode, const char *value, return 0; } - if (!strncmp("lzo", value, 3)) { + if (len >= 3 && !strncmp("lzo", value, 3)) { type = BTRFS_COMPRESS_LZO; btrfs_set_fs_incompat(fs_info, COMPRESS_LZO); - } else if (!strncmp("zlib", value, 4)) { + } else if (len >= 4 && !strncmp("zlib", value, 4)) { type = BTRFS_COMPRESS_ZLIB; - } else if (!strncmp("zstd", value, 4)) { + } else if (len >= 4 && !strncmp("zstd", value, 4)) { type = BTRFS_COMPRESS_ZSTD; btrfs_set_fs_incompat(fs_info, COMPRESS_ZSTD); } else {
xattr value is not NULL-terminated string. When you specify "lz" as the property value, strncmp("lzo", value, 3) will try to read one byte after the value buffer, causing the following OOB access. Fix this out-of-bound by explicitly check the required length. [ 1519.998589] ================================================================== [ 1520.007505] BUG: KASAN: slab-out-of-bounds in strncmp+0xab/0xc0 [ 1520.015116] Read of size 1 at addr ffff8886daec16c2 by task btrfs/15317 [ 1520.026628] CPU: 4 PID: 15317 Comm: btrfs Not tainted 5.1.0-rc7+ #3 [ 1520.034635] Hardware name: Supermicro X10SLL-F/X10SLL-F, BIOS 3.0 04/24/2015 [ 1520.043416] Call Trace: [ 1520.047562] dump_stack+0x71/0xa0 [ 1520.052584] print_address_description+0x65/0x229 [ 1520.058997] ? strncmp+0xab/0xc0 [ 1520.063929] ? strncmp+0xab/0xc0 [ 1520.068834] kasan_report.cold+0x1a/0x38 [ 1520.074439] ? strncmp+0xab/0xc0 [ 1520.079343] strncmp+0xab/0xc0 [ 1520.084110] prop_compression_validate+0x22/0x70 [btrfs] [ 1520.091135] btrfs_xattr_handler_set_prop+0x6c/0x1f0 [btrfs] [ 1520.098452] __vfs_setxattr+0xd8/0x130 [ 1520.103821] ? xattr_resolve_name+0x3e0/0x3e0 [ 1520.109812] __vfs_setxattr_noperm+0xeb/0x3b0 [ 1520.115790] vfs_setxattr+0xa3/0xd0 [ 1520.120898] setxattr+0x17a/0x2c0 [ 1520.125824] ? vfs_setxattr+0xd0/0xd0 [ 1520.131102] ? __pmd_alloc+0x560/0x560 [ 1520.136452] ? __do_sys_newfstat+0xd3/0xe0 [ 1520.142123] ? __ia32_sys_newfstatat+0xf0/0xf0 [ 1520.148140] ? __kasan_slab_free+0x141/0x170 [ 1520.153955] ? handle_mm_fault+0x1ae/0x640 [ 1520.159564] __x64_sys_fsetxattr+0x1a0/0x220 [ 1520.165347] do_syscall_64+0xa0/0x2e0 [ 1520.170515] ? page_fault+0x8/0x30 [ 1520.175408] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 1520.181975] RIP: 0033:0x7f84f257ae6e [ 1520.187068] Code: 48 8b 0d 1d 70 0c 00 f7 d8 64 89 01 48 83 c8 ff c3 66 2e 0f 1f 84 00 00 00 00 00 90 f3 0f 1e fa 49 89 ca b8 be 00 00 00 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 8b 0d ea 6f 0c 00 f7 d8 64 89 01 48 [ 1520.209083] RSP: 002b:00007fff87996fa8 EFLAGS: 00000246 ORIG_RAX: 00000000000000be [ 1520.218336] RAX: ffffffffffffffda RBX: 000000000000000b RCX: 00007f84f257ae6e [ 1520.227132] RDX: 00007fff8799798b RSI: 00005561caf83270 RDI: 0000000000000003 [ 1520.235912] RBP: 00007fff8799798b R08: 0000000000000000 R09: 00005561caf83290 [ 1520.244691] R10: 0000000000000002 R11: 0000000000000246 R12: 00005561caf83270 [ 1520.253462] R13: 0000000000000003 R14: 00007fff87997972 R15: 00007fff8799797f [ 1520.265443] Allocated by task 15317: [ 1520.270697] __kasan_kmalloc.constprop.0+0xc2/0xd0 [ 1520.277677] setxattr+0xe8/0x2c0 [ 1520.283084] __x64_sys_fsetxattr+0x1a0/0x220 [ 1520.289540] do_syscall_64+0xa0/0x2e0 [ 1520.295379] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 1520.306288] Freed by task 12227: [ 1520.311632] __kasan_slab_free+0x12c/0x170 [ 1520.317828] kfree+0x90/0x1e0 [ 1520.322891] btrfs_free_block_groups+0x8a1/0xd80 [btrfs] [ 1520.330313] close_ctree+0x37e/0x650 [btrfs] [ 1520.336627] generic_shutdown_super+0x12e/0x320 [ 1520.343177] kill_anon_super+0x36/0x60 [ 1520.348983] btrfs_kill_super+0x3d/0x2c0 [btrfs] [ 1520.355636] deactivate_locked_super+0x85/0xd0 [ 1520.362108] deactivate_super+0x122/0x140 [ 1520.368166] cleanup_mnt+0x9f/0x130 [ 1520.373699] task_work_run+0x131/0x1c0 [ 1520.379490] exit_to_usermode_loop+0x133/0x160 [ 1520.386002] do_syscall_64+0x259/0x2e0 [ 1520.391796] entry_SYSCALL_64_after_hwframe+0x44/0xa9 [ 1520.402415] The buggy address belongs to the object at ffff8886daec16c0 which belongs to the cache kmalloc-8 of size 8 [ 1520.418769] The buggy address is located 2 bytes inside of 8-byte region [ffff8886daec16c0, ffff8886daec16c8) [ 1520.434407] The buggy address belongs to the page: [ 1520.441358] page:ffffea001b6bb040 count:1 mapcount:0 mapping:ffff8886ff40fb80 index:0x0 [ 1520.451601] flags: 0x17ffe000000200(slab) [ 1520.457868] raw: 0017ffe000000200 ffffea001bedcec0 0000000700000007 ffff8886ff40fb80 [ 1520.467940] raw: 0000000000000000 0000000080aa00aa 00000001ffffffff 0000000000000000 [ 1520.478024] page dumped because: kasan: bad access detected [ 1520.489795] Memory state around the buggy address: [ 1520.496963] ffff8886daec1580: fc 00 fc fc 00 fc fc 00 fc fc 00 fc fc 00 fc fc [ 1520.506621] ffff8886daec1600: 00 fc fc 04 fc fc fb fc fc fb fc fc fb fc fc fb [ 1520.516277] >ffff8886daec1680: fc fc 04 fc fc 00 fc fc 02 fc fc fb fc fc 00 fc [ 1520.525915] ^ [ 1520.533584] ffff8886daec1700: fc 00 fc fc 00 fc fc 00 fc fc 00 fc fc 04 fc fc [ 1520.543137] ffff8886daec1780: fb fc fc 00 fc fc 00 fc fc 00 fc fc 00 fc fc 00 [ 1520.552642] ================================================================== Fixes: 272e5326c783 ("btrfs: prop: fix vanished compression property after failed set") Fixes: 50398fde997f ("btrfs: prop: fix zstd compression parameter validation") Cc: stable@vger.kernel.org # 4.14+: 802a5c69584a: btrfs: prop: use common helper for type to string conversion Cc: stable@vger.kernel.org # 4.14+: 3dcf96c7b9fe: btrfs: drop redundant forward declaration in props.c Cc: stable@vger.kernel.org # 4.14+ Signed-off-by: Naohiro Aota <naohiro.aota@wdc.com> --- fs/btrfs/props.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-)