Bug 199869

Summary: use-after-free in ext4_journal_get_write_access() when mounting and operating a crafted ext4 image
Product: File System Reporter: Wen Xu (wen.xu)
Component: ext4Assignee: fs_ext4 (fs_ext4)
Status: RESOLVED DUPLICATE    
Severity: normal CC: tytso, wen.xu
Priority: P1    
Hardware: All   
OS: Linux   
Kernel Version: 4.17-rc5 Subsystem:
Regression: No Bisected commit-id:
Attachments: The (compressed) crafted image which causes crash
poc.c

Description Wen Xu 2018-05-28 23:21:50 UTC
Created attachment 276243 [details]
The (compressed) crafted image which causes crash

- Overview
use-after-free in ext4_journal_get_write_access() when mounting and operating a crafted ext4 image

- Reproduce
I tested with both ext4 dev branch and upstream kernel.

# mkdir mnt
# mount -t ext4 242.img mnt
# gcc -o poc poc.c
# ./poc ./mnt

- Kernel message (KASAN build)
[  539.561185] EXT4-fs (loop0): barriers disabled
[  539.567687] EXT4-fs (loop0): mounted filesystem with ordered data mode. Opts: (null)
[  550.378164] EXT4-fs error (device loop0): ext4_map_blocks:592: inode #14: block 1048576: comm poc: lblock 41 mapped to illegal pblock (length 1)
[  550.385965] EXT4-fs error (device loop0): ext4_clear_blocks:843: inode #14: comm poc: attempt to clear invalid blocks 1048576 len 1
[  550.424114] ==================================================================
[  550.425590] BUG: KASAN: use-after-free in do_get_write_access+0x119/0x6a0
[  550.426961] Read of size 8 at addr ffff8801e932cdc8 by task poc/1472

[  550.428567] CPU: 1 PID: 1472 Comm: poc Not tainted 4.17.0-rc5+ #6
[  550.428570] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[  550.428572] Call Trace:
[  550.428584]  dump_stack+0x7b/0xb5
[  550.428595]  print_address_description+0x70/0x290
[  550.428602]  kasan_report+0x291/0x390
[  550.428607]  ? do_get_write_access+0x119/0x6a0
[  550.428613]  __asan_load8+0x54/0x90
[  550.428619]  do_get_write_access+0x119/0x6a0
[  550.428626]  jbd2_journal_get_write_access+0x5f/0x80
[  550.428636]  __ext4_journal_get_write_access+0x3b/0x80
[  550.428646]  ext4_xattr_release_block+0x74/0x3f0
[  550.428654]  ext4_xattr_delete_inode+0x431/0x700
[  550.428662]  ? ext4_xattr_set_credits+0xf0/0xf0
[  550.428672]  ? ext4_punch_hole+0x680/0x680
[  550.428679]  ? __ext4_journal_start_sb+0x89/0x180
[  550.428685]  ext4_evict_inode+0x4ba/0x9b0
[  550.428692]  ? ext4_da_write_begin+0x610/0x610
[  550.428702]  evict+0x16f/0x290
[  550.428708]  iput+0x280/0x300
[  550.428716]  do_unlinkat+0x2c6/0x440
[  550.428723]  ? __ia32_sys_rmdir+0x30/0x30
[  550.428730]  ? kasan_unpoison_shadow+0x36/0x50
[  550.428736]  ? kasan_kmalloc+0xad/0xe0
[  550.428742]  ? kasan_check_write+0x14/0x20
[  550.428750]  ? strncpy_from_user+0xa8/0x1c0
[  550.428757]  ? getname_flags+0x110/0x2c0
[  550.428764]  __x64_sys_unlink+0x30/0x40
[  550.428771]  do_syscall_64+0x78/0x170
[  550.428777]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  550.428782] RIP: 0033:0x7f4490535a97
[  550.428785] RSP: 002b:00007ffc3e1ab708 EFLAGS: 00000286 ORIG_RAX: 0000000000000057
[  550.428791] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f4490535a97
[  550.428794] RDX: 0000000000000001 RSI: 0000000000000001 RDI: 00000000011de0a0
[  550.428798] RBP: 00007ffc3e1ab870 R08: 0000000000000003 R09: 0000000000000000
[  550.428801] R10: 00000000000001d4 R11: 0000000000000286 R12: 0000000000400c20
[  550.428804] R13: 00007ffc3e1ab970 R14: 0000000000000000 R15: 0000000000000000

[  550.429132] Allocated by task 1239:
[  550.429843]  save_stack+0x46/0xd0
[  550.429849]  kasan_kmalloc+0xad/0xe0
[  550.429854]  kasan_slab_alloc+0x11/0x20
[  550.429859]  kmem_cache_alloc+0xd1/0x1e0
[  550.429864]  getname_flags+0x76/0x2c0
[  550.429870]  user_path_at_empty+0x23/0x40
[  550.429877]  vfs_statx+0xce/0x160
[  550.429883]  __do_sys_newlstat+0x8c/0xe0
[  550.429889]  __x64_sys_newlstat+0x31/0x40
[  550.429894]  do_syscall_64+0x78/0x170
[  550.429899]  entry_SYSCALL_64_after_hwframe+0x44/0xa9

[  550.430221] Freed by task 1239:
[  550.430864]  save_stack+0x46/0xd0
[  550.430869]  __kasan_slab_free+0x13c/0x1a0
[  550.430875]  kasan_slab_free+0xe/0x10
[  550.430879]  kmem_cache_free+0x89/0x1e0
[  550.430884]  putname+0x80/0x90
[  550.430889]  filename_lookup+0x191/0x280
[  550.430894]  user_path_at_empty+0x36/0x40
[  550.430899]  vfs_statx+0xce/0x160
[  550.430905]  __do_sys_newlstat+0x8c/0xe0
[  550.430911]  __x64_sys_newlstat+0x31/0x40
[  550.430916]  do_syscall_64+0x78/0x170
[  550.430921]  entry_SYSCALL_64_after_hwframe+0x44/0xa9

[  550.431244] The buggy address belongs to the object at ffff8801e932c400
                which belongs to the cache names_cache of size 4096
[  550.433730] The buggy address is located 2504 bytes inside of
                4096-byte region [ffff8801e932c400, ffff8801e932d400)
[  550.436081] The buggy address belongs to the page:
[  550.437063] page:ffffea0007a4ca00 count:1 mapcount:0 mapping:0000000000000000 index:0x0 compound_mapcount: 0
[  550.439002] flags: 0x2ffff0000008100(slab|head)
[  550.439931] raw: 02ffff0000008100 0000000000000000 0000000000000000 0000000100070007
[  550.441459] raw: dead000000000100 dead000000000200 ffff8801f6da0380 0000000000000000
[  550.442989] page dumped because: kasan: bad access detected

[  550.444423] Memory state around the buggy address:
[  550.445376]  ffff8801e932cc80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[  550.446832]  ffff8801e932cd00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[  550.448278] >ffff8801e932cd80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[  550.449704]                                               ^
[  550.450813]  ffff8801e932ce00: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[  550.452250]  ffff8801e932ce80: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[  550.453675] ==================================================================
[  550.455102] Disabling lock debugging due to kernel taint
[  550.455584] BUG: unable to handle kernel paging request at 0000000074726f6a
[  550.457030] PGD 80000001f0a56067 P4D 80000001f0a56067 PUD 0
[  550.458162] Oops: 0000 [#1] SMP KASAN PTI
[  550.458968] Modules linked in: snd_hda_codec_generic snd_hda_intel snd_hda_codec snd_hwdep snd_hda_core snd_pcm snd_timer snd i2c_piix4 mac_hid soundcore ib_iser rdma_cm iw_cm ib_cm ib_core iscsi_tcp libiscsi_tcp libiscsi scsi_transport_iscsi autofs4 raid10 raid456 async_raid6_recov async_memcpy async_pq async_xor async_tx raid1 raid0 multipath linear 8139too qxl crct10dif_pclmul crc32_pclmul drm_kms_helper aesni_intel syscopyarea sysfillrect sysimgblt fb_sys_fops ttm aes_x86_64 crypto_simd drm cryptd glue_helper floppy 8139cp mii pata_acpi
[  550.469505] CPU: 1 PID: 1472 Comm: poc Tainted: G    B             4.17.0-rc5+ #6
[  550.470986] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[  550.472874] RIP: 0010:__find_get_block+0x192/0x400
[  550.473838] RSP: 0018:ffff8801dec47958 EFLAGS: 00010286
[  550.474883] RAX: 0000000000000000 RBX: 0000000000000000 RCX: ffffffffb94373c2
[  550.476310] RDX: 0000000000000000 RSI: 0000000000000008 RDI: 0000000074726f6a
[  550.477748] RBP: ffff8801dec479a8 R08: ffffed003bde1c30 R09: ffffed003bde1c30
[  550.479162] R10: 0000000000000001 R11: ffffed003bde1c2f R12: 00000000866efaf8
[  550.480589] R13: 0000000000000010 R14: 0000000074726f62 R15: ffff8801eb4b61f8
[  550.482005] FS:  00007f4490a22700(0000) GS:ffff8801f7100000(0000) knlGS:0000000000000000
[  550.483606] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  550.484771] CR2: 0000000074726f6a CR3: 00000001f0c14000 CR4: 00000000000006e0
[  550.486195] Call Trace:
[  550.486721]  ? kasan_check_write+0x14/0x20
[  550.487551]  ? _raw_spin_lock+0x17/0x40
[  550.488344]  jbd2_journal_cancel_revoke+0x12b/0x170
[  550.489326]  do_get_write_access+0x5ac/0x6a0
[  550.490188]  jbd2_journal_get_write_access+0x5f/0x80
[  550.491185]  __ext4_journal_get_write_access+0x3b/0x80
[  550.492232]  ext4_xattr_release_block+0x74/0x3f0
[  550.493164]  ext4_xattr_delete_inode+0x431/0x700
[  550.494095]  ? ext4_xattr_set_credits+0xf0/0xf0
[  550.495010]  ? ext4_punch_hole+0x680/0x680
[  550.495838]  ? __ext4_journal_start_sb+0x89/0x180
[  550.496813]  ext4_evict_inode+0x4ba/0x9b0
[  550.497629]  ? ext4_da_write_begin+0x610/0x610
[  550.498530]  evict+0x16f/0x290
[  550.499161]  iput+0x280/0x300
[  550.499774]  do_unlinkat+0x2c6/0x440
[  550.500520]  ? __ia32_sys_rmdir+0x30/0x30
[  550.501336]  ? kasan_unpoison_shadow+0x36/0x50
[  550.502238]  ? kasan_kmalloc+0xad/0xe0
[  550.503004]  ? kasan_check_write+0x14/0x20
[  550.503837]  ? strncpy_from_user+0xa8/0x1c0
[  550.504699]  ? getname_flags+0x110/0x2c0
[  550.505500]  __x64_sys_unlink+0x30/0x40
[  550.506282]  do_syscall_64+0x78/0x170
[  550.507051]  entry_SYSCALL_64_after_hwframe+0x44/0xa9
[  550.508078] RIP: 0033:0x7f4490535a97
[  550.508807] RSP: 002b:00007ffc3e1ab708 EFLAGS: 00000286 ORIG_RAX: 0000000000000057
[  550.510322] RAX: ffffffffffffffda RBX: 0000000000000000 RCX: 00007f4490535a97
[  550.511746] RDX: 0000000000000001 RSI: 0000000000000001 RDI: 00000000011de0a0
[  550.516314] RBP: 00007ffc3e1ab870 R08: 0000000000000003 R09: 0000000000000000
[  550.517759] R10: 00000000000001d4 R11: 0000000000000286 R12: 0000000000400c20
[  550.519177] R13: 00007ffc3e1ab970 R14: 0000000000000000 R15: 0000000000000000
[  550.520608] Code: 48 89 de e8 e1 bd fc 00 4c 89 e7 e8 c9 bd f5 ff 4d 8b 2c 24 4d 85 ed 75 d4 e9 6f ff ff ff 4c 8b 75 d0 49 8d 7e 08 e8 ae bd f5 ff <49> 8b 46 08 48 8d 78 30 49 89 c6 48 89 45 b8 e8 9a bd f5 ff 4c
[  550.524384] RIP: __find_get_block+0x192/0x400 RSP: ffff8801dec47958
[  550.525637] CR2: 0000000074726f6a
[  550.526392] ---[ end trace f568c098f25eca21 ]---

- Brief analysis

UAF location: https://elixir.bootlin.com/linux/v4.17-rc5/source/fs/jbd2/transaction.c#L869

	if (buffer_dirty(bh)) {

bh is already freed when executing this line.

Later in the same function, kernel executes jbd2_journal_cancel_revoke(). See https://elixir.bootlin.com/linux/v4.17-rc5/source/fs/jbd2/revoke.c#L459

		bh2 = __find_get_block(bh->b_bdev, bh->b_blocknr, bh->b_size);

bh is freed, which makes bh->b_bdev have invalid value leading to crash. 

- Credit
Found by Wen Xu from SSLab, Gatech.
Comment 1 Wen Xu 2018-05-28 23:22:07 UTC
Created attachment 276245 [details]
poc.c
Comment 2 Theodore Tso 2018-06-14 20:29:55 UTC
This is a dup of #200001.   With "ext4: always verify the magic number in xattr blocks" applied, I'm no longer seeing crashes and instead we get the following:

root@kvm-xfstests:~# /vdb/poc/do-869
[   82.775704] EXT4-fs (loop0): barriers disabled
[   82.778168] EXT4-fs (loop0): mounted filesystem with ordered data mode. Opts: (null)
[   82.786417] EXT4-fs error (device loop0): ext4_get_branch:171: inode #14: block 1048576: comm poc-199869: invalid block
[   82.790189] EXT4-fs error (device loop0): ext4_map_blocks:592: inode #14: block 1048576: comm poc-199869: lblock 41 mapped to illegal pblock (length 1)
[   82.795350] EXT4-fs error (device loop0): ext4_clear_blocks:849: inode #14: comm poc-199869: attempt to clear invalid blocks 1048576 len 1
[   82.811524] EXT4-fs error (device loop0): ext4_xattr_block_get:533: inode #14: comm poc-199869: corrupted xattr block 97
[   82.813991] EXT4-fs error (device loop0): ext4_xattr_block_get:533: inode #14: comm poc-199869: corrupted xattr block 97
[   82.817136] EXT4-fs error (device loop0): ext4_xattr_block_get:533: inode #14: comm poc-199869: corrupted xattr block 97
[   82.819888] EXT4-fs error (device loop0): ext4_xattr_block_get:533: inode #14: comm poc-199869: corrupted xattr block 97
[   82.822414] EXT4-fs error (device loop0): ext4_xattr_block_get:533: inode #14: comm poc-199869: corrupted xattr block 97
[   82.824712] EXT4-fs error (device loop0): ext4_xattr_block_get:533: inode #14: comm poc-199869: corrupted xattr block 97
[   82.826988] EXT4-fs error (device loop0): ext4_xattr_block_get:533: inode #14: comm poc-199869: corrupted xattr block 97
[   82.829463] EXT4-fs warning (device loop0): ext4_evict_inode:302: xattr delete (err -117)

*** This bug has been marked as a duplicate of bug 200001 ***