[PATCH] NFSv4.1: fix out-of-bounds write from zero back channel ca_maxrequests

Paula posted 1 patch 1 month, 1 week ago
fs/nfs/nfs4proc.c | 2 ++
1 file changed, 2 insertions(+)
[PATCH] NFSv4.1: fix out-of-bounds write from zero back channel ca_maxrequests
Posted by Paula 1 month, 1 week ago
nfs4_verify_back_channel_attrs() validates the channel attributes a
server returns in a CREATE_SESSION reply before they are used to size
the back channel slot table. Unlike its fore channel sibling
nfs4_verify_fore_channel_attrs(), it never rejects a back channel
ca_maxrequests (rcvd->max_reqs) of zero.

A zero value reaches nfs4_realloc_slot_table(), where
nfs4_reset_slot_table() derives both server_highest_slotid and
max_slotid from max_reqs - 1. The subtraction is unsigned, so zero
underflows to 0xffffffff and is stored as the table's slot-id ceiling.

The attributes are chosen by the server in its CREATE_SESSION response:
a malicious or compromised NFSv4.1 server, on the wire, with no
authentication under sec=sys, and before the mount completes.

With server_highest_slotid and max_slotid at 0xffffffff, the bounds
checks in nfs4_lookup_slot() (slotid <= max_slotid) and validate_seqid()
(csa_slotid > server_highest_slotid) no longer constrain the
server-chosen CB_SEQUENCE csa_slotid. nfs4_lock_slot() then runs
__set_bit(slotid, tbl->used_slots) against the fixed used_slots[] array,
which is only SLOT_TABLE_SZ (16) unsigned longs / 1024 bits. A csa_slotid
of 3584 sets a bit 448 bytes past the array, a slab-out-of-bounds write
into the neighbouring allocation. The slot id is server-controlled, so
the write offset is attacker-chosen and deterministic.

KASAN labels the access below "Read of size 8" because __set_bit() is a
read-modify-write and the sanitizer flags the load; the store is the
actual defect, hence "out-of-bounds write".

  BUG: KASAN: slab-out-of-bounds in nfs4_lock_slot+0x148/0x15c
  Read of size 8 at addr ffff0000c7232bf0 by task NFSv4 callback/235
  Call trace:
   nfs4_lock_slot+0x148/0x15c
   nfs4_try_to_lock_slot+0x74/0xc0
   nfs4_callback_sequence+0x850/0x141c
   nfs4_callback_compound+0x414/0x111c
   nfs_callback_dispatch+0x6c/0xf4
   svc_process_common+0xb3c/0x1ba4
   svc_process_bc+0x400/0x9bc
   svc_recv+0xb5c/0x23fc
   nfs4_callback_svc+0xa8/0x140
   kthread+0x32c/0x3e4
   ret_from_fork+0x10/0x20

  Allocated by task 234:
   __kmalloc_cache_noprof+0x188/0x480
   nfs4_alloc_session+0x44/0x260
   nfs41_init_client+0x18/0x80
   nfs4_init_client+0x120/0x440
   nfs4_set_client+0x310/0x5a0
   nfs4_create_server+0x124/0x234
   nfs4_try_get_tree+0x70/0x240
   vfs_get_tree+0x74/0x2c0

Reachable with CONFIG_NFS_V4_1 whenever the client mounts an NFSv4.1
export from a server the attacker controls or can spoof.

Reject a zero back channel ca_maxrequests in
nfs4_verify_back_channel_attrs(), as nfs4_verify_fore_channel_attrs()
already does, so the underflow never sizes the slot table. The error
propagates through nfs4_verify_channel_attrs() and aborts
nfs4_proc_create_session() before nfs4_update_session() installs the
poisoned ceiling.

Fixes: 5405fc44c337 ("NFSv4.x: Add kernel parameter to control the callback server")
Cc: stable@vger.kernel.org
Assisted-by: Bynario AI
Signed-off-by: Paula <paula@bynar.io>
---
 fs/nfs/nfs4proc.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c
index 5709c6fea..182078c2e 100644
--- a/fs/nfs/nfs4proc.c
+++ b/fs/nfs/nfs4proc.c
@@ -9161,6 +9161,8 @@ static int nfs4_verify_back_channel_attrs(struct nfs41_create_session_args *args
 		return -EINVAL;
 	if (rcvd->max_resp_sz_cached > sent->max_resp_sz_cached)
 		return -EINVAL;
+	if (rcvd->max_reqs == 0)
+		return -EINVAL;
 	if (rcvd->max_ops > sent->max_ops)
 		return -EINVAL;
 	if (rcvd->max_reqs > sent->max_reqs)

base-commit: 3a0dd7ba4f44cdc116d83712f61e7c1a95be3588
-- 
2.50.1 (Apple Git-155)