ksmbd: fix use-after-free of a deferred file_lock on double SMB2_CANCEL
A deferred byte-range lock (an SMB2_LOCK that blocks) registers an async work on
conn->async_requests via setup_async_work(), with cancel_fn =
smb2_remove_blocked_lock and cancel_argv[0] pointing at the struct file_lock.
When the request is cancelled, the worker frees the file_lock with
locks_free_lock() and takes the cancelled early-exit, which "goto out"s and never
reaches release_async_work() -- the only site that unlinks the work from
conn->async_requests and clears cancel_fn/cancel_argv. The work therefore stays
matchable on async_requests with a live cancel_fn pointing at the freed file_lock,
until connection teardown finally runs release_async_work().
smb2_cancel() fires cancel_fn unconditionally with no state guard, so a second
SMB2_CANCEL for the same AsyncId, arriving in that window, re-runs
smb2_remove_blocked_lock() on the freed file_lock -- a slab use-after-free:
BUG: KASAN: slab-use-after-free in __locks_delete_block
__locks_delete_block
locks_delete_block
ksmbd_vfs_posix_lock_unblock
smb2_remove_blocked_lock
smb2_cancel <- 2nd SMB2_CANCEL fires cancel_fn
handle_ksmbd_work
Allocated by ...: locks_alloc_lock <- smb2_lock
Freed by ...: locks_free_lock <- smb2_lock (cancelled branch)
... cache file_lock_cache of size 192
Reproduced on mainline with KASAN by an authenticated SMB client.
Skip a work whose state is already KSMBD_WORK_CANCELLED so its cancel callback
cannot be fired a second time.
Cc: stable@vger.kernel.org
Signed-off-by: Gil Portnoy <dddhkts1@gmail.com>
Acked-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: Steve French <stfrench@microsoft.com>
master
parent
7ce4fc4001
commit
f580d27e89
|
|
@ -7322,6 +7322,17 @@ int smb2_cancel(struct ksmbd_work *work)
|
|||
le64_to_cpu(hdr->Id.AsyncId))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* A cancelled deferred byte-range lock frees its
|
||||
* file_lock and takes the smb2_lock() early-exit that
|
||||
* skips release_async_work(), so the work stays on
|
||||
* conn->async_requests with a live cancel_fn pointing
|
||||
* at the freed file_lock. Re-firing it on a second
|
||||
* SMB2_CANCEL is a use-after-free.
|
||||
*/
|
||||
if (iter->state == KSMBD_WORK_CANCELLED)
|
||||
break;
|
||||
|
||||
ksmbd_debug(SMB,
|
||||
"smb2 with AsyncId %llu cancelled command = 0x%x\n",
|
||||
le64_to_cpu(hdr->Id.AsyncId),
|
||||
|
|
|
|||
Loading…
Reference in New Issue