All of lore.kernel.org
 help / color / mirror / Atom feed
* use-after-free in nfs4stat.c _free_cpntf_state_locked()
@ 2025-10-03 12:34 rtm
  2025-10-03 17:40 ` Chuck Lever
  0 siblings, 1 reply; 2+ messages in thread
From: rtm @ 2025-10-03 12:34 UTC (permalink / raw)
  To: Chuck Lever, Jeff Layton; +Cc: linux-nfs

If an NFS 4.2 client has a COPY_NOTIFY registered, and then
re-establishes its session with EXCHANGE_ID with a new verifier and
CREATE_SESSION, nfsd4_create_session() calls expire_client() for the
old session, which frees the nfs4_stid associated with the
COPY_NOTIFY. But the COPY_NOTIFY's nfs4_cpntf_state still exists; when
nfs4_laundromat() expires it, _free_cpntf_state_locked()'s
list_del(&cps->cp_list) uses the freed memory of the nfs4_stid.

A demo:

# uname -r
6.17.0-01737-g50c19e20ed2e-dirty
# cat /etc/exports
/tmp 127.0.0.1(rw,subtree_check,pnfs)
# wget http://www.rtmrtm.org/rtm/nfsd185b.c
# cc nfsd185b.c
# ./a.out
(wait 10 or 20 seconds for nfs4_laundromat())
(you may have to run a.out more than once)
list_del corruption. prev->next should be ffff8881068669d8, but was 6b6b6b6b6b6b6b6b. (prev=ffff888105190010)
kernel BUG at lib/list_debug.c:62!
Oops: invalid opcode: 0000 [#1] SMP DEBUG_PAGEALLOC PTI
CPU: 2 UID: 0 PID: 268 Comm: kworker/u51:2 Tainted: G        W           6.17.0-01737-g50c19e20ed2e-dirty #33 PREEMPT(voluntary) 
Workqueue: nfsd4 laundromat_main
RIP: 0010:__list_del_entry_valid_or_report+0xdd/0x110
Call Trace:
 _free_cpntf_state_locked+0x40/0xb0
 laundromat_main+0x5ec/0xaf0

The nfs4_cpntf_state is allocated and linked into the
nfs4_stid.sc_cp_list here:

#0  list_add (head=<optimized out>, new=<optimized out>)
    at fs/nfsd/nfs4state.c:985
#1  nfs4_alloc_init_cpntf_state (nn=nn@entry=0xffffffd602d82000, 
    p_stid=0xffffffd606858008) at fs/nfsd/nfs4state.c:985
#2  0xffffffff804b04e0 in nfsd4_copy_notify (rqstp=0xffffffd6040fb800, 
    cstate=<optimized out>, u=0xffffffd6052e2720) at fs/nfsd/nfs4proc.c:2078
(gdb) print cps
$1 = (struct nfs4_cpntf_state *) 0xffffffd603fe9020
(gdb) print p_stid
$2 = (struct nfs4_stid *) 0xffffffd606858008

The nfs4_stid is freed here:

#0  nfs4_free_ol_stateid (stid=0xffffffd606858008) at fs/nfsd/nfs4state.c:1502
#1  0xffffffff804c3ed2 in free_ol_stateid_reaplist (
    reaplist=reaplist@entry=0xffffffc60031baf8) at fs/nfsd/nfs4state.c:1602
#2  0xffffffff804c46fa in release_openowner (oo=0xffffffd606857008)
    at fs/nfsd/nfs4state.c:1696
#3  0xffffffff804c4898 in __destroy_client (clp=clp@entry=0xffffffd605308008)
    at fs/nfsd/nfs4state.c:2483
#4  0xffffffff804c49fa in expire_client (clp=0xffffffd605308008)
    at fs/nfsd/nfs4state.c:2533
#5  0xffffffff804c7a26 in nfsd4_create_session (rqstp=0xffffffd6040fb800, 
    cstate=<optimized out>, u=0xffffffd6052e2060) at fs/nfsd/nfs4state.c:4041
(gdb) print stid
$3 = (struct nfs4_stid *) 0xffffffd606858008
(gdb) print stid->sc_cp_list
$4 = {next = 0xffffffd603fe9038, prev = 0xffffffd603fe9038}

Freeing the nfs4_cpntf_state trips over the free nfs4_stid here:

#0  _free_cpntf_state_locked (nn=nn@entry=0xffffffd602d82000, 
    cps=0xffffffd603fe9020) at fs/nfsd/nfs4state.c:7226
#1  0xffffffff804c6210 in nfs4_laundromat (nn=0xffffffd602d82000)
    at fs/nfsd/nfs4state.c:6836
#2  laundromat_main (laundry=0xffffffd602d820d0) at fs/nfsd/nfs4state.c:6926
(gdb) print cps
$5 = (struct nfs4_cpntf_state *) 0xffffffd603fe9020
(gdb) print cps->cp_list
$6 = {next = 0xffffffd606858010, prev = 0xffffffd606858010}
(gdb) print cps->cp_list.next.prev
$7 = (struct list_head *) 0x6b6b6b6b6b6b6b6b

Robert Morris
rtm@mit.edu

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2025-10-03 17:40 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-03 12:34 use-after-free in nfs4stat.c _free_cpntf_state_locked() rtm
2025-10-03 17:40 ` Chuck Lever

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.