NACK: [SRU][J][PATCH 1/1] ksmbd: fix use-after-free in __smb2_lease_break_noti()
Andrei Gherzan
andrei.gherzan at canonical.com
Mon Sep 14 23:48:01 UTC 2026
On 26/09/10 12:35AM, Cengiz Can via kernel-team wrote:
> From: Namjae Jeon <linkinjeon at kernel.org>
>
> Move tcp_transport free to ksmbd_conn_free. If ksmbd connection is
> referenced when ksmbd server thread terminates, It will not be freed,
> but conn->tcp_transport is freed. __smb2_lease_break_noti can be performed
> asynchronously when the connection is disconnected. __smb2_lease_break_noti
> calls ksmbd_conn_write, which can cause use-after-free
> when conn->ksmbd_transport is already freed.
>
> Cc: stable at vger.kernel.org
> Reported-by: Norbert Szetei <norbert at doyensec.com>
> Tested-by: Norbert Szetei <norbert at doyensec.com>
> Signed-off-by: Namjae Jeon <linkinjeon at kernel.org>
> Signed-off-by: Steve French <stfrench at microsoft.com>
> (backported from commit 21a4e47578d44c6b37c4fc4aba8ed7cc8dbb13de)
> [bot_kybele: Tree already carries a refactor (ee426bfb/b1f1e806-style) where the
> refcnt-zero final conn release lives in ksmbd_conn_put(), not in
> ksmbd_conn_free(); so ksmbd_conn_free() keeps calling ksmbd_conn_put() (plus
> this tree's extra kfree(conn->mechToken)) and the upstream
> ksmbd_free_transport(conn->transport) was placed in ksmbd_conn_put()'s
> atomic_dec_and_test block (before kfree(conn)), the true analogue of upstream's
> ksmbd_conn_free() hunk. transport_tcp.h lacks...]
> CVE-2025-37777
> Assisted-by: kybele:claude-opus-4.8
> Signed-off-by: Cengiz Can <cengiz.can at canonical.com>
> ---
> fs/ksmbd/connection.c | 11 ++++++-----
> fs/ksmbd/transport_tcp.c | 14 +++++++++-----
> fs/ksmbd/transport_tcp.h | 1 +
> 3 files changed, 16 insertions(+), 10 deletions(-)
>
> diff --git a/fs/ksmbd/connection.c b/fs/ksmbd/connection.c
> index 76afa733351a..215248528ee0 100644
> --- a/fs/ksmbd/connection.c
> +++ b/fs/ksmbd/connection.c
> @@ -42,11 +42,11 @@ struct ksmbd_conn *ksmbd_conn_get(struct ksmbd_conn *conn)
> * ksmbd_conn_put() - drop a reference and, if it was the last, perform the
> * final, once-per-struct release of a ksmbd_conn.
> *
> - * The transport is not released here: free_transport() releases it after
> - * calling ksmbd_conn_free(). The final release therefore only pairs
> - * ida_destroy() with kfree(), reaches no sleeping teardown and is safe
> - * from any context, including the RCU callback that frees an opinfo
> - * holding a connection reference.
> + * The transport is released here rather than in free_transport() so that it
> + * lives as long as the connection it belongs to. __smb2_lease_break_noti()
> + * can run asynchronously after the connection is disconnected and dereference
> + * conn->transport via ksmbd_conn_write(); freeing the transport only on the
> + * final connection put avoids that use-after-free.
> */
> void ksmbd_conn_put(struct ksmbd_conn *conn)
> {
> @@ -54,6 +54,7 @@ void ksmbd_conn_put(struct ksmbd_conn *conn)
> return;
>
> if (atomic_dec_and_test(&conn->refcnt)) {
> + ksmbd_free_transport(conn->transport);
This introduces a double free bug. Upstream has a known bug fixed in commit
a89f5fae998b, which carries Fixes: 21a4e47578d4. It calls out the cause:
free_transport function for tcp connection can be called from smbdirect. It
will cause kernel oops.
This is particularly important for Jammy as CONFIG_SMB_SERVER_SMBDIRECT is
enabled for all supported arches.
Another issue is that this is now reachable from an RCU softirq callback, where
it may not sleep.
oplock.c:202 call_rcu(&opinfo->rcu_head, opinfo_free_rcu);
oplock.c:130 opinfo_free_rcu() →
oplock.c:122 free_opinfo() →
oplock.c:126 ksmbd_conn_put(opinfo->conn);
> ida_destroy(&conn->async_ida);
> kfree(conn);
> }
> diff --git a/fs/ksmbd/transport_tcp.c b/fs/ksmbd/transport_tcp.c
> index 28dc86dfb030..5ab7fb43cac5 100644
> --- a/fs/ksmbd/transport_tcp.c
> +++ b/fs/ksmbd/transport_tcp.c
> @@ -94,17 +94,21 @@ static struct tcp_transport *alloc_transport(struct socket *client_sk)
> return t;
> }
>
> -static void free_transport(struct tcp_transport *t)
> +void ksmbd_free_transport(struct ksmbd_transport *kt)
> {
> - kernel_sock_shutdown(t->sock, SHUT_RDWR);
> - sock_release(t->sock);
> - t->sock = NULL;
> + struct tcp_transport *t = TCP_TRANS(kt);
>
> - ksmbd_conn_free(KSMBD_TRANS(t)->conn);
> + sock_release(t->sock);
> kfree(t->iov);
> kfree(t);
> }
>
> +static void free_transport(struct tcp_transport *t)
> +{
> + kernel_sock_shutdown(t->sock, SHUT_RDWR);
> + ksmbd_conn_free(KSMBD_TRANS(t)->conn);
> +}
> +
> /**
> * kvec_array_init() - initialize a IO vector segment
> * @new: IO vector to be initialized
> diff --git a/fs/ksmbd/transport_tcp.h b/fs/ksmbd/transport_tcp.h
> index e338bebe322f..5925ec5df475 100644
> --- a/fs/ksmbd/transport_tcp.h
> +++ b/fs/ksmbd/transport_tcp.h
> @@ -7,6 +7,7 @@
> #define __KSMBD_TRANSPORT_TCP_H__
>
> int ksmbd_tcp_set_interfaces(char *ifc_list, int ifc_list_sz);
> +void ksmbd_free_transport(struct ksmbd_transport *kt);
> int ksmbd_tcp_init(void);
> void ksmbd_tcp_destroy(void);
--
Andrei Gherzan
gpg: rsa4096/D4D94F67AD0E9640
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <https://lists.ubuntu.com/archives/kernel-team/attachments/20260915/0964d2ae/attachment.sig>
More information about the kernel-team
mailing list