Bug 199849

Summary: Invalid pointer dereference in io_ctl_map_page() when mounting and operating a crafted btrfs image
Product: File System Reporter: Wen Xu (wen.xu)
Component: btrfsAssignee: BTRFS virtual assignee (fs_btrfs)
Status: RESOLVED CODE_FIX    
Severity: normal CC: wen.xu
Priority: P1    
Hardware: All   
OS: Linux   
Kernel Version: 4.17 Subsystem:
Regression: No Bisected commit-id:
Attachments: The (compressed) crafted image which causes crash
poc.c

Description Wen Xu 2018-05-26 14:53:53 UTC
Created attachment 276215 [details]
The (compressed) crafted image which causes crash

- Overview
Invalid pointer dereference in io_ctl_map_page() when mounting and operating a crafted btrfs image

- Reproduce (4.17-rc5)
# mkdir mnt
# mount -t btrfs 6.img mnt
# gcc -o poc poc.c
# ./poc ./mnt

- Kernel message
[  190.329292] BUG: unable to handle kernel NULL pointer dereference at 0000000000000010
[  190.330849] PGD 80000001e7843067 P4D 80000001e7843067 PUD 1e8278067 PMD 0
[  190.332422] Oops: 0000 [#1] SMP KASAN PTI
[  190.333354] Modules linked in: snd_hda_codec_generic snd_hda_intel snd_hda_codec snd_hwdep snd_hda_core snd_pcm snd_timer snd mac_hid soundcore i2c_piix4 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 crct10dif_pclmul qxl crc32_pclmul drm_kms_helper aesni_intel syscopyarea sysfillrect sysimgblt fb_sys_fops ttm drm aes_x86_64 crypto_simd cryptd glue_helper pata_acpi 8139cp floppy mii
[  190.343124] CPU: 0 PID: 1308 Comm: btrfs-transacti Tainted: G    B   W         4.17.0-rc5+ #6
[  190.344786] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Ubuntu-1.8.2-1ubuntu1 04/01/2014
[  190.346638] RIP: 0010:io_ctl_map_page+0x40/0xc0
[  190.347530] RSP: 0018:ffff8801f3497a38 EFLAGS: 00010282
[  190.348558] RAX: 0000000000000000 RBX: ffff8801e60f29e8 RCX: 0000000000000000
[  190.349962] RDX: 0000000000000000 RSI: 0000000000000297 RDI: 0000000000000297
[  190.351342] RBP: ffff8801f3497a50 R08: ffffed003ee03ebb R09: ffffed003ee03ebb
[  190.352727] R10: 0000000000000001 R11: ffffed003ee03eba R12: 0000000000000010
[  190.354109] R13: 0000000000000001 R14: ffff8801cb60c348 R15: ffff8801df3c3b80
[  190.355495] FS:  0000000000000000(0000) GS:ffff8801f7000000(0000) knlGS:0000000000000000
[  190.357080] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[  190.358205] CR2: 0000000000000010 CR3: 00000001e0c84000 CR4: 00000000000006f0
[  190.359586] Call Trace:
[  190.360088]  __btrfs_write_out_cache+0x361/0x820
[  190.361024]  ? io_ctl_init+0x1c0/0x1c0
[  190.361765]  ? _atomic_dec_and_lock+0x1f/0x70
[  190.362622]  ? iput+0xc2/0x300
[  190.363234]  ? _raw_spin_lock+0x17/0x40
[  190.363993]  ? cache_save_setup+0x1fc/0x5c0
[  190.364836]  ? kasan_check_write+0x14/0x20
[  190.365648]  ? __iget+0x1e/0x30
[  190.366275]  ? igrab+0x3b/0x70
[  190.366888]  ? lookup_free_space_inode+0x49/0x170
[  190.367811]  btrfs_write_out_cache+0xc9/0x130
[  190.368682]  btrfs_start_dirty_block_groups+0x42e/0x7c0
[  190.369717]  ? btrfs_force_chunk_alloc+0x40/0x40
[  190.370633]  btrfs_commit_transaction+0x19f/0xfa0
[  190.371565]  ? kasan_check_write+0x14/0x20
[  190.372380]  ? join_transaction+0x645/0x6a0
[  190.373236]  ? btrfs_record_root_in_trans+0x24/0xb0
[  190.374202]  ? btrfs_apply_pending_changes+0xa0/0xa0
[  190.375185]  ? start_transaction+0x153/0x640
[  190.376039]  transaction_kthread+0x225/0x250
[  190.376907]  kthread+0x180/0x1d0
[  190.377561]  ? btrfs_cleanup_transaction+0xac0/0xac0
[  190.378548]  ? kthread_associate_blkcg+0x150/0x150
[  190.379501]  ret_from_fork+0x35/0x40
[  190.380218] Code: 41 89 f5 e8 43 cf ba ff 48 8d 7b 38 4c 8b 63 18 e8 36 ce ba ff 48 63 43 38 4d 8d 24 c4 8d 50 01 4c 89 e7 89 53 38 e8 20 cf ba ff <4d> 8b 24 24 48 8d 7b 10 e8 a3 cf ba ff 4c 89 63 10 48 c7 c7 f0
[  190.383954] RIP: io_ctl_map_page+0x40/0xc0 RSP: ffff8801f3497a38
[  190.385157] CR2: 0000000000000010
[  190.385821] ---[ end trace 1297a390ba7e0a67 ]---

- Reason
static void io_ctl_map_page(struct btrfs_io_ctl *io_ctl, int clear)
{
	ASSERT(io_ctl->index < io_ctl->num_pages);
	io_ctl->page = io_ctl->pages[io_ctl->index++];
	io_ctl->cur = page_address(io_ctl->page);
	io_ctl->orig = io_ctl->cur;
	io_ctl->size = PAGE_SIZE;
	if (clear)
		clear_page(io_ctl->cur);
}

io_ctl->pages is not properly initialized.

- Credit
Found by Wen Xu and Po-Ning Tseng from SSLab, Gatech.
Comment 1 Wen Xu 2018-05-26 14:54:09 UTC
Created attachment 276217 [details]
poc.c