* 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
* Re: use-after-free in nfs4stat.c _free_cpntf_state_locked()
2025-10-03 12:34 use-after-free in nfs4stat.c _free_cpntf_state_locked() rtm
@ 2025-10-03 17:40 ` Chuck Lever
0 siblings, 0 replies; 2+ messages in thread
From: Chuck Lever @ 2025-10-03 17:40 UTC (permalink / raw)
To: Olga Kornievskaia; +Cc: linux-nfs, Jeff Layton, rtm
On 10/3/25 8:34 AM, rtm@csail.mit.edu wrote:
> 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
Olga, do you have a few moments to triage this?
--
Chuck Lever
^ 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.