Hi all, I found an UAF in NFSD. - Root cause In nfsd4_setclientid_confirm(), it calls get_client_locked() without checking the return code. But if is_client_expired() returns true get_client_locked() do not Increment the "cl_rpc_users" and return "nfserr_expired". We can just decrement the "cl_rpc_users" as follow: nfsd4_setclientid_confirm get_client_locked(conf) if (is_client_expired(clp)) return nfserr_expired --> Return atomic_inc(&clp->cl_rpc_users) --> Failed to increment ... put_client_renew_locked(conf) if (!atomic_dec_and_test(&clp->cl_rpc_users)) --> Decrement - Case Here is a race case, which need to interact with /proc/fs, so the impact is limited: compound_request[renew, time_consuming_op, ...] Thread1 nfsd4_renew set_client cstate->clp = lookup_clientid() atomic_inc(cl_rpc_users) --> cl_rpc_users++ Handle time_consuming_op ... Thread2 echo "expire" > /proc/fs/nfsd/clients/xx/ctl client_ctl_write clp = get_nfsdfs_client(file_inode(file)) --> cl_ref++ force_expire_client spin_lock(&nn->client_lock) clp->cl_time = 0 --> Client expired spin_unlock(&nn->client_lock) wait_event(&clp->cl_rpc_users == 0) --> Wait here Thread2 nfsd4_setclientid_confirm conf = find_confirmed_client(clid, false, nn) ... get_client_locked(conf) --> Expired, do nothing ... put_client_renew_locked(conf) --> cl_rpc_users-- Thread2 echo "expire" > /proc/fs/nfsd/clients/xx/ctl client_ctl_write force_expire_client wait_event(&clp->cl_rpc_users == 0) --> Continue ... if (!already_expired) expire_client(clp) drop_client(clp) --> cl_ref-- __free_client --> Free client Thread1 nfs4_renew ... time_consuming_op ... nfs4svc_encode_compoundres nfsd4_sequence_done put_client_renew(cs->clp) --> UAF - KASAN report: root@syzkaller:~# ./poc Call cb_null_proc Call cb_null_proc [ 107.019508] ------------[ cut here ]------------ [ 107.020726] WARNING: CPU: 1 PID: 214 at fs/nfsd/nfs4state.c:196 find_client_in_id_table.isra.0+0x3cd/0x5e0 [ 107.022966] Modules linked in: [ 107.024142] CPU: 1 UID: 0 PID: 214 Comm: nfsd Not tainted 6.14.2 #13 [ 107.024877] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 [ 107.025860] RIP: 0010:find_client_in_id_table.isra.0+0x3cd/0x5e0 [ 107.026379] Code: df 48 89 ea 48 c1 ea 03 0f b6 04 02 84 c0 74 08 3c 03 0f 8e 33 01 00 00 41 c7 84 24 08 05 00 00 00 00 00 00 e9 48 fd ff ff 90 <0f> 0b 90 48 b8 00 00 00 06 [ 107.027613] RSP: 0018:ffff88800e647b18 EFLAGS: 00000246 [ 107.028458] RAX: dffffc0000000000 RBX: 000000000000000a RCX: 1ffffffff0cfaf78 [ 107.029431] RDX: 1ffff1100106f014 RSI: 00000000683fd4f9 RDI: ffff88800b74eec0 [ 107.030132] RBP: ffff888008378168 R08: ffff88800837816c R09: ffffed1001cc8f61 [ 107.030497] R10: 0000000000000003 R11: 0000000000032b9d R12: ffff888008378000 [ 107.030825] R13: ffff8880081b0000 R14: ffff8880083780a0 R15: 000000009881379c [ 107.031334] FS: 0000000000000000(0000) GS:ffff88806d300000(0000) knlGS:0000000000000000 [ 107.032249] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 107.032604] CR2: 00005636322587c8 CR3: 000000000d57c000 CR4: 00000000000006f0 [ 107.033437] Call Trace: [ 107.034226] <TASK> [ 107.037033] nfsd4_setclientid_confirm+0x305/0xc00 [ 107.038036] nfsd4_proc_compound+0xccc/0x2550 [ 107.038641] nfsd_dispatch+0x240/0x680 [ 107.039041] ? __pfx_nfsd_dispatch+0x10/0x10 [ 107.039476] ? __pfx_try_to_wake_up+0x10/0x10 [ 107.039810] ? svc_generic_init_request+0x2e7/0x530 [ 107.040598] svc_process+0x102c/0x2130 [ 107.041020] ? svc_xprt_received+0xa8/0x130 [ 107.041354] ? __pfx_svc_process+0x10/0x10 [ 107.041751] ? __pfx_nfsd_dispatch+0x10/0x10 [ 107.041979] ? __pfx_read_tsc+0x10/0x10 [ 107.042193] svc_recv+0x157a/0x1ed0 [ 107.042413] nfsd+0x247/0x360 [ 107.042598] ? __pfx_nfsd+0x10/0x10 [ 107.042770] kthread+0x319/0x620 [ 107.043150] ? __pfx_kthread+0x10/0x10 [ 107.043970] ? __pfx__raw_spin_lock_irq+0x10/0x10 [ 107.044573] ? finish_task_switch.isra.0+0x142/0x610 [ 107.045028] ? __pfx_kthread+0x10/0x10 [ 107.045627] ret_from_fork+0x2f/0x70 [ 107.046238] ? __pfx_kthread+0x10/0x10 [ 107.046599] ret_from_fork_asm+0x1a/0x30 [ 107.047342] </TASK> [ 107.047682] ---[ end trace 0000000000000000 ]--- [ 107.048415] renew_client_locked: client (clientid 683fd4f9/9881379c) already expired sh: 1: cannot create /proc/fs/nfsd/clients/3/ctl: Directory nonexistent [ 109.115204] ================================================================== [ 109.115977] BUG: KASAN: slab-use-after-free in put_client_renew+0x48a/0x5a0 [ 109.116491] Read of size 8 at addr ffff8880083784e8 by task nfsd/213 [ 109.116976] [ 109.117787] CPU: 0 UID: 0 PID: 213 Comm: nfsd Tainted: G W 6.14.2 #13 [ 109.117973] Tainted: [W]=WARN [ 109.118026] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.15.0-1 04/01/2014 [ 109.118112] Call Trace: [ 109.118206] <TASK> [ 109.118238] dump_stack_lvl+0x55/0x70 [ 109.118297] print_report+0xcb/0x630 [ 109.118337] ? put_client_renew+0x48a/0x5a0 [ 109.118365] kasan_report+0xaf/0xe0 [ 109.118396] ? put_client_renew+0x48a/0x5a0 [ 109.118426] put_client_renew+0x48a/0x5a0 [ 109.118461] nfs4svc_encode_compoundres+0x1c1/0x240 [ 109.118497] nfsd_dispatch+0x2e9/0x680 [ 109.118528] ? __pfx_nfsd_dispatch+0x10/0x10 [ 109.118560] ? __pfx_try_to_wake_up+0x10/0x10 [ 109.118598] ? svc_generic_init_request+0x2e7/0x530 [ 109.118640] svc_process+0x102c/0x2130 [ 109.118676] ? svc_xprt_received+0xa8/0x130 [ 109.118742] ? __pfx_svc_process+0x10/0x10 [ 109.118788] ? __pfx_nfsd_dispatch+0x10/0x10 [ 109.118825] ? __pfx_read_tsc+0x10/0x10 [ 109.118860] svc_recv+0x157a/0x1ed0 [ 109.118926] nfsd+0x247/0x360 [ 109.118953] ? __pfx_nfsd+0x10/0x10 [ 109.118981] kthread+0x319/0x620 [ 109.119015] ? __pfx_kthread+0x10/0x10 [ 109.119071] ? __pfx__raw_spin_lock_irq+0x10/0x10 [ 109.119098] ? finish_task_switch.isra.0+0x142/0x610 [ 109.119177] ? __pfx_kthread+0x10/0x10 [ 109.119210] ret_from_fork+0x2f/0x70 [ 109.119248] ? __pfx_kthread+0x10/0x10 [ 109.119278] ret_from_fork_asm+0x1a/0x30 [ 109.119322] </TASK> [ 109.119441] [ 109.125204] Allocated by task 213: [ 109.125615] kasan_save_stack+0x24/0x50 [ 109.126008] kasan_save_track+0x14/0x30 [ 109.126259] __kasan_slab_alloc+0x59/0x70 [ 109.126544] kmem_cache_alloc_noprof+0xf5/0x340 [ 109.126848] create_client+0x258/0x1320 [ 109.127075] nfsd4_setclientid+0x1cd/0x1380 [ 109.127354] nfsd4_proc_compound+0xccc/0x2550 [ 109.127610] nfsd_dispatch+0x240/0x680 [ 109.127845] svc_process+0x102c/0x2130 [ 109.128061] svc_recv+0x157a/0x1ed0 [ 109.128274] nfsd+0x247/0x360 [ 109.128513] kthread+0x319/0x620 [ 109.128692] ret_from_fork+0x2f/0x70 [ 109.128883] ret_from_fork_asm+0x1a/0x30 [ 109.129228] [ 109.129375] Freed by task 252: [ 109.129538] kasan_save_stack+0x24/0x50 [ 109.129770] kasan_save_track+0x14/0x30 [ 109.130064] kasan_save_free_info+0x3b/0x60 [ 109.130428] __kasan_slab_free+0x38/0x50 [ 109.130665] kmem_cache_free+0x163/0x3b0 [ 109.130847] client_ctl_write+0x3ce/0x5a0 [ 109.131074] vfs_write+0x21d/0xcc0 [ 109.131291] ksys_write+0xef/0x1c0 [ 109.131521] do_syscall_64+0xa6/0x1a0 [ 109.131744] entry_SYSCALL_64_after_hwframe+0x77/0x7f [ 109.131967] [ 109.132241] Last potentially related work creation: [ 109.132604] kasan_save_stack+0x24/0x50 [ 109.132847] kasan_record_aux_stack+0x8c/0xa0 [ 109.133038] __queue_work+0x5cc/0xe30 [ 109.133295] queue_work_on+0x59/0x60 [ 109.133502] nfsd4_run_cb+0x45/0x90 [ 109.133683] nfsd4_setclientid_confirm+0x432/0xc00 [ 109.133956] nfsd4_proc_compound+0xccc/0x2550 [ 109.134203] nfsd_dispatch+0x240/0x680 [ 109.134535] svc_process+0x102c/0x2130 [ 109.134793] svc_recv+0x157a/0x1ed0 [ 109.135043] nfsd+0x247/0x360 [ 109.135300] kthread+0x319/0x620 [ 109.135610] ret_from_fork+0x2f/0x70 [ 109.135941] ret_from_fork_asm+0x1a/0x30 [ 109.136317] [ 109.136501] Second to last potentially related work creation: [ 109.136877] kasan_save_stack+0x24/0x50 [ 109.137224] kasan_record_aux_stack+0x8c/0xa0 [ 109.137564] __queue_work+0x5cc/0xe30 [ 109.137851] queue_work_on+0x59/0x60 [ 109.138125] nfsd4_run_cb+0x45/0x90 [ 109.138392] nfsd4_setclientid_confirm+0x8cb/0xc00 [ 109.138643] nfsd4_proc_compound+0xccc/0x2550 [ 109.138852] nfsd_dispatch+0x240/0x680 [ 109.139101] svc_process+0x102c/0x2130 [ 109.139320] svc_recv+0x157a/0x1ed0 [ 109.139453] nfsd+0x247/0x360 [ 109.139617] kthread+0x319/0x620 [ 109.139851] ret_from_fork+0x2f/0x70 [ 109.139999] ret_from_fork_asm+0x1a/0x30 [ 109.140291] [ 109.140411] The buggy address belongs to the object at ffff888008378000 [ 109.140411] which belongs to the cache nfs4_client of size 1328 [ 109.141009] The buggy address is located 1256 bytes inside of [ 109.141009] freed 1328-byte region [ffff888008378000, ffff888008378530) [ 109.141662] [ 109.141894] The buggy address belongs to the physical page: [ 109.142498] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x8378 [ 109.143447] head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0 [ 109.143874] flags: 0x100000000000040(head|node=0|zone=1) [ 109.145116] page_type: f5(slab) [ 109.146022] raw: 0100000000000040 ffff888007747c80 dead000000000122 0000000000000000 [ 109.146509] raw: 0000000000000000 0000000080160016 00000000f5000000 0000000000000000 [ 109.146886] head: 0100000000000040 ffff888007747c80 dead000000000122 0000000000000000 [ 109.147217] head: 0000000000000000 0000000080160016 00000000f5000000 0000000000000000 [ 109.147563] head: 0100000000000003 ffffea000020de01 ffffffffffffffff 0000000000000000 [ 109.147998] head: ffff888000000008 0000000000000000 00000000ffffffff 0000000000000000 [ 109.148445] page dumped because: kasan: bad access detected [ 109.148734] [ 109.148848] Memory state around the buggy address: [ 109.149493] ffff888008378380: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 109.149925] ffff888008378400: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 109.150298] >ffff888008378480: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb [ 109.150675] ^ [ 109.151224] ffff888008378500: fb fb fb fb fb fb fc fc fc fc fc fc fc fc fc fc [ 109.151543] ffff888008378580: fc fc fc fc fc fc fa fb fb fb fb fb fb fb fb fb [ 109.151915] ================================================================== [ 109.152592] Disabling lock debugging due to kernel taint rpc_service() failed for server