| Age | Commit message (Collapse) | Author |
|
ksmbd_reopen_durable_fd() walks the inode's m_op_list and rebinds every
detached oplock to the reconnecting session:
list_for_each_entry_rcu(op, &ci->m_op_list, op_entry,
lockdep_is_held(&ci->m_lock)) {
if (op->conn)
continue;
op->conn = ksmbd_conn_get(fp->conn);
op->sess = work->sess;
}
The only key is op->conn == NULL, which every detached durable handle on
that inode matches, not just the one owned by fp. When two sessions hold
durable handles on the same file and both disconnect, reconnecting one of
them adopts the other session's oplock: op->sess is overwritten with the
reconnecting session without taking a reference on it, while op->conn
pins the connection.
The sibling teardown path, session_fd_check(), keys on the identity of
the connection being torn down (op->conn == conn) rather than on shared
state, and so does not have this problem.
Once the adopting session is destroyed, ksmbd_session_destroy() frees it
while the foreign oplock still points at it. The reader in
ksmbd_close_fd_app_instance_id() validates only opinfo->conn, which is
still live thanks to the reference taken above, and then dereferences the
stale session:
if (!opinfo->conn) {
up_read(&fp->f_ci->m_lock);
goto out;
}
ft = &opinfo->sess->file_table;
write_lock(&ft->lock);
BUG: KASAN: slab-use-after-free in _raw_write_lock+0x74/0xd0
Write of size 4 at addr ffff88810a970528 by task kworker/0:0/9
Workqueue: ksmbd-io handle_ksmbd_work
Call Trace:
_raw_write_lock+0x74/0xd0
ksmbd_close_fd_app_instance_id+0x183/0x410
smb2_open+0x1346/0x4430
handle_ksmbd_work+0x2bb/0x7b0
Reached from an authenticated session against a share with the default
durable-handle and oplock configuration: two sessions open the same file
with a durable-v2 handle and an RH lease under distinct AppInstanceIds,
both log off, one reconnects with DH2C, and a later durable-v2 create
carrying the other AppInstanceId walks into the freed session.
Constrain the loop to the oplock owned by the file being reopened.
Fixes: f363a0fb134a ("ksmbd: fix app-instance durable supersede session UAF")
Cc: stable@vger.kernel.org
Signed-off-by: Aldo Ariel Panzardo <qwe.aldo@gmail.com>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
SMB3.1.1 multichannel binding preserves the preauthentication hash in a
preauth_session between the NTLM negotiate and authenticate requests.
The binding NTLM negotiate allocates this object and returns
STATUS_MORE_PROCESSING_REQUIRED. If the client disconnects before it sends
the authenticate request, neither the authenticate nor error cleanup paths
free the object.
Release any remaining preauthentication sessions when tearing down the
connection. Initialize the list when allocating the connection so that this
cleanup is safe regardless of the negotiated dialect.
Reported-by: Runa Takemoto <takemotoruna223@gmail.com>
Fixes: f5a544e3bab7 ("ksmbd: add support for SMB3 multichannel")
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
A connection-close scan can miss the synthetic CHANGE_NOTIFY work item
because smb2_notify() registers it directly after setup_async_work()
has returned. Link both regular and synthetic async work through one
helper that checks the connection state under request_lock.
If the connection is already closing, release a newly allocated async
ID or complete the synthetic notify work immediately.
Signed-off-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Co-developed-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
An async request may still be waiting when a connection is closed.
This can stop the connection from closing.
Cancel active async requests before waiting for them to finish.
Suggested-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Some SMB responses keep their data in another buffer. The SMB header
and the data are then in different iovs.
The old code only handled this for SMB2 READ. For other commands, it
signed only the last iov. QUERY_INFO and CHANGE_NOTIFY can also use
another iov for their data. Their SMB header was not signed, so Windows
will client rejected the response.
Find the iov that starts with the current SMB header. Sign this iov and
all iovs after it.
Suggested-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
ipc_validate_msg() computes the expected message size by reading length
fields out of the response buffer supplied by the userspace ksmbd daemon
(payload_sz, session_key_len, ngroups, ...). Those fields are read before
the buffer is verified to be large enough to contain the struct they belong
to, so a short response makes the read land past the end of the allocation.
handle_response() sizes entry->response purely from the netlink attribute
length (nla_len()) and only guards the leading handle read, so the daemon
can install a response as small as the kmalloc-8 object seen below. When
ipc_msg_send_request() then calls ipc_validate_msg() for a
KSMBD_EVENT_RPC_REQUEST, the cast to struct ksmbd_rpc_command reads
resp->payload_sz at offset 8 of an 8-byte allocation:
[ 3697.841381] ==================================================================
[ 3697.844099] BUG: KASAN: slab-out-of-bounds in ipc_msg_send_request+0x763/0x800
[ 3697.846604] Read of size 4 at addr ffff888105f95910 by task kworker/4:3/20682
[ 3697.849061]
[ 3697.849801] CPU: 4 UID: 0 PID: 20682 Comm: kworker/4:3 Not tainted 7.2.0-rc3-next-20260717-virtme #117 PREEMPT(lazy)
[ 3697.850077] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
[ 3697.850303] Workqueue: ksmbd-io handle_ksmbd_work
[ 3697.850592] Call Trace:
[ 3697.850794] <TASK>
[ 3697.850952] __dump_stack+0x21/0x60
[ 3697.851239] dump_stack_lvl+0xc2/0x100
[ 3697.851528] print_address_description+0x77/0x200
[ 3697.851816] ? ipc_msg_send_request+0x763/0x800
[ 3697.852024] print_report+0x58/0x70
[ 3697.852316] kasan_report+0x117/0x150
[ 3697.852585] ? down_write+0x146/0x1f0
[ 3697.852809] ? ipc_msg_send_request+0x763/0x800
[ 3697.853082] ipc_msg_send_request+0x763/0x800
[ 3697.853385] ? __pfx_ipc_msg_send_request+0x10/0x10
[ 3697.853604] ? kasan_unpoison+0x48/0x70
[ 3697.853936] ? __pfx___up_read+0x10/0x10
[ 3697.854221] ksmbd_rpc_ioctl+0x380/0x520
[ 3697.854542] ? __pfx_ksmbd_rpc_ioctl+0x10/0x10
[ 3697.854757] ? kasan_unpoison+0x48/0x70
[ 3697.854962] ? copy_from_kernel_nofault+0x32c/0x4e0
[ 3697.855166] ? kasan_unpoison+0x48/0x70
[ 3697.855416] fsctl_pipe_transceive+0x139/0x7a0
[ 3697.855705] ? __pfx_copy_from_kernel_nofault+0x10/0x10
[ 3697.855937] ? __pfx_fsctl_pipe_transceive+0x10/0x10
[ 3697.856388] ? __sanitizer_cov_trace_switch+0x7b/0x140
[ 3697.856620] smb2_ioctl+0x1141/0x3420
[ 3697.856994] ? __pfx_smb2_ioctl+0x10/0x10
[ 3697.857182] ? get_smb2_cmd_val+0xe3/0x1c0
[ 3697.857655] handle_ksmbd_work+0x9ad/0x15e0
[ 3697.858034] ? __pfx_handle_ksmbd_work+0x10/0x10
[ 3697.858251] ? lock_release+0xf7/0x360
[ 3697.858466] ? process_scheduled_works+0x954/0x1600
[ 3697.858698] ? process_scheduled_works+0x954/0x1600
[ 3697.858905] process_scheduled_works+0xc22/0x1600
[ 3697.859368] ? __pfx_process_scheduled_works+0x10/0x10
[ 3697.859637] ? __pfx_assign_work+0x10/0x10
[ 3697.859896] ? lock_is_held_type+0x7b/0x110
[ 3697.860146] worker_thread+0x975/0xee0
[ 3697.860524] ? __pfx_do_raw_spin_lock+0x10/0x10
[ 3697.860830] ? __kthread_parkme+0x21e/0x260
[ 3697.861105] kthread+0x3a6/0x490
[ 3697.861423] ? __pfx_worker_thread+0x10/0x10
[ 3697.861643] ? __pfx_kthread+0x10/0x10
[ 3697.861878] ret_from_fork+0x55a/0xa20
[ 3697.862194] ? __pfx_ret_from_fork+0x10/0x10
[ 3697.862480] ? __pfx_kthread+0x10/0x10
[ 3697.862714] ret_from_fork_asm+0x1a/0x30
[ 3697.862965] </TASK>
[ 3697.863039]
[ 3697.938882] Allocated by task 20761:
[ 3697.940257] kasan_save_track+0x3e/0x80
[ 3697.941782] __kasan_kmalloc+0x72/0x90
[ 3697.943228] __kvmalloc_node_noprof+0x3e9/0x6a0
[ 3697.944948] handle_generic_event+0x59b/0x750
[ 3697.946592] genl_family_rcv_msg_doit+0x3d6/0x560
[ 3697.946977] genl_rcv_msg+0x67c/0x900
[ 3697.947224] netlink_rcv_skb+0x286/0x580
[ 3697.947488] genl_rcv+0x2d/0x80
[ 3697.947706] netlink_unicast+0x937/0xb70
[ 3697.947993] netlink_sendmsg+0x977/0xc10
[ 3697.948268] __sock_sendmsg+0x264/0x2d0
[ 3697.948536] __sys_sendto+0x4de/0x690
[ 3697.948789] __x64_sys_sendto+0x173/0x380
[ 3697.949069] do_syscall_64+0x13d/0x420
[ 3697.949328] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 3697.949662]
[ 3697.949779] The buggy address belongs to the object at ffff888105f95908
[ 3697.949779] which belongs to the cache kmalloc-8 of size 8
[ 3697.950550] The buggy address is located 0 bytes to the right of
[ 3697.950550] allocated 8-byte region [ffff888105f95908, ffff888105f95910)
[ 3697.951455]
[ 3697.951574] The buggy address belongs to the physical page:
[ 3697.951958] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0xffff888105f951b8 pfn:0x105f95
[ 3697.952571] flags: 0x100000000000200(workingset|node=0|zone=2)
[ 3697.952973] page_type: f5(slab)
[ 3697.953198] raw: 0100000000000200 ffff888100042640 ffffea0004063610 ffff888100040588
[ 3697.953707] raw: ffff888105f951b8 00000000001c000e 00000000f5000000 0000000000000000
[ 3697.954240] page dumped because: kasan: bad access detected
[ 3697.954616]
[ 3697.954734] Memory state around the buggy address:
[ 3697.955063] ffff888105f95800: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fa
[ 3697.955534] ffff888105f95880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 3697.956006] >ffff888105f95900: fc 00 fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 3697.956477] ^
[ 3697.956728] ffff888105f95980: fc fc fc fa fc fc fc fc fc fc fc fc fc fc fc fc
[ 3697.957202] ffff888105f95a00: fc fc fc fc fc fa fc fc fc fc fc fc fc fc fc fc
[ 3697.957671] ==================================================================
The final "entry->msg_sz != msg_sz" comparison cannot help: the offending
read has already happened by the time it runs. Every case in the switch
shares this pattern.
Floor entry->msg_sz against the base struct of each event type before
dereferencing any of its length fields. On failure ipc_msg_send_request()
already frees the response and returns NULL, so callers stay safe.
The malformed message originates from the ksmbd.mountd daemon over genl
netlink rather than a remote SMB client, so triggering it requires a buggy
or compromised daemon; it is still an out-of-bounds read the validator is
meant to prevent.
Fixes: d6a6aa81eac2 ("ksmbd: validate response sizes in ipc_validate_msg()")
Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
smbdirect_socket_destroy() releases the listener's pending/ready child
sockets while still holding the listener's handler lock, the
&id_priv->handler_mutex taken via rdma_lock_handler(), not
sc->listen.lock, and before the listener's own rdma_destroy_id().
That ordering has one real consequence and one cosmetic one.
The real one: smbdirect_socket_release() drops the child's last
reference, which destroys the child's cm_id. Doing that before the
listener's rdma_destroy_id() lets _cma_cancel_listens(), running from
the listener's _destroy_id(), walk an already freed child id_priv,
which KASAN catches as a slab-use-after-free during listener shutdown:
[ 4758.909130] BUG: KASAN: slab-use-after-free in __mutex_lock+0x1469/0x1560
[ 4758.911450] Read of size 1 at addr ffff88821c381db4 by task ksmbd.control/1652
[ 4758.913262] Call Trace:
[ 4758.913267] <TASK>
[ 4758.913299] __mutex_lock+0x1469/0x1560
[ 4758.913408] _cma_cancel_listens+0x312/0x3b0
[ 4758.913413] _destroy_id+0x363/0xee0
[ 4758.913417] smbdirect_socket_destroy_sync+0x17d5/0x2440
[ 4758.913443] smbdirect_socket_release+0x124/0x230
[ 4758.913451] ksmbd_rdma_stop_listening+0x9f/0x190
[ 4758.913457] ksmbd_conn_transport_destroy+0x65/0x3c0
[ 4758.913463] kill_server_store+0x1fb/0x2b0
[ 4758.913501] kernfs_fop_write_iter+0x349/0x4d0
[ 4758.913507] vfs_write+0x5e7/0xc70
[ 4758.913528] ksys_write+0x12a/0x210
[ 4758.913541] do_syscall_64+0x135/0x460
[ 4758.913555] entry_SYSCALL_64_after_hwframe+0x77/0x7f
The cosmetic one: releasing a child recurses into
smbdirect_socket_destroy(), which takes the child's own
rdma_lock_handler() lock nested under the listener's. The listener's
and the child's cm_id are always different instances, so this cannot
deadlock for real; the CM core itself nests a new connection id's
handler_mutex under the listening id's in cma_ib_req_handler(). But
lockdep only sees one lock class, reports possible recursive locking,
and then disables itself, hiding real locking bugs for the rest of the
run:
[ 2424.579653] WARNING: possible recursive locking detected
[ 2424.581180] 7.1.0-next-20260623+ #89 Not tainted
[ 2424.582548] --------------------------------------------
[ 2424.584500] ksmbd.control/8854 is trying to acquire lock:
[ 2424.586817] ffff888102303c20 (&id_priv->handler_mutex){+.+.}-{4:4}, at: smbdirect_socket_destroy_sync+0xc39/0x2440
[ 2424.590590]
[ 2424.590590] but task is already holding lock:
[ 2424.591601] ffff888102046c20 (&id_priv->handler_mutex){+.+.}-{4:4}, at: smbdirect_socket_destroy_sync+0xc39/0x2440
[ 2424.594178]
[ 2424.594178] other info that might help us debug this:
[ 2424.596634] Possible unsafe locking scenario:
[ 2424.596634]
[ 2424.598841] CPU0
[ 2424.599765] ----
[ 2424.600695] lock(&id_priv->handler_mutex);
[ 2424.601836] lock(&id_priv->handler_mutex);
[ 2424.602590]
[ 2424.602590] *** DEADLOCK ***
[ 2424.602590]
[ 2424.604512] May be due to missing lock nesting notation
Splice the pending/ready children onto a local list under the
listener's listen.lock, while the handler lock is held so a concurrent
CM CONNECT_REQUEST cannot add more, but defer the actual
smbdirect_socket_release() calls until after the listener's cm_id has
been destroyed and its handler lock dropped. The children are
independent sockets whose teardown needs neither the listener's
handler lock nor its cm_id.
Found with ksmbdzzer [2], a KSMBD fuzzer that drives libFuzzer with a
kcov-dataflow [1] coverage vector: it folds each instrumented
comparison/argument's runtime operand value together with its PC (the
default arm mixes them as pc⊕val) so that a new operand value at a known
site counts as new coverage.
[1] https://lwn.net/Articles/1077606/
[2] https://github.com/yskzalloc/kcov-dataflow
Fixes: dc691b91ad16 ("smb: smbdirect: introduce smbdirect_socket_{listen,accept}()")
Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
__smbdirect_socket_schedule_cleanup() and smbdirect_socket_cleanup_work()
take sc->listen.lock and walk the listener's pending list, recursing
into smbdirect_socket_schedule_cleanup() for each child, and every
child takes its own listen.lock inside that walk.
This cannot deadlock for real: the nesting is strictly listener into
child, a child never listens, so the outer and the inner lock are
always different instances. lockdep only tracks lock classes, though;
it sees the same class acquired twice and reports a possible recursive
locking deadlock. This change therefore works around a lockdep
limitation rather than fixing a real hang, but the report is still
worth avoiding: lockdep disables itself after the first splat and then
hides real locking bugs for the rest of the run.
Only a socket that was a listener owns a populated listen.ready/pending
list; a child has empty lists and nothing to do in these blocks. Guard
both of them with sc->listen.backlog != -1, the "was a listener" marker
that smbdirect_socket_destroy() already uses: listen.backlog leaves its
initial -1 exactly once, when smbdirect_socket_listen() succeeds. The
alternative !sc->accept.listener test reads as "not a listener" while
meaning the opposite, and it is also true for an accepted child, whose
accept.listener has been cleared on hand-over. With the guard the walk
only runs for a listener and never nests a child's listen.lock under
it; a pending child stays on its listener's list for the free path
(smbdirect_socket_destroy) to reap.
[ 741.705044] WARNING: possible recursive locking detected
[ 741.705403] 7.1.0-next-20260623+ #75 Not tainted
[ 741.705695] --------------------------------------------
[ 741.706022] ksmbd.control/18502 is trying to acquire lock:
[ 741.706379] ffff888108d612f8 (&sc->listen.lock){....}-{3:3}, at: __smbdirect_socket_schedule_cleanup+0x719/0xd70
[ 741.707008]
[ 741.707008] but task is already holding lock:
[ 741.707396] ffff8881087642f8 (&sc->listen.lock){....}-{3:3}, at: __smbdirect_socket_schedule_cleanup+0x719/0xd70
[ 741.708025]
[ 741.708025] other info that might help us debug this:
[ 741.708448] Possible unsafe locking scenario:
[ 741.708448]
[ 741.708845] CPU0
[ 741.709016] ----
[ 741.709186] lock(&sc->listen.lock);
[ 741.709453] lock(&sc->listen.lock);
[ 741.709705]
[ 741.709705] *** DEADLOCK ***
[ 741.709705]
[ 741.710095] May be due to missing lock nesting notation
[ 741.710095]
[ 741.710663] 6 locks held by ksmbd.control/18502:
[ 741.710975] #0: ffff888109e51420 (sb_writers#7){.+.+}-{0:0}, at: vfs_write+0x1e7/0xc70
[ 741.711561] #1: ffff888126ec3880 (&of->mutex){+.+.}-{4:4}, at: kernfs_fop_write_iter+0x1be/0x4d0
[ 741.712147] #2: ffff888102af17b0 (kn->active#45){.+.+}-{0:0}, at: kernfs_fop_write_iter+0x205/0x4d0
[ 741.712803] #3: ffffffff85ad1e00 (ctrl_lock){+.+.}-{4:4}, at: kill_server_store+0x1e0/0x2b0
[ 741.713381] #4: ffffffff85ad41a0 (init_lock){+.+.}-{4:4}, at: ksmbd_conn_transport_destroy+0x5b/0x3c0
[ 741.713995] #5: ffff8881087642f8 (&sc->listen.lock){....}-{3:3}, at: __smbdirect_socket_schedule_cleanup+0x719/0xd70
[ 741.714736]
[ 741.714736] stack backtrace:
[ 741.715038] CPU: 4 UID: 0 PID: 18502 Comm: ksmbd.control Not tainted 7.1.0-next-20260623+ #75 PREEMPT(lazy)
[ 741.715043] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
[ 741.715046] Call Trace:
[ 741.715049] <TASK>
[ 741.715052] dump_stack_lvl+0x77/0xa0
[ 741.715058] print_deadlock_bug+0x279/0x290
[ 741.715065] __lock_acquire+0x272a/0x2e30
[ 741.715070] ? stack_trace_save+0xae/0x100
[ 741.715075] ? smb_direct_logging_vaprintf+0x1a0/0x230
[ 741.715079] ? __pfx_smb_direct_logging_vaprintf+0x10/0x10
[ 741.715082] ? __timer_delete+0x58/0x320
[ 741.715087] lock_acquire+0xd3/0x270
[ 741.715091] ? __smbdirect_socket_schedule_cleanup+0x719/0xd70
[ 741.715095] ? entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 741.715099] _raw_spin_lock_irqsave+0x42/0x60
[ 741.715105] ? __smbdirect_socket_schedule_cleanup+0x719/0xd70
Note the two addresses above: ffff888108d612f8 is the child's lock,
ffff8881087642f8 the listener's, always distinct objects.
Found with ksmbdzzer [2], a KSMBD fuzzer that drives libFuzzer with a
kcov-dataflow [1] coverage vector: it folds each instrumented
comparison/argument's runtime operand value together with its PC (the
default arm mixes them as pc⊕val) so that a new operand value at a known
site counts as new coverage.
[1] https://lwn.net/Articles/1077606/
[2] https://github.com/yskzalloc/kcov-dataflow
Fixes: dc691b91ad16 ("smb: smbdirect: introduce smbdirect_socket_{listen,accept}()")
Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
Reviewed-by: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
On the rdma_accept_failed error path of
smbdirect_accept_connect_request(), the receive io posted just above is
owned by the QP (recv_io is set to NULL after a successful post). The
error path fell through to smbdirect_connection_destroy_mem_pools()
before smbdirect_connection_destroy_qp(), so the mem pools and the
recv_io slab cache were destroyed while that recv_io was still
outstanding on the QP.
The drain in smbdirect_connection_destroy_qp() (ib_drain_qp()) is what
runs the recv completion that returns the recv_io to the free list, so
destroying the pools first leaves the object outstanding at
kmem_cache_destroy() time ("Slab cache still has objects") and later
frees it into an already-destroyed mempool (mempool_free_bulk
NULL-pointer dereference).
Give rdma_accept_failed its own teardown that drains the QP first, then
destroys the mem pools, and returns. The remaining labels
(post_recv_io_failed onward) run before the recv_io was ever posted, so
they keep the mem-pools-then-qp order.
The outstanding recv_io at kmem_cache_destroy() time:
[ 3487.344647] =============================================================================
[ 3487.349942] BUG smbdirect_recv_io_cache_ffff88811ba99000 (Not tainted): Objects remaining on __kmem_cache_shutdown()
[ 3487.356078] -----------------------------------------------------------------------------
[ 3487.356078]
[ 3487.356738] Object 0xffff8881511c3440 @offset=13376
[ 3487.358464] Allocated in mempool_alloc_noprof+0x18c/0x290 age=1194 cpu=6 pid=22254
[ 3487.361197] mempool_alloc_noprof+0x18c/0x290
[ 3487.361542] smbdirect_connection_create_mem_pools+0x405/0x780
[ 3487.361972] smbdirect_accept_connect_request+0x5a8/0x1b80
[ 3487.362359] smbdirect_listen_rdma_event_handler+0x1579/0x1b90
[ 3487.362779] cma_cm_event_handler+0x9c/0x230
[ 3487.363096] cma_ib_req_handler+0x2682/0x45d0
[ 3487.363414] cm_process_work+0x56/0x3d0
[ 3487.363676] cm_work_handler+0x8a0e/0xd000
[ 3487.367496] process_scheduled_works+0xa07/0x13a0
[ 3487.367859] worker_thread+0x7c9/0xc80
[ 3487.368148] kthread+0x341/0x430
[ 3487.368407] ret_from_fork+0x3a8/0x7a0
[ 3487.368704] ret_from_fork_asm+0x1a/0x30
[ 3487.370307] Slab 0xffffea0005447000 objects=19 used=1 fp=0xffff8881511c0040 flags=0x100000000000240(workingset|head|node=0|zone=2)
[ 3487.372840] ------------[ cut here ]------------
[ 3487.373195] WARNING: mm/slub.c:1244 at __slab_err+0x1a/0x30, CPU#6: kworker/6:84/22254
[ 3487.373759] Modules linked in:
[ 3487.373993] CPU: 6 UID: 0 PID: 22254 Comm: kworker/6:84 Tainted: G B 7.1.0-next-20260623+ #88 PREEMPT(lazy)
[ 3487.374778] Tainted: [B]=BAD_PAGE
[ 3487.377830] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
[ 3487.378515] Workqueue: ib_cm cm_work_handler
[ 3487.378820] RIP: 0010:__slab_err+0x1a/0x30
[ 3487.379129] Code: 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 0f 1f 44 00 00 e8 36 00 00 00 bf 05 00 00 00 be 01 00 00 00 e8 f7 75 45 00 90 <0f> 0b 90 c3 cc cc cc cc cc 66 66 66 66 2e 0f 1f 84 00 00 00 00 00
[ 3487.383255] RSP: 0018:ffff888220fc7050 EFLAGS: 00010093
[ 3487.383643] RAX: ffffffff8168e60a RBX: ffff88810955e640 RCX: ffff88821c381d80
[ 3487.384158] RDX: 0000000000000000 RSI: 0000000000000008 RDI: ffffffff870fa080
[ 3487.384662] RBP: ffff888220fc7068 R08: ffffffff870fa087 R09: 1ffffffff0e1f410
[ 3487.385192] R10: dffffc0000000000 R11: fffffbfff0e1f411 R12: ffffea0005447210
[ 3487.385674] R13: ffffea0005447000 R14: ffff888220fc7068 R15: ffff88812a8ab300
[ 3487.388932] FS: 0000000000000000(0000) GS:ffff888427e76000(0000) knlGS:0000000000000000
[ 3487.389529] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 3487.389934] CR2: 00007ffcf2d84fd8 CR3: 0000000111d64006 CR4: 0000000000f72ef0
[ 3487.390440] PKRU: 55555554
[ 3487.390641] Call Trace:
[ 3487.390826] <TASK>
[ 3487.391209] __kmem_cache_shutdown+0x1aa/0x2b0
[ 3487.392062] ? smbdirect_connection_destroy_mem_pools+0x239/0x300
[ 3487.393565] kmem_cache_destroy+0x9d/0x180
[ 3487.398663] smbdirect_connection_destroy_mem_pools+0x239/0x300
[ 3487.403534] ? __pfx_smb_direct_logging_needed+0x10/0x10
[ 3487.407562] smbdirect_accept_connect_request+0x95c/0x1b80
[ 3487.412391] ? __pfx_smbdirect_accept_connect_request+0x10/0x10
[ 3487.416753] ? do_raw_spin_lock+0x130/0x300
[ 3487.420623] ? smbdirect_socket_set_initial_parameters+0x28b/0x6a0
[ 3487.424322] ? lock_acquire+0x4c/0x270
[ 3487.424409] ksmbd: can't change a file to a directory
[ 3487.426321] ? trace_irq_enable+0x36/0x120
[ 3487.429144] smbdirect_listen_rdma_event_handler+0x1579/0x1b90
[ 3487.432606] ? __pfx_smbdirect_listen_rdma_event_handler+0x10/0x10
[ 3487.433595] ? trace_cm_event_handler+0x51/0x170
[ 3487.435183] ? __pfx_smbdirect_listen_rdma_event_handler+0x10/0x10
[ 3487.435646] ? cma_listen_handler+0xf6/0x150
[ 3487.435975] cma_cm_event_handler+0x9c/0x230
[ 3487.436288] cma_ib_req_handler+0x2682/0x45d0
[ 3487.439039] ? __pfx_cma_ib_req_handler+0x10/0x10
[ 3487.439540] ? __pfx_roce_resolve_route_from_path+0x10/0x10
[ 3487.439972] ? stack_depot_save_flags+0x34/0x840
[ 3487.440374] ? __xas_nomem+0xa9/0x410
[ 3487.443356] ? xas_clear_mark+0x26c/0x4a0
[ 3487.443673] cm_process_work+0x56/0x3d0
[ 3487.443969] ? _raw_spin_unlock_irq+0x28/0x50
[ 3487.444317] cm_work_handler+0x8a0e/0xd000
[ 3487.444624] ? __pfx_cm_work_handler+0x10/0x10
[ 3487.444971] ? pwq_dec_nr_in_flight+0xa73/0xdf0
[ 3487.445344] ? __pfx_pwq_dec_nr_in_flight+0x10/0x10
[ 3487.445737] ? lock_acquire+0x4c/0x270
[ 3487.448833] ? process_scheduled_works+0x995/0x13a0
[ 3487.449230] ? process_scheduled_works+0x995/0x13a0
[ 3487.449588] process_scheduled_works+0xa07/0x13a0
[ 3487.449938] ? __pfx_process_scheduled_works+0x10/0x10
[ 3487.450334] ? do_raw_spin_lock+0x130/0x300
[ 3487.450639] ? assign_work+0x3bb/0x5c0
[ 3487.450916] worker_thread+0x7c9/0xc80
[ 3487.451211] kthread+0x341/0x430
[ 3487.451453] ? __pfx_worker_thread+0x10/0x10
[ 3487.451756] ? __pfx_kthread+0x10/0x10
[ 3487.454814] ret_from_fork+0x3a8/0x7a0
[ 3487.455114] ? __pfx_ret_from_fork+0x10/0x10
[ 3487.455450] ? __switch_to+0xb76/0x1110
[ 3487.455772] ? __pfx_kthread+0x10/0x10
[ 3487.456081] ret_from_fork_asm+0x1a/0x30
[ 3487.456384] </TASK>
[ 3487.456549] irq event stamp: 0
[ 3487.456767] hardirqs last enabled at (0): [<0000000000000000>] 0x0
[ 3487.460124] hardirqs last disabled at (0): [<ffffffff81684648>] copy_process+0xa08/0x3a10
[ 3487.460726] softirqs last enabled at (0): [<ffffffff81684648>] copy_process+0xa08/0x3a10
[ 3487.461328] softirqs last disabled at (0): [<0000000000000000>] 0x0
[ 3487.461778] ---[ end trace 0000000000000000 ]---
[ 3487.543875] ksmbd: can't change a file to a directory
[ 3487.599675] ksmbd: can't change a file to a directory
[ 3487.626694] ksmbd: can't change a file to a directory
[ 3487.824687] ksmbd: can't change a file to a directory
[ 3487.871840] ksmbd: can't change a file to a directory
[ 3487.986207] ------------[ cut here ]------------
[ 3487.987157] kmem_cache_destroy smbdirect_recv_io_cache_ffff88811ba99000: Slab cache still has objects when called from smbdirect_connection_destroy_mem_pools+0x239/0x300
[ 3487.987183] WARNING: mm/slab_common.c:572 at kmem_cache_destroy+0x15c/0x180, CPU#6: kworker/6:84/22254
[ 3487.999821] Modules linked in:
[ 3488.001902] CPU: 6 UID: 0 PID: 22254 Comm: kworker/6:84 Tainted: G B W 7.1.0-next-20260623+ #88 PREEMPT(lazy)
[ 3488.008289] Tainted: [B]=BAD_PAGE, [W]=WARN
[ 3488.010459] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
[ 3488.014502] Workqueue: ib_cm cm_work_handler
[ 3488.017790] RIP: 0010:kmem_cache_destroy+0x16a/0x180
[ 3488.020662] Code: fd ff 48 8b 3d 2f c0 9c 06 48 89 de 5b 41 5e 5d e9 5b a3 0e 00 48 8d 3d a4 07 12 04 48 8b 53 58 48 c7 c6 91 9d 3e 85 4c 89 f1 <67> 48 0f b9 3a e9 33 ff ff ff 66 66 66 2e 0f 1f 84 00 00 00 00 00
[ 3488.028077] RSP: 0018:ffff888220fc70b8 EFLAGS: 00010202
[ 3488.032038] RAX: 0000000000000001 RBX: ffff88810955e640 RCX: ffffffff822bc079
[ 3488.035742] RDX: ffff88812404ec40 RSI: ffffffff853e9d91 RDI: ffffffff85f7ac50
[ 3488.037830] RBP: 0000000000000001 R08: ffff8883aef3e843 R09: 1ffff11075de7d08
[ 3488.041076] R10: dffffc0000000000 R11: ffffed1075de7d09 R12: 1ffff11024fa6c3c
[ 3488.045376] R13: ffff888127d361e8 R14: ffffffff822bc079 R15: ffff88811ba99538
[ 3488.049073] FS: 0000000000000000(0000) GS:ffff888427e76000(0000) knlGS:0000000000000000
[ 3488.052515] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 3488.054934] CR2: 00007ffcf2d84fd8 CR3: 0000000111d64006 CR4: 0000000000f72ef0
[ 3488.058529] PKRU: 55555554
[ 3488.060542] Call Trace:
[ 3488.061595] <TASK>
[ 3488.062006] smbdirect_connection_destroy_mem_pools+0x239/0x300
[ 3488.066041] ? __pfx_smb_direct_logging_needed+0x10/0x10
[ 3488.068620] smbdirect_accept_connect_request+0x95c/0x1b80
[ 3488.071594] ? __pfx_smbdirect_accept_connect_request+0x10/0x10
[ 3488.073218] ksmbd: not allow base filename in rename
[ 3488.074751] ? do_raw_spin_lock+0x130/0x300
[ 3488.076792] ksmbd: can't change a file to a directory
[ 3488.077942] ? smbdirect_socket_set_initial_parameters+0x28b/0x6a0
[ 3488.080143] ? lock_acquire+0x4c/0x270
[ 3488.080747] ? trace_irq_enable+0x36/0x120
[ 3488.081400] smbdirect_listen_rdma_event_handler+0x1579/0x1b90
[ 3488.085089] ? __pfx_smbdirect_listen_rdma_event_handler+0x10/0x10
[ 3488.089333] ? trace_cm_event_handler+0x51/0x170
[ 3488.092637] ? __pfx_smbdirect_listen_rdma_event_handler+0x10/0x10
[ 3488.095741] ? cma_listen_handler+0xf6/0x150
[ 3488.099638] cma_cm_event_handler+0x9c/0x230
[ 3488.101552] cma_ib_req_handler+0x2682/0x45d0
[ 3488.104571] ? __pfx_cma_ib_req_handler+0x10/0x10
[ 3488.106790] ? __pfx_roce_resolve_route_from_path+0x10/0x10
[ 3488.109799] ? stack_depot_save_flags+0x34/0x840
[ 3488.112174] ? __xas_nomem+0xa9/0x410
[ 3488.114217] ? xas_clear_mark+0x26c/0x4a0
[ 3488.116854] cm_process_work+0x56/0x3d0
[ 3488.118179] ? _raw_spin_unlock_irq+0x28/0x50
[ 3488.120034] cm_work_handler+0x8a0e/0xd000
[ 3488.121677] ? __pfx_cm_work_handler+0x10/0x10
[ 3488.123682] ? pwq_dec_nr_in_flight+0xa73/0xdf0
[ 3488.126928] ? __pfx_pwq_dec_nr_in_flight+0x10/0x10
[ 3488.129390] ? lock_acquire+0x4c/0x270
[ 3488.131432] ? process_scheduled_works+0x995/0x13a0
[ 3488.132694] ksmbd: can't change a file to a directory
[ 3488.136702] ? process_scheduled_works+0x995/0x13a0
[ 3488.140060] process_scheduled_works+0xa07/0x13a0
[ 3488.143379] ? __pfx_process_scheduled_works+0x10/0x10
[ 3488.147221] ? do_raw_spin_lock+0x130/0x300
[ 3488.150790] ? assign_work+0x3bb/0x5c0
[ 3488.154259] worker_thread+0x7c9/0xc80
[ 3488.155730] kthread+0x341/0x430
[ 3488.158309] ? __pfx_worker_thread+0x10/0x10
[ 3488.160865] ? __pfx_kthread+0x10/0x10
[ 3488.164384] ret_from_fork+0x3a8/0x7a0
[ 3488.167020] ? __pfx_ret_from_fork+0x10/0x10
[ 3488.170050] ? __switch_to+0xb76/0x1110
[ 3488.171809] ? __pfx_kthread+0x10/0x10
[ 3488.174955] ret_from_fork_asm+0x1a/0x30
[ 3488.175605] </TASK>
[ 3488.176206] irq event stamp: 0
[ 3488.179360] hardirqs last enabled at (0): [<0000000000000000>] 0x0
[ 3488.186521] hardirqs last disabled at (0): [<ffffffff81684648>] copy_process+0xa08/0x3a10
[ 3488.191889] softirqs last enabled at (0): [<ffffffff81684648>] copy_process+0xa08/0x3a10
[ 3488.196853] softirqs last disabled at (0): [<0000000000000000>] 0x0
[ 3488.200870] ---[ end trace 0000000000000000 ]---
Found with ksmbdzzer [2], a KSMBD fuzzer that drives libFuzzer with a
kcov-dataflow [1] coverage vector: it folds each instrumented
comparison/argument's runtime operand value together with its PC (the
default arm mixes them as pc⊕val) so that a new operand value at a known
site counts as new coverage.
[1] https://lwn.net/Articles/1077606/
[2] https://github.com/yskzalloc/kcov-dataflow
Fixes: eb3ed1e9048c ("smb: smbdirect: introduce smbdirect_accept_connect_request()")
Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
Acked-by: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
smbdirect_connection_destroy_qp() creates the send and receive completion
queues with ib_alloc_cq_any(), which for IB_POLL_WORKQUEUE arms an
internal completion handler that runs ib_cq_poll_work() on a workqueue.
Tearing those CQs down with ib_destroy_cq() frees them without first
cancelling that poll work.
If the provider posts a completion late -- for example Soft-RoCE (rxe)
posting an RNR error from rxe_receiver() after rdma_destroy_qp() -- the
handler re-queues ib_cq_poll_work() on the already-freed CQ, and a
follow-on access faults in rxe_req_notify_cq().
Use ib_free_cq(), which cancel_work_sync()es the poll work before freeing
the CQ, so no completion handler can run against a freed queue.
[ 1236.599526] ==================================================================
[ 1236.602142] BUG: KASAN: slab-use-after-free in ib_cq_poll_work+0xd0/0x1a0
[ 1236.605524] Read of size 8 at addr ffff888111865800 by task kworker/4:1H/82
[ 1236.609017]
[ 1236.609270] CPU: 4 UID: 0 PID: 82 Comm: kworker/4:1H Not tainted 7.2.0-rc3-next-20260717-virtme #110 PREEMPT(lazy)
[ 1236.609287] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
[ 1236.609498] Workqueue: ib-comp-wq ib_cq_poll_work
[ 1236.609525] Call Trace:
[ 1236.609536] <TASK>
[ 1236.609545] __dump_stack+0x21/0x60
[ 1236.609562] dump_stack_lvl+0xc2/0x100
[ 1236.609573] print_address_description+0x77/0x200
[ 1236.609587] ? ib_cq_poll_work+0xd0/0x1a0
[ 1236.609597] print_report+0x58/0x70
[ 1236.609607] kasan_report+0x117/0x150
[ 1236.609623] ? ib_cq_poll_work+0xd0/0x1a0
[ 1236.609636] ? process_scheduled_works+0x954/0x1600
[ 1236.609650] ib_cq_poll_work+0xd0/0x1a0
[ 1236.609662] ? process_scheduled_works+0x954/0x1600
[ 1236.609674] process_scheduled_works+0xc22/0x1600
[ 1236.609698] ? __pfx_process_scheduled_works+0x10/0x10
[ 1236.609713] ? __pfx_assign_work+0x10/0x10
[ 1236.609726] ? lock_is_held_type+0x7b/0x110
[ 1236.609741] worker_thread+0x975/0xee0
[ 1236.609757] ? __pfx_do_raw_spin_lock+0x10/0x10
[ 1236.609775] ? __kthread_parkme+0x21e/0x260
[ 1236.609789] kthread+0x3a6/0x490
[ 1236.609800] ? __pfx_worker_thread+0x10/0x10
[ 1236.609809] ? __pfx_kthread+0x10/0x10
[ 1236.609820] ret_from_fork+0x55a/0xa20
[ 1236.609835] ? __pfx_ret_from_fork+0x10/0x10
[ 1236.609850] ? __pfx_kthread+0x10/0x10
[ 1236.609861] ret_from_fork_asm+0x1a/0x30
[ 1236.609880] </TASK>
[ 1236.609886]
[ 1236.661292] Allocated by task 5076:
[ 1236.662640] kasan_save_track+0x3e/0x80
[ 1236.663842] __kasan_kmalloc+0x72/0x90
[ 1236.664763] __kmalloc_noprof+0x2b0/0x5d0
[ 1236.665356] __ib_alloc_cq+0x284/0x1000
[ 1236.666573] __ib_alloc_cq_any+0x23e/0x340
[ 1236.668654] smbdirect_connection_create_qp+0x6f7/0x1070
[ 1236.669757] smbdirect_accept_connect_request+0x500/0x1ca0
[ 1236.672625] smbdirect_listen_rdma_event_handler+0x1655/0x1c50
[ 1236.673930] cma_listen_handler+0x1bf/0x260
[ 1236.674923] cma_cm_event_handler+0x128/0x380
[ 1236.676926] cma_ib_req_handler+0x2d3d/0x4de0
[ 1236.678368] cm_process_work+0xb0/0x530
[ 1236.680454] cm_queue_work_unlock+0xb1/0x230
[ 1236.681673] cm_work_handler+0x969f/0xdca0
[ 1236.682704] process_scheduled_works+0xc22/0x1600
[ 1236.683447] worker_thread+0x975/0xee0
[ 1236.685901] kthread+0x3a6/0x490
[ 1236.688164] ret_from_fork+0x55a/0xa20
[ 1236.689522] ret_from_fork_asm+0x1a/0x30
[ 1236.690073]
[ 1236.690378] Freed by task 5137:
[ 1236.692242] kasan_save_track+0x3e/0x80
[ 1236.694272] kasan_save_free_info+0x40/0x50
[ 1236.695514] __kasan_slab_free+0x3a/0x60
[ 1236.696773] kfree+0x14e/0x4e0
[ 1236.697216] ib_destroy_cq_user+0x18d/0x250
[ 1236.699817] smbdirect_connection_destroy_qp+0xf2/0x280
[ 1236.702115] smbdirect_socket_destroy_sync+0x1607/0x2720
[ 1236.704062] smbdirect_socket_release+0x140/0x280
[ 1236.705286] smb_direct_free_transport+0x3b/0x90
[ 1236.707241] __ksmbd_conn_release_work+0x99/0xf0
[ 1236.709287] process_scheduled_works+0xc22/0x1600
[ 1236.710763] worker_thread+0x975/0xee0
[ 1236.711262] kthread+0x3a6/0x490
[ 1236.711720] ret_from_fork+0x55a/0xa20
[ 1236.712232] ret_from_fork_asm+0x1a/0x30
[ 1236.712762]
[ 1236.712992] Last potentially related work creation:
[ 1236.715157] kasan_save_stack+0x3e/0x60
[ 1236.716993] kasan_record_aux_stack+0x99/0xb0
[ 1236.718864] insert_work+0xb2/0x4a0
[ 1236.720916] __queue_work+0xebb/0x1260
[ 1236.722397] queue_work_on+0x23b/0x350
[ 1236.723809] ib_cq_completion_workqueue+0xac/0x160
[ 1236.724895] rxe_cq_post+0x433/0x7c0
[ 1236.726273] rxe_receiver+0xa41/0xd0d0
[ 1236.727754] do_work+0x272/0x860
[ 1236.728896] process_scheduled_works+0xc22/0x1600
[ 1236.730026] worker_thread+0x975/0xee0
[ 1236.731499] kthread+0x3a6/0x490
[ 1236.732132] ret_from_fork+0x55a/0xa20
[ 1236.733171] ret_from_fork_asm+0x1a/0x30
[ 1236.734224]
[ 1236.734871] Second to last potentially related work creation:
[ 1236.736001] kasan_save_stack+0x3e/0x60
[ 1236.737161] kasan_record_aux_stack+0x99/0xb0
[ 1236.739074] insert_work+0xb2/0x4a0
[ 1236.740414] __queue_work+0xebb/0x1260
[ 1236.740932] queue_work_on+0x23b/0x350
[ 1236.741849] ib_cq_completion_workqueue+0xac/0x160
[ 1236.744099] rxe_cq_post+0x433/0x7c0
[ 1236.745514] rxe_receiver+0xa41/0xd0d0
[ 1236.746091] do_work+0x272/0x860
[ 1236.747187] process_scheduled_works+0xc22/0x1600
[ 1236.749060] worker_thread+0x975/0xee0
[ 1236.750224] kthread+0x3a6/0x490
[ 1236.751480] ret_from_fork+0x55a/0xa20
[ 1236.751989] ret_from_fork_asm+0x1a/0x30
[ 1236.752974]
[ 1236.753627] The buggy address belongs to the object at ffff888111865800
[ 1236.753627] which belongs to the cache kmalloc-1k of size 1024
[ 1236.757729] The buggy address is located 0 bytes inside of
[ 1236.757729] freed 1024-byte region [ffff888111865800, ffff888111865c00)
[ 1236.760816]
[ 1236.761373] The buggy address belongs to the physical page:
[ 1236.762599] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x111860
[ 1236.764306] head: order:3 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
[ 1236.765281] flags: 0x100000000000040(head|node=0|zone=2)
[ 1236.765979] page_type: f5(slab)
[ 1236.766410] raw: 0100000000000040 ffff8881000430c0 ffffea0004a7e210 ffffea0004586210
[ 1236.770418] raw: 0000000000000000 00000000000a000a 00000000f5000000 0000000000000000
[ 1236.775899] head: 0100000000000040 ffff8881000430c0 ffffea0004a7e210 ffffea0004586210
[ 1236.782823] head: 0000000000000000 00000000000a000a 00000000f5000000 0000000000000000
[ 1236.786239] head: 0100000000000003 fffffffffffffe01 00000000ffffffff 00000000ffffffff
[ 1236.790658] head: 0000000000000000 0000000000000000 00000000ffffffff 0000000000000000
[ 1236.794132] page dumped because: kasan: bad access detected
[ 1236.798301]
[ 1236.799640] Memory state around the buggy address:
[ 1236.802028] ffff888111865700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 1236.806254] ffff888111865780: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 1236.809036] >ffff888111865800: fa fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 1236.813968] ^
[ 1236.816416] ffff888111865880: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 1236.819454] ffff888111865900: fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb fb
[ 1236.823143] ==================================================================
[ 1236.830365] Disabling lock debugging due to kernel taint
[ 1236.831136] BUG: unable to handle page fault for address: ffffc90006dc8080
[ 1236.838157] #PF: supervisor read access in kernel mode
[ 1236.843686] #PF: error_code(0x0000) - not-present page
[ 1236.849393] PGD 100000067 P4D 100000067 PUD 100366067 PMD 12913e067 PTE 0
[ 1236.854156] Oops: Oops: 0000 [#1] SMP KASAN NOPTI
[ 1236.857893] CPU: 4 UID: 0 PID: 82 Comm: kworker/4:1H Tainted: G B 7.2.0-rc3-next-20260717-virtme #110 PREEMPT(lazy)
[ 1236.860893] ksmbd: smb_direct: smbdirect_connection_recv_io_refill() failed -ECONNRESET
[ 1236.864209] Tainted: [B]=BAD_PAGE
[ 1236.864220] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
[ 1236.864233] Workqueue: ib-comp-wq ib_cq_poll_work
[ 1236.878314] RIP: 0010:rxe_req_notify_cq+0x13a/0x350
[ 1236.881683] Code: 20 87 fd 4c 89 fe 48 ba 00 00 00 00 00 fc ff df 4c 8b 3e 49 83 ef 80 4c 89 f8 48 c1 e8 03 0f b6 04 10 84 c0 0f 85 9b 01 00 00 <45> 8b 2f 41 80 3c 16 00 74 18 49 89 f6 48 89 f7 e8 71 20 87 fd 4c
[ 1236.886819] ksmbd: smb_direct: smbdirect_connection_recv_io_refill() failed -ECONNRESET
[ 1236.890671] RSP: 0018:ffff88810222f920 EFLAGS: 00010046
[ 1236.890705] RAX: 0000000000000000 RBX: ffff88810222f920 RCX: ffffffff84c92863
[ 1236.901613] RDX: dffffc0000000000 RSI: ffff888120456d48 RDI: ffff888120456d48
[ 1236.903979] RBP: ffff88810222fa20 R08: 0000000000000003 R09: 0000000000000004
[ 1236.908958] R10: dffffc0000000000 R11: ffffed1020445f10 R12: ffff888120456d40
[ 1236.914473] R13: dffffc0000000000 R14: 1ffff1102408ada9 R15: ffffc90006dc8080
[ 1236.918946] FS: 0000000000000000(0000) GS:ffff88842600d000(0000) knlGS:0000000000000000
[ 1236.921600] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1236.925779] CR2: ffffc90006dc8080 CR3: 000000012b35f003 CR4: 0000000000f72ef0
[ 1236.926809] ksmbd: smb_direct: smbdirect_connection_recv_io_refill() failed -ECONNRESET
[ 1236.928302] PKRU: 55555554
[ 1236.928328] Call Trace:
[ 1236.928338] <TASK>
[ 1236.928352] ? ib_cq_poll_work+0xd0/0x1a0
[ 1236.928374] ? __pfx_rxe_req_notify_cq+0x10/0x10
[ 1236.941601] ? ib_cq_poll_work+0xd0/0x1a0
[ 1236.943306] ib_cq_poll_work+0xfe/0x1a0
[ 1236.943961] ? process_scheduled_works+0x954/0x1600
[ 1236.947036] process_scheduled_works+0xc22/0x1600
[ 1236.951626] ? __pfx_process_scheduled_works+0x10/0x10
[ 1236.954316] ? __pfx_assign_work+0x10/0x10
[ 1236.958110] ? lock_is_held_type+0x7b/0x110
[ 1236.960042] worker_thread+0x975/0xee0
[ 1236.962668] ? __pfx_do_raw_spin_lock+0x10/0x10
[ 1236.965334] ? __kthread_parkme+0x21e/0x260
[ 1236.966058] kthread+0x3a6/0x490
[ 1236.968115] ? __pfx_worker_thread+0x10/0x10
[ 1236.971020] ? __pfx_kthread+0x10/0x10
[ 1236.974488] ret_from_fork+0x55a/0xa20
[ 1236.977419] ? __pfx_ret_from_fork+0x10/0x10
[ 1236.979846] ? __pfx_kthread+0x10/0x10
[ 1236.981238] ret_from_fork_asm+0x1a/0x30
[ 1236.984086] </TASK>
[ 1236.986181] Modules linked in:
[ 1236.989048] CR2: ffffc90006dc8080
[ 1236.990412] ---[ end trace 0000000000000000 ]---
[ 1236.994119] RIP: 0010:rxe_req_notify_cq+0x13a/0x350
[ 1236.998482] Code: 20 87 fd 4c 89 fe 48 ba 00 00 00 00 00 fc ff df 4c 8b 3e 49 83 ef 80 4c 89 f8 48 c1 e8 03 0f b6 04 10 84 c0 0f 85 9b 01 00 00 <45> 8b 2f 41 80 3c 16 00 74 18 49 89 f6 48 89 f7 e8 71 20 87 fd 4c
[ 1237.006635] RSP: 0018:ffff88810222f920 EFLAGS: 00010046
[ 1237.008513] RAX: 0000000000000000 RBX: ffff88810222f920 RCX: ffffffff84c92863
[ 1237.014100] RDX: dffffc0000000000 RSI: ffff888120456d48 RDI: ffff888120456d48
[ 1237.021058] RBP: ffff88810222fa20 R08: 0000000000000003 R09: 0000000000000004
[ 1237.024939] R10: dffffc0000000000 R11: ffffed1020445f10 R12: ffff888120456d40
[ 1237.030203] R13: dffffc0000000000 R14: 1ffff1102408ada9 R15: ffffc90006dc8080
[ 1237.034299] FS: 0000000000000000(0000) GS:ffff88842600d000(0000) knlGS:0000000000000000
[ 1237.037311] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
[ 1237.042838] CR2: ffffc90006dc8080 CR3: 000000012b35f003 CR4: 0000000000f72ef0
Found with ksmbdzzer [2], a KSMBD fuzzer that drives libFuzzer with a
kcov-dataflow [1] coverage vector: it folds each instrumented
comparison/argument's runtime operand value together with its PC (the
default arm mixes them as pc⊕val) so that a new operand value at a known
site counts as new coverage.
[1] https://lwn.net/Articles/1077606/
[2] https://github.com/yskzalloc/kcov-dataflow
Fixes: 6073eb3e3175 ("smb: smbdirect: introduce smbdirect_connection_{create,destroy}_qp()")
Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
Acked-by: Stefan Metzmacher <metze@samba.org>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
ksmbd_alloc_user() copies resp->hash_sz bytes out of the mountd IPC
login response with
user->passkey_sz = resp->hash_sz;
user->passkey = kmalloc(resp->hash_sz, KSMBD_DEFAULT_GFP);
if (user->passkey)
memcpy(user->passkey, resp->hash, resp->hash_sz);
resp->hash_sz is a __u16 supplied by the response, but resp->hash[] is
only KSMBD_REQ_MAX_HASH_SZ bytes. A malformed or malicious login
response can set hash_sz well beyond that (up to 65535), so the memcpy()
reads past the end of the response object. ipc_validate_msg() does not
bound hash_sz, so reject any response whose hash_sz exceeds the on-stack
hash[] buffer before allocating and copying.
[ 2030.238706] BUG: KASAN: slab-out-of-bounds in ksmbd_alloc_user+0x278/0x680
[ 2030.240549] Read of size 65535 at addr ffff888121bb6680 by task kworker/4:1/18611
[ 2030.242296]
[ 2030.242710] CPU: 4 UID: 0 PID: 18611 Comm: kworker/4:1 Not tainted 7.1.0-next-20260623-virtme #96 PREEMPT(lazy)
[ 2030.242732] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.17.0-debian-1.17.0-1 04/01/2014
[ 2030.242743] Workqueue: ksmbd-io handle_ksmbd_work
[ 2030.242763] Call Trace:
[ 2030.242769] <TASK>
[ 2030.242776] dump_stack_lvl+0xa2/0xd0
[ 2030.242794] print_address_description+0x77/0x200
[ 2030.242815] ? ksmbd_alloc_user+0x278/0x680
[ 2030.242831] print_report+0x58/0x70
[ 2030.242848] kasan_report+0x117/0x150
[ 2030.242869] ? ksmbd_alloc_user+0x278/0x680
[ 2030.242888] kasan_check_range+0x3c7/0x3f0
[ 2030.242908] ? ksmbd_alloc_user+0x278/0x680
[ 2030.242925] __asan_memcpy+0x29/0x70
[ 2030.242942] ksmbd_alloc_user+0x278/0x680
[ 2030.242960] ksmbd_login_user+0xc3/0x120
[ 2030.242978] ntlm_authenticate+0x5e6/0x1b00
[ 2030.243017] ? __pfx_ntlm_authenticate+0x10/0x10
[ 2030.243035] ? ksmbd_session_lookup+0x188/0x1d0
[ 2030.243054] ? __pfx_ksmbd_session_lookup+0x10/0x10
[ 2030.243090] ? __sanitizer_cov_trace_switch+0x7b/0x140
[ 2030.243108] smb2_sess_setup+0x1e4a/0x27b0
[ 2030.243126] ? copy_from_kernel_nofault+0x199/0x300
[ 2030.243156] ? __pfx_smb2_sess_setup+0x10/0x10
[ 2030.243173] ? get_smb2_cmd_val+0xe3/0x1c0
[ 2030.243208] handle_ksmbd_work+0x954/0x1280
[ 2030.243230] ? __pfx_handle_ksmbd_work+0x10/0x10
[ 2030.243249] ? process_scheduled_works+0xa07/0x1490
[ 2030.243270] ? process_scheduled_works+0xa07/0x1490
[ 2030.243291] process_scheduled_works+0xa70/0x1490
[ 2030.243320] ? __pfx_process_scheduled_works+0x10/0x10
[ 2030.243340] ? do_raw_spin_lock+0x130/0x300
[ 2030.243358] ? lock_is_held_type+0x7b/0x110
[ 2030.243388] worker_thread+0x932/0xe20
[ 2030.243415] kthread+0x38a/0x470
[ 2030.243431] ? __pfx_worker_thread+0x10/0x10
[ 2030.243451] ? __pfx_kthread+0x10/0x10
[ 2030.243467] ret_from_fork+0x484/0x910
[ 2030.243485] ? __pfx_ret_from_fork+0x10/0x10
[ 2030.243501] ? __switch_to+0xc77/0x12c0
[ 2030.243523] ? __pfx_kthread+0x10/0x10
[ 2030.243540] ret_from_fork_asm+0x1a/0x30
[ 2030.243564] </TASK>
[ 2030.243570]
[ 2030.290164] Allocated by task 19279:
[ 2030.290911] kasan_save_track+0x3e/0x80
[ 2030.292179] __kasan_kmalloc+0x72/0x90
[ 2030.293217] __kvmalloc_node_noprof+0x3ff/0x6b0
[ 2030.294467] handle_generic_event+0x59b/0x750
[ 2030.295345] genl_family_rcv_msg_doit+0x238/0x340
[ 2030.296553] genl_rcv_msg+0x606/0x7b0
[ 2030.297129] netlink_rcv_skb+0x22b/0x4a0
[ 2030.298500] genl_rcv+0x2d/0x40
[ 2030.299273] netlink_unicast+0x7ba/0x930
[ 2030.300019] netlink_sendmsg+0x8c3/0xb00
[ 2030.301073] __sock_sendmsg+0xec/0x140
[ 2030.301579] __sys_sendto+0x357/0x470
[ 2030.302255] __x64_sys_sendto+0xe3/0x100
[ 2030.303425] do_syscall_64+0x135/0x460
[ 2030.304763] entry_SYSCALL_64_after_hwframe+0x77/0x7f
[ 2030.305594]
[ 2030.305819] The buggy address belongs to the object at ffff888121bb6640
[ 2030.305819] which belongs to the cache kmalloc-192 of size 192
[ 2030.309595] The buggy address is located 64 bytes inside of
[ 2030.309595] allocated 166-byte region [ffff888121bb6640, ffff888121bb66e6)
[ 2030.312484]
[ 2030.312719] The buggy address belongs to the physical page:
[ 2030.314315] page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x121bb6
[ 2030.316481] head: order:1 mapcount:0 entire_mapcount:0 nr_pages_mapped:0 pincount:0
[ 2030.318248] flags: 0x100000000000040(head|node=0|zone=2)
[ 2030.319662] page_type: f5(slab)
[ 2030.320242] raw: 0100000000000040 ffff8881000424c0 ffffea00047c1510 ffff888100040468
[ 2030.321911] raw: 0000000000000000 0000000000150015 00000000f5000000 0000000000000000
[ 2030.324413] head: 0100000000000040 ffff8881000424c0 ffffea00047c1510 ffff888100040468
[ 2030.326150] head: 0000000000000000 0000000000150015 00000000f5000000 0000000000000000
[ 2030.327960] head: 0100000000000001 ffffffffffffff81 00000000ffffffff 00000000ffffffff
[ 2030.329615] head: ffff888121bb7ab0 0000000000000000 00000000ffffffff 0000000000000000
[ 2030.331861] page dumped because: kasan: bad access detected
[ 2030.332946]
[ 2030.333502] Memory state around the buggy address:
[ 2030.334698] ffff888121bb6580: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 2030.336475] ffff888121bb6600: fc fc fc fc fc fc fc fc 00 00 00 00 00 00 00 00
[ 2030.338143] >ffff888121bb6680: 00 00 00 00 00 00 00 00 00 00 00 00 06 fc fc fc
[ 2030.339116] ^
[ 2030.341315] ffff888121bb6700: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
[ 2030.342801] ffff888121bb6780: fc fc fc fc fc fc fc fc fa fb fb fb fb fb fb fb
[ 2030.344643] ==================================================================
Found with ksmbdzzer [2], a KSMBD fuzzer that drives libFuzzer with a
kcov-dataflow [1] coverage vector: it folds each instrumented
comparison/argument's runtime operand value together with its PC (the
default arm mixes them as pc⊕val) so that a new operand value at a known
site counts as new coverage.
[1] https://lwn.net/Articles/1077606/
[2] https://github.com/yskzalloc/kcov-dataflow
Fixes: e2f34481b24d ("cifsd: add server-side procedures for SMB3")
Signed-off-by: Yunseong Kim <yunseong.kim@est.tech>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
ksmbd tracked only credit counts (total_credits/outstanding_credits) and
never validated the MessageId of an incoming request. As a result a
request carrying a MessageId outside the granted range was accepted, a
MessageId could be replayed, and a 64-bit sequence wrap was not detected.
Maintain a command sequence window per connection:
- [seq_low, seq_high) is the range of granted sequence numbers and
seq_bitmap records which of them have been granted but not yet
consumed. The window starts as { 0 } at connection setup.
- smb2_set_rsp_credits() extends seq_high by the number of credits it
grants (setting the corresponding bits), capped so the window never
spans more than KSMBD_CMD_SEQ_WINDOW (== SMB2_MAX_CREDITS) sequence
numbers. This implements the "limit the range of acceptable
sequence numbers" allowance and keeps seq_bitmap usable as a ring.
- smb2_check_sequence_number(), run for every SMB2 request from
ksmbd_smb2_check_message(), verifies that the CreditCharge
consecutive sequence numbers starting at MessageId lie within the
window and have not already been consumed, then removes them and
slides seq_low forward. CANCEL consumes nothing. A violation
(out of window, replay, or wrap) tears the connection down.
The legacy SMB1 multi-protocol negotiate occupies sequence number 0 but
does not pass through ksmbd_smb2_check_message(), so it consumes that
sequence number explicitly; otherwise seq_low would stay pinned at 0
after the upgrade to SMB2 and eventually stall credit grants.
For an in-order client seq_high - seq_low equals total_credits, so the
window-room cap never reduces the number of credits granted. it only
engages for a client that withholds low sequence numbers.
init_smb2_max_credits() now clamps the configured maximum to
SMB2_MAX_CREDITS so the window (and its bitmap) can always represent
every outstanding sequence number.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
release_async_work() zeroes work->async_id before the CANCELLED path
calls smb2_send_interim_resp(work, STATUS_CANCELLED), which reads
work->async_id to build the response's AsyncId field. The cancellation
response for a cancelled blocked-lock request is sent with AsyncId=0
instead of the id the client received in the original STATUS_PENDING
response for this request.
Checked against every other release_async_work() call site in this
file: smb2_read()/smb2_write() don't send a further async response
afterward (their status goes out on the synchronous path instead), and
smb2_notify()'s two async paths already transfer the id to a separate
struct before releasing, so this reordering is scoped to smb2_lock()
only.
Send the STATUS_CANCELLED response while work->async_id is still valid,
then release the async work afterward.
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
smb2_open()'s maximal-access handling sets maximal_access to the
FILE_MAXIMAL_ACCESS_LE request sentinel, then calls
smb_check_perm_dacl() to compute the real access mask from the
object's DACL.
smb_check_perm_dacl() returns success without touching *pdaccess when
the object has no stored NT ACL xattr (ksmbd_vfs_get_sd_xattr() fails,
taking an early goto err_out with rc still 0). This leaves
maximal_access holding the raw FILE_MAXIMAL_ACCESS_LE sentinel instead
of a real access mask.
Observed live: a freshly-created share root shows macOS's "no entry"
(prohibited-access) badge on connect, even though POSIX permissions
clearly allow access -- macOS requests maximal access via the MxAc
create context on every share-root open, not via DesiredAccess, so it
trusts the leaked sentinel verbatim instead of falling through to the
correct POSIX-based path.
Fall back to ksmbd_vfs_query_maximal_access() -- the same POSIX-based
computation already used for the DesiredAccess-requested-maximal-access
case below -- whenever the sentinel comes back unmodified.
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
ksmbd durable handles are currently in-memory state. There is no persistent
open recovery, cluster ownership epoch, fencing, or failover implementation
behind the continuous-availability share flag.
Do not advertise SMB2 persistent-handle or continuous-availability
capabilities until those guarantees exist. A client requesting DH2Q then
falls back to the existing durable V2 behavior rather than being promised a
persistent handle that cannot survive a server failure.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Correct the durable-handle V2 response context layout and use the V2
context size when chaining a following CREATE response context. Validate
the only defined DH2Q/DH2C flag, require the reconnect request type to
match the saved open type, and process the application instance identifier
before durable V2 state.
Persistent opens are durable opens as required by MS-SMB2. Permit the
durable reconnect path to rebind either type of disconnected open.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
The SMB2 NEGOTIATE request's SMB2_NEGOTIATE_SIGNING_REQUIRED bit requires
the server to set Connection.ShouldSign. KSMBD represents that state with
conn->sign, but previously set it only when its signing configuration was
auto or disabled.
Set conn->sign whenever the client requires signing, independently of the
server's signing mode. Keep the mandatory server-mode handling unchanged.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
The server proc entry does not expose configured limits or enough outcome
data to distinguish protocol errors from transport stalls.
Report the server state, listener and signing configuration, connection
limits, timeout values, current client and open-file totals, IPC activity,
and durable scavenger state. Classify processed SMB2 response statuses by
NTSTATUS severity and provide counters for common error groups while
retaining the per-command counters.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
There is no kernel-side view of the share configurations currently cached
by active tree connections.
Add a shares proc entry that reports each active share name, type,
tree-connection count, create masks, and descriptive configuration flags.
Maintain a per-share tree-connection counter with the existing global
counter so the value can be read without walking every session.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Session and file proc entries lack the state needed to correlate inactive
sessions with durable or delete-pending opens.
Add the account type, dialect, idle time, open-file count, tree-connect
count, and per-channel POSIX negotiation state to session entries. Extend
the open-file table with the file state, durable timeout, create options,
share access, and descriptive flags for durable, persistent, resilient,
delete-on-close, stream, POSIX, and attribute-only opens.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
The clients proc file currently shows only a small subset of the state
needed to diagnose stalled or mis-negotiated connections.
Report the transport, connection state, outstanding and total credits,
session count, lifetime request count, and negotiated signing, encryption,
compression, and POSIX features. Report each connection as a key/value
record rather than a wide fixed-width table.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
The ksmbd procfs monitoring files produce misleading or malformed output.
The constant-name helper uses a bitwise test for enum values. This omits
zero-valued constants and can print multiple names for one lease state. It
also unconditionally emits a newline, splitting entries in the open-file
table across two lines. Session capabilities are printed as numeric flag
values even though a table of descriptive names is available.
Use exact matching for enum values. Print flag names as a comma-separated
list, preserving unknown bits as hexadecimal values. Let callers control
line termination so each open-file entry remains on one line. Print common
session properties once, and report signing and encryption independently.
Adjust client and open-file column widths for IPv6 addresses and 64-bit
file IDs, and fix the misspelled OPLOCK_EXCLUSIVE name. Also expose and
maintain the total request count alongside the per-command counters.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
SMB3 clients can replay selected requests after a channel disconnect by
setting SMB2_FLAGS_REPLAY_OPERATION. The command sequence window rejects
duplicate MessageIds on one connection, but it does not validate requests
resent on another channel with a new MessageId.
Add the state and validation required to replay durable CREATE and
file-handle operations:
- track each open ChannelSequence, outstanding request counts, and lock
sequence entries.
- retain a request-owned open reference until the common response path
completes ChannelSequence accounting.
- replay DurableHandleReqV2 CREATE requests by CreateGuid, validating the
durable state, SecurityContext, session, lease key, and persistent flag.
- publish CreateGuid and SecurityContext before an oplock or lease break
can defer CREATE, rejecting replays of that pending CREATE with
STATUS_FILE_NOT_AVAILABLE.
- retain the original CREATE action and replay completed CreateGuid
requests, including requests that did not receive a durable-handle grant,
without modifying the existing open.
- make replayed oplock and lease break acknowledgements idempotent. And
- preserve SMB2_FLAGS_REPLAY_OPERATION in responses.
Return STATUS_FILE_NOT_AVAILABLE when ChannelSequence validation rejects a
replayed WRITE, IOCTL, or SET_INFO request.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Deferred CHANGE_NOTIFY work keeps an async message ID after the original
request work is released. A durable handle can outlive its connection, so
the connection teardown can destroy its async IDA before the handle close
releases the pending notify work.
Give the synthetic deferred work a connection reference. Release it after
the async ID in ksmbd_free_work_struct(). This keeps the async IDA alive
until the deferred work is released, even when the original connection has
already left the connection list.
During server shutdown there is no client to receive a cleanup response.
Skip the write and only release the pending work.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
An existing file can be opened with a truncating create request that
supplies FileAttributes. Do not reset its cached DOS attributes while
opening it. After a successful truncation, apply the requested attributes
and store them in the DOS attribute xattr.
This preserves READONLY when a truncating open requests that attribute.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_CREATE_OR_GET_OBJECT_ID requires a fixed-size output buffer, but an
invalid file handle must take precedence over output buffer validation.
Look up the handle before checking the available response buffer size. This
returns STATUS_FILE_CLOSED for a closed handle while preserving the buffer
size validation for valid handles.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
A base file that has been marked for deletion remains present while stream
handles are open. Name-based opens of either the base file or one of its
streams must return STATUS_DELETE_PENDING during that interval.
ksmbd_inode_pending_delete() returned only the per-handle stream state for
stream handles. It therefore skipped the inode-wide S_DEL_PENDING state set
by the base file delete-on-close path. As a result, a new stream open
incorrectly succeeded.
Check the inode-wide pending-delete state first for every handle. Only when
the base file is not pending, check the per-handle stream state.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Break read and handle caching leases before entering the VFS rename path.
This keeps the destination name hidden until the lease holder acknowledges
the break.
Send the break synchronously before returning STATUS_PENDING for a rename.
This avoids a race between the interim response and notification handling.
Keep the existing asynchronous notification flow for all other lease break
paths so chained breaks retain their ordering.
Use the connection which owns the open for the notification. A lease table
is shared by connections using the same client GUID. Its saved connection
may belong to another active channel. Use it only when the owning channel
is being released.
Check directory sharing before issuing a break to avoid unnecessary lease
breaks for a rename that must fail with a sharing violation.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
RH leases map to SMB2_OPLOCK_LEVEL_II because they do not include write
caching. smb_grant_oplock() only sent break notifications for previous
BATCH or EXCLUSIVE levels, so a conflicting open could skip the lease
break when the existing lease was RH.
That leaves the opener to fail or complete without the expected pending
lease break sequence, instead of first asking the holder to drop handle
caching.
Treat share-mode conflicts against leases with HANDLE_CACHING as needing
a break even when the mapped oplock level is LEVEL_II. This lets the
server send the RH -> R lease break and wait for the normal break
handling before continuing the conflicting open.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_QUERY_NETWORK_INTERFACE_INFO is not tied to an open file handle.
Clients send SMB2_NO_FID for both file id fields when issuing this
request.
Reject requests that provide any other file id before checking the
output buffer size. This returns STATUS_INVALID_PARAMETER for invalid
file ids instead of treating the request as valid or reporting
STATUS_BUFFER_TOO_SMALL for a small output buffer.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_DUPLICATE_EXTENTS_TO_FILE currently returns STATUS_NOT_SUPPORTED
when vfs_clone_file_range() cannot clone the requested range. That can
happen on filesystems without reflink support even though the server can
still satisfy the request by copying the bytes.
Validate the requested source range before attempting the operation. If
the destination range extends past EOF, leave the destination size
unchanged and complete the request without copying, matching observed
client expectations for this ioctl.
Reject sparse source to non-sparse destination requests as unsupported.
Keep sparse destination and sparse-to-sparse cases on the normal clone
or copy path.
Reject overlapping same-file ranges as unsupported before attempting
the clone or copy operation.
Return the expected handle status for invalid handles. A closed target
handle fails with STATUS_FILE_CLOSED, while a bad source handle embedded
in the request buffer fails with STATUS_INVALID_HANDLE.
Fall back to vfs_copy_file_range() whenever the clone operation does not
copy the full requested length, and keep reporting an error only if the
fallback also fails or copies a partial range.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Advertise trim support through FS_SECTOR_SIZE_INFORMATION and handle
FSCTL_FILE_LEVEL_TRIM requests.
Process each trim range by punching a hole while keeping the file size
unchanged, and report the number of ranges completed in the ioctl
response. The trim operation uses the same byte-range lock handling as
zero data for the affected part of the file.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_SET_ZERO_DATA changes file allocation state and must respect
byte-range locks over the affected part of the file. Check the requested
range, clipped to EOF, before issuing the fallocate operation.
Return STATUS_FILE_LOCK_CONFLICT when the range conflicts with an
existing lock. Ranges starting past EOF are left untouched by the lock
check so they continue to succeed.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_SET_SPARSE should not require FILE_WRITE_ATTRIBUTES only. A handle
with FILE_WRITE_DATA or FILE_APPEND_DATA is also allowed to set the file
allocation state, while FILE_WRITE_EA alone must still be rejected.
FSCTL_QUERY_ALLOCATED_RANGES needs FILE_READ_DATA access. Reject handles
that only have metadata access such as FILE_READ_ATTRIBUTES.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_QUERY_ALLOCATED_RANGES currently relies on SEEK_DATA and SEEK_HOLE
for every file. That works for files with holes, but it is not a good
match for dense files.
A zeroed range in a dense file may be represented as an unwritten extent
and skipped by SEEK_DATA. The server can then return no allocated ranges
even though the file should still be treated as allocated from the
protocol point of view.
For dense files, report the requested range clipped to EOF as allocated
instead of probing holes. Keep using SEEK_DATA and SEEK_HOLE for files
marked with FILE_ATTRIBUTE_SPARSE_FILE, and wait for writeback before
probing so punch-hole updates are visible to the filesystem seek
implementation.
This fixes the case where a query after zeroing data could return no
ranges for a dense file.
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
silenced __rpc_method()'s own "Unsupported RPC: mdssvc" log line
but missed that ksmbd_session_rpc_open() failing for that same,
now-still-rejected pipe also trips a second, separate pr_err() here in
its caller. macOS's routine mdssvc (Spotlight) probes still spam the
kernel log via this second site on every single probe, defeating the
original commit's stated purpose.
Suppress this specific case the same way the other site does; behavior
is unchanged for every other RPC failure.
__rpc_method() (mgmt/user_session.c) returns -ENOENT for mdssvc, not
-EINVAL.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
get_file_stream_info() (FileStreamInformation QUERY_INFO) reported
each enumerated stream's StreamSize/StreamAllocationSize as
stream_name_len -- the byte length of the stream's *name*, not its
data. This is the same bug class already fixed for EndOfFile/
AllocationSize on an open stream handle (ksmbd_stream_eof()), just
missed at this second site: a client enumerating streams sees a size
derived from the name string length instead of the stream's actual
content length, inconsistent with what querying the same stream by
handle reports.
Compute the real value length the same way ksmbd_stream_eof() does,
via ksmbd_vfs_casexattr_len() on the already-known xattr key.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Extends the existing V1 inline-FinderInfo mechanism
(SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR) with the V2 variant:
byte-identical layout otherwise, except the ShortNameLength+Reserved
bytes (ignored outright by V1 clients) become a single flags field that
V2 clients actually interpret.
Negotiation: when a client's own client_caps requests V2
(SMB2_CRTCTX_AAPL_SUPPORTS_READ_DIR_ATTR_V2), advertise V2 instead of
V1 in the server's own server_caps reply -- they're mutually exclusive
on the wire, not both set together.
Wire format: the only currently-defined V2 flag,
AAPL_READDIR_ATTR_V2_NO_XATTR, signals that an item has no
xattrs/streams so the client can skip a separate query. Compute this
per-entry in ksmbd_vfs_fill_dentry_attrs() by checking for any xattr
under the XATTR_NAME_STREAM ("user.DosStream.") prefix -- a reliable,
distinct marker for genuine ADS/stream xattrs, unlike DOSATTRIB or ACL
xattrs which live under different prefixes, so this can't
false-positive into telling Finder a file has no extra data when it
actually does. Only computed when a V2 connection is active, to avoid
the extra listxattr() call otherwise.
V1's fixed ShortNameLength=24 convention (real macOS clients ignore
the value outright per the same client source, so it's cosmetic
parity with other real servers, not a functional requirement) is kept
V1-only rather than reused as a V2 base value -- V2 clients do
interpret this field, so it needs a clean 0-or-flag value, not a
leftover V1 constant that happens not to collide with the one defined
flag bit today.
Confirmed via live diagnostics that macOS actually negotiates and
uses V2 (client_caps bit 0x10 set) rather than falling back to V1 or
ignoring the capability.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
fsctl_copychunk() treats FSCTL_SRV_COPYCHUNK with ChunkCount=0 as the
standard SMB2 "query my copy limits, don't copy anything" request and
returns success without ever looking up the file handles. That's
correct for compliant SMB2 clients, but macOS Finder's Cmd+D duplicate
sends ChunkCount=0 expecting the server to copy the whole file/stream
-- so duplicated files are left at their just-created 0 bytes while
the client reports success.
Scope the full-copy fallback to AAPL-negotiated connections on a
Time Machine share (conn->is_aapl && KSMBD_SHARE_FLAG_TIME_MACHINE)
only, so standard non-AAPL SMB2 clients, and AAPL-negotiated clients
on ordinary shares, keep the spec-correct query-limits behavior
unchanged.
both streams and regular files now share a single chunk_count == 0
fast path added right after src_file_size is computed, reusing the
same buffered-copy helper and vfs_copy_file_range()/COPY_FILE_SPLICE
fallback the existing per-chunk loop already uses, rather than the
separate xattr-specific get/setxattr path this used before that
rework.
ChunksWritten/ChunkBytesWritten are 0 in the response: this is a
synthesized whole-file copy, not a response to any chunk descriptor
the client actually sent (it sent none), so there's no real chunk to
report the count/size of. Only TotalBytesWritten is meaningful here.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
smb2_notify() currently returns STATUS_NOT_IMPLEMENTED synchronously
for every CHANGE_NOTIFY request. Genuine SMB2 servers never complete a
CHANGE_NOTIFY spontaneously -- it's satisfied only by a real directory
change or with STATUS_NOTIFY_CLEANUP when the watched handle is
closed. macOS smbfs.kext depends on this deferred-completion contract:
receiving STATUS_NOT_IMPLEMENTED instead makes it hard-freeze on
unmount, since it never sees the cleanup it's waiting for.
Add a notify_pendings list on struct ksmbd_file (protected by the
existing f_lock) and a notify_entry list_head on struct ksmbd_work to
link onto it. smb2_notify() now replies STATUS_PENDING immediately and
queues a deferred STATUS_NOTIFY_CLEANUP response on the watched
handle; __ksmbd_close_fd() drains and sends any pending notifications
when the handle is actually closed. The drain splices the list out
under fp->f_lock first, then processes the detached copy without the
lock -- smb2_notify() on another connection can be adding to the same
list at the same time a close happens on this one, and
ksmbd_conn_write() can sleep (it takes the connection's write mutex),
so it must not be called while the spinlock is held.
Also handle the FileId=FFFF...FFFF share-root sentinel that macOS
backupd sends to watch for changes without holding an open handle --
without an immediate STATUS_PENDING/STATUS_NOTIFY_CLEANUP reply here,
backupd aborts Time Machine setup with STATUS_FILE_CLOSED.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Without READDIR_ATTR, macOS Finder resolves type/creator/icon for
every file in a directory listing by opening its AFP_AfpInfo stream
individually -- one extra CREATE+QUERY_INFO+CLOSE round trip per file,
which is the dominant cost of browsing a large directory over SMB from
a Mac.
When the client negotiates READDIR_ATTR (conn->aapl_readdir_attr, set
during CREATE's AAPL context exchange), inline the same information
directly into each FILEID_BOTH_DIRECTORY_INFORMATION FIND entry:
EaSize = max_access, expanded specific rights
(GENERIC_ALL_FLAGS), not the raw FILE_GENERIC_ALL_LE
"generic" meta-bit -- that bit has none of the
specific FILE_* rights macOS's smbfs.kext checks
bit-by-bit, so reporting it directly would fail
every access check and show Finder's "no entry"
badge on every file/folder.
ShortNameLength = 24 (fixed; the spec says 0 when there's no short
name; kept for wire parity with reference server,
see below)
ShortName[0..7] = resource fork size (0 -- no resource forks)
ShortName[8..23] = compressed FinderInfo (all zero: type/creator
unset, client falls back to extension-based
icon/type detection, consistent with the
AFP_AfpInfo synthesis this mirrors)
Reserved2 = Unix mode bits
Reparse-point status is still carried via ExtFileAttributes rather
than EaSize once READDIR_ATTR is active, since EaSize is repurposed
for max_access.
Reverse-engineered from macOS smbfs.kext network behavior and cross-checked
against reference implementation marshalling (reference implementation
behavior). Also confirmed against AAPL's published public client behavior
(public client behavior reference) -- every field here matches exactly,
except ShortNameLength=24: real V1 clients read but never examine that
field, so it's kept for wire parity with reference server, not because
macOS requires it.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Once a server advertises the AAPL COPYFILE capability, macOS requires
an AFP_AfpInfo stream on every file it looks at for Finder type/
creator/icon resolution. smb2_set_stream_name_xattr() currently
returns -EBADF (STATUS_OBJECT_NAME_NOT_FOUND) when a client opens
AFP_AfpInfo with FILE_OPEN disposition and the xattr doesn't exist
yet, which macOS treats as fatal for that file: Finder falls back to
showing a generic icon, and file operations that depend on succeeding
against this stream (e.g. Cmd+D duplication) fail.
Synthesize a 60-byte zeroed AFP_AfpInfo xattr (magic 0x00051607,
version 0x00020000, both big-endian per the AFP_AfpInfo wire format)
on first FILE_OPEN probe instead. type=0/creator=0 tells macOS to fall
back to extension-based type detection, which is correct for files with
no explicit Finder metadata. The synthesized xattr persists on disk, so
this only pays the extra write once per file; a later genuine write from
macOS (e.g. after the user assigns a custom icon) overwrites it normally.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
macOS clients (Finder, and specifically Time Machine's backupd) send
an "AAPL" SMB2 create context on CREATE to negotiate AAPL-specific
server capabilities (server_caps/vol_caps/model string). Without a
response to this context, macOS Time Machine over SMB does not work
at all.
Add the AAPL create context structs (create_aapl_rsp,
aapl_server_query_req) and create_aapl_rsp_buf(), which builds the
kAAPL_SERVER_QUERY response mirroring the layout observed from macOS's
own smbd, including the model string workaround: omitting the model
string when the client requested it causes smbfs.kext to enter a
broken disconnect path requiring a full macOS reboot to recover from.
Command codes and bitmap values reuse the existing SMB2_CRTCTX_AAPL_*
constants in fs/smb/common/smb2pdu.h.
Wire format confirmed against AAPL's published public client kernel
source (public client behavior reference) -- every field
here and every SMB2_CRTCTX_AAPL_* constant matches exactly.
Hook the request parsing and response into smb2_open()'s existing
create-context handling, following the same DataOffset+DataLength
bounds-checking convention already used by every other context parser
in this file. The AAPL model string is configurable via the existing
netlink startup path (server_conf.aapl_model, default "Xserve").
This is scoped to shares with the new KSMBD_SHARE_FLAG_TIME_MACHINE
flag only, not enabled globally -- AAPL's AAPL extension is
undocumented, so containing its blast radius to shares that explicitly
opt in limits risk to ordinary SMB shares.
conn->aapl_readdir_attr is set here when the client also advertises
READDIR_ATTR support, but the actual inline-FinderInfo wire format
(the feature that flag gates) is not implemented yet -- follow-up
commit.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
Free it unconditionally after ksmbd_alloc_user() calls.
kmemleak splat:
unreferenced object 0xffff888103b83540 (size 192):
comm "pool-0", pid 16970, jiffies 4377290937
hex dump (first 32 bytes):
00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 ................
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
backtrace (crc 408ccc66):
__kvmalloc_node_noprof+0x730/0x920
handle_generic_event+0xec/0x1a0 [ksmbd]
genl_family_rcv_msg_doit+0xe0/0x130
genl_rcv_msg+0x181/0x290
netlink_rcv_skb+0x4f/0x100
genl_rcv+0x28/0x40
netlink_unicast+0x1e6/0x2c0
netlink_sendmsg+0x20a/0x450
____sys_sendmsg+0x2e8/0x310
___sys_sendmsg+0x78/0xc0
__sys_sendmsg+0x63/0xc0
do_syscall_64+0xa1/0x670
entry_SYSCALL_64_after_hwframe+0x76/0x7e
Fixes: a77e0e02af1c ("ksmbd: add support for supplementary groups")
Signed-off-by: Enzo Matsumiya <ematsumiya@suse.de>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
smb2_open() calls vfs_fallocate(fp->filp, ...) unconditionally when a
client's CREATE request includes an AllocationSize create context.
For a stream handle, fp->filp refers to the base file's data fork
(streams are xattr-backed on the same underlying file, not separate
files), so this pre-allocates storage on the base file's actual data
instead of doing anything meaningful for the stream -- fallocate has
no applicability to an xattr-backed stream at all.
Skip the fallocate call for stream handles.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
set_file_disposition_info() checks S_ISDIR(inode->i_mode) &&
ksmbd_vfs_empty_dir(fp) == -ENOTEMPTY to refuse deleting a non-empty
directory. A stream handle's fp->filp refers to the same underlying
inode as its base file or directory (streams are xattr-backed on that
same inode), so this check also fires when the target is actually a
stream attached to a directory, not the directory itself -- deleting
the stream then incorrectly fails with -EBUSY whenever the directory
happens to be non-empty, even though removing an xattr has nothing to
do with the directory's contents.
Skip the directory-emptiness check for stream handles, matching how
ksmbd_stream_fd() is already used elsewhere in this function.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
ndr_decode_dos_attr() only populates da->itime for version-4 DOS
attribute xattrs; for version 3 it's skipped entirely (only
da->create_time is set). smb2_update_xattrs() declared da without
initializing it, so fp->itime = da.itime unconditionally copies
whatever was on the kernel stack for any file carrying a version-3
xattr (e.g. written by an older client or server) -- uninitialized
stack memory that can later be exposed to a client via QUERY_INFO.
Zero-initialize da at declaration, matching the pattern
fsctl_set_sparse() already uses in this same file.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
FSCTL_GET_REPARSE_POINT writes
Both cases write a fixed-size response structure into rsp->Buffer
without first checking that out_buf_len (the space smb2_ioctl()
actually has available, computed by smb2_calc_max_out_buf_len() from
the client's OutputBufferLength minus space already consumed earlier
in a compound request) is large enough. Every comparable case in this
same switch (FSCTL_SRV_ENUMERATE_SNAPSHOTS, FSCTL_GET_COMPRESSION,
FSCTL_VALIDATE_NEGOTIATE_INFO, FSCTL_SRV_REQUEST_RESUME_KEY,
FSCTL_SRV_COPYCHUNK) validates this first; these two don't.
A client can send a compound SMB2 request where an earlier command in
the same compound chain consumes most of work->response_buf, leaving
smb2_calc_max_out_buf_len() only a few bytes of out_buf_len for a
trailing FSCTL_CREATE_OR_GET_OBJECT_ID or FSCTL_GET_REPARSE_POINT.
Both then unconditionally write their full fixed-size structure
(64 bytes and 8 bytes respectively) at rsp->Buffer[0] regardless,
overflowing past the actual remaining space in the response buffer.
Add the same out_buf_len check used by every other fixed-size-response
case in this function, before the write.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
When a client's Durable Handle Request V2 sets Timeout=0 ("let the
server choose"), fp->durable_timeout was set to 60. Every other use
of this field is in milliseconds: DURABLE_HANDLE_MAX_TIMEOUT (300000)
in smb2pdu.h, the nonzero branch immediately above
(min_t(unsigned int, dh_info.timeout, DURABLE_HANDLE_MAX_TIMEOUT),
where dh_info.timeout is the wire value and already milliseconds per
spec), and the scavenger in vfs_cache.c, which adds it directly to
jiffies_to_msecs(jiffies).
60 is off by 1000x: the handle becomes scavenger-eligible 60
milliseconds after close instead of 60 seconds. A client requesting
Timeout=0 is relying entirely on the server's default to cover the
gap between a dropped connection and its reconnect -- 60ms is not
enough time for even a fast network blip to be detected and
reconnected, so any real disruption loses the race and a subsequent
DH2C reconnect fails with a durable-handle lookup miss instead of
succeeding.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|
|
ksmbd_fd_set_delete_pending()/ksmbd_fd_clear_delete_pending()
to keep a stream's FileDispositionInformation from marking the whole
file for deletion, but used the inode-wide S_DEL_ON_CLS_STREAM flag to
do it -- the exact same problem class the commit was fixing, one level
up.
S_DEL_ON_CLS_STREAM lives on the shared ksmbd_inode, not on any
specific stream handle. If a file has multiple stream handles open and
one gets marked delete-pending via FileDispositionInformation, the
flag can't record *which* stream should be deleted: whichever stream
handle happens to close first (not necessarily the one that was
actually marked) sees S_DEL_ON_CLS_STREAM set and has its xattr
removed. Two clients (or two handles from the same client) touching
different streams on the same file can end up deleting the wrong one.
ksmbd_inode_pending_delete() has the same issue: it only checks
S_DEL_PENDING, which is never set for a stream handle, so a client
querying FileStandardInformation.DeletePending on a stream marked via
this path would incorrectly see 0.
Track this per-handle instead (stream_del_pending on struct
ksmbd_file), matching the file itself rather than the shared inode.
ksmbd_fd_set_delete_on_close() (the CREATE-time FILE_DELETE_ON_CLOSE
option, a separate call path from FileDispositionInformation) still
uses the inode-wide flag; __ksmbd_inode_close() now checks both, since
either one should trigger removing the stream's xattr on close.
Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Gael Blivet <gael.blivet@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
|