* buffer overrun in nfsd4_store_cache_entry()
@ 2025-10-04 13:53 rtm
2025-10-04 16:39 ` Chuck Lever
0 siblings, 1 reply; 2+ messages in thread
From: rtm @ 2025-10-04 13:53 UTC (permalink / raw)
To: Chuck Lever, Jeff Layton; +Cc: linux-nfs
A client can cause nfsd4_store_cache_entry() to write beyond the end
of slot->sl_data[] here:
base = resp->cstate.data_offset;
slot->sl_datalen = buf->len - base;
if (read_bytes_from_xdr_buf(buf, base, slot->sl_data, slot->sl_datalen))
The demo below does this by sending a CREATE_SESSION with
maxresp_sz = 64
maxresp_cached = 80
The result is that the next request that includes a SEQUENCE causes
nfsd4_encode_operation() to decide that the response will be too
large, and to return just an error response to the SEQUENCE. Then
nfsd4_store_cache_entry() decides to cache the result, because
nfsd4_cache_this() (really nfsd4_is_solo_sequence()) returns true. But
the maxresp_cached=80 caused nfsd4_alloc_slot() to allocate slots with
"size" of zero, so that slot->sl_data[] has no space. But
nfsd4_store_cache_entry() is not aware of the true size of sl_data[].
On my setup the problem is detected by the slub redzone debug mechanism
when I shut down nfsd, with rpc.nfsd 0.
# cat /etc/exports
/tmp 127.0.0.1(rw,subtree_check,pnfs)
# wget http://www.rtmrtm.org/rtm/nfsd185f.c
# cc nfsd185f.c
# ./a.out
...
[Right Redzone overwritten] 0xffff888103f65200-0xffff888103f65207 @offset=4608. First byte 0x0 instead of 0xcc
=============================================================================
BUG kmalloc-96 (Tainted: G W ): Object corrupt
-----------------------------------------------------------------------------
Allocated in 0x2700003500000001 age=12934 cpu=2 pid=1118
BUG: unable to handle page fault for address: ffff888105768000
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
PGD 3801067 P4D 3801067 PUD 43f902067 PMD 43f8d6067 PTE 800ffffefa897020
Oops: Oops: 0000 [#1] SMP DEBUG_PAGEALLOC PTI
CPU: 11 UID: 0 PID: 1325 Comm: rpc.nfsd Tainted: G W 6.17.0-09936-gcbf33b8e0b36-dirty #34 PREEMPT(voluntary)
Tainted: [W]=WARN
Hardware name: FreeBSD BHYVE/BHYVE, BIOS 14.0 10/17/2021
RIP: 0010:stack_trace_print+0x23/0x60
Call Trace:
<TASK>
stack_depot_print+0x48/0x50
print_track+0x42/0x70
print_tracking+0x3e/0x70
object_err+0x9a/0x2d0
check_bytes_and_report+0x11a/0x140
check_object+0x1b7/0x340
free_to_partial_list+0x1c6/0x570
? free_session_slots+0x8a/0x140
? free_session_slots+0x8a/0x140
__slab_free+0x21e/0x3c0
? __pfx_schedule_timeout+0x10/0x10
? free_session_slots+0x8a/0x140
kfree+0x2b1/0x350
free_session_slots+0x8a/0x140
free_client+0x86/0x150
__destroy_client+0x246/0x290
nfs4_state_shutdown_net+0x1da/0x3c0
nfsd_destroy_serv+0x11a/0x1b0
nfsd_svc+0x1da/0x300
write_threads+0xec/0x1b0
? __pfx_write_threads+0x10/0x10
nfsctl_transaction_write+0x4a/0x80
vfs_write+0xf8/0x470
? putname+0x5e/0x80
ksys_write+0x70/0xf0
__x64_sys_write+0x18/0x20
x64_sys_call+0x7d/0x20f0
do_syscall_64+0xa4/0x280
entry_SYSCALL_64_after_hwframe+0x77/0x7f
Robert Morris
rtm@mit.edu
^ permalink raw reply [flat|nested] 2+ messages in thread* Re: buffer overrun in nfsd4_store_cache_entry()
2025-10-04 13:53 buffer overrun in nfsd4_store_cache_entry() rtm
@ 2025-10-04 16:39 ` Chuck Lever
0 siblings, 0 replies; 2+ messages in thread
From: Chuck Lever @ 2025-10-04 16:39 UTC (permalink / raw)
To: Jeff Layton, linux-nfs; +Cc: rtm
On 10/4/25 9:53 AM, rtm@csail.mit.edu wrote:
> A client can cause nfsd4_store_cache_entry() to write beyond the end
> of slot->sl_data[] here:
>
> base = resp->cstate.data_offset;
> slot->sl_datalen = buf->len - base;
> if (read_bytes_from_xdr_buf(buf, base, slot->sl_data, slot->sl_datalen))
>
> The demo below does this by sending a CREATE_SESSION with
>
> maxresp_sz = 64
> maxresp_cached = 80
>
> The result is that the next request that includes a SEQUENCE causes
> nfsd4_encode_operation() to decide that the response will be too
> large, and to return just an error response to the SEQUENCE. Then
> nfsd4_store_cache_entry() decides to cache the result, because
> nfsd4_cache_this() (really nfsd4_is_solo_sequence()) returns true. But
> the maxresp_cached=80 caused nfsd4_alloc_slot() to allocate slots with
> "size" of zero, so that slot->sl_data[] has no space. But
> nfsd4_store_cache_entry() is not aware of the true size of sl_data[].
2020 static struct nfsd4_slot *nfsd4_alloc_slot(struct
nfsd4_channel_attrs *fattrs,
2021 int index, gfp_t gfp)
2022 {
2023 struct nfsd4_slot *slot;
2024 size_t size;
2025
2026 /*
2027 * The RPC and NFS session headers are never saved in
2028 * the slot reply cache buffer.
2029 */
2030 size = fattrs->maxresp_cached < NFSD_MIN_HDR_SEQ_SZ ?
2031 0 : fattrs->maxresp_cached - NFSD_MIN_HDR_SEQ_SZ;
2032
2033 slot = kzalloc(struct_size(slot, sl_data, size), gfp);
2034 if (!slot)
2035 return NULL;
2036 slot->sl_index = index;
2037 return slot;
2038 }
Perhaps the internal minimum slot cache size should be the size of a
singleton SEQUENCE response, and not zero.
Is there any logic that checks that the response to be cached isn't
larger than the slot cache we allocated here? I don't see it in
nfsd4_check_resp_size() for example.
--
Chuck Lever
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2025-10-04 16:39 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-04 13:53 buffer overrun in nfsd4_store_cache_entry() rtm
2025-10-04 16:39 ` 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.