From: Chengfeng Ye <nicoyip.dev@gmail.com>
To: davem@davemloft.net, edumazet@kernel.org, kuba@kernel.org,
pabeni@redhat.com
Cc: kuniyu@google.com, horms@kernel.org, fmancera@suse.de,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
stable@vger.kernel.org, Chengfeng Ye <nicoyip.dev@gmail.com>
Subject: [PATCH net v5] net: pin protocol module before inet socket allocation
Date: Tue, 13 Oct 2026 15:56:04 +0800 [thread overview]
Message-ID: <20261013075604.868273-1-nicoyip.dev@gmail.com> (raw)
inet_create() and inet6_create() look up the protocol under RCU, then drop
RCU before using the resulting proto. A concurrent module unload can
unregister the protosw, wait for the RCU readers, and destroy the proto's
slab before sk_alloc() uses it. sk_alloc() may sleep, so the RCU read-side
critical section cannot simply be extended across the allocation.
An LLM-based source scan identified this issue. It was reproduced with
l2tp_ip by racing socket(AF_INET, SOCK_DGRAM, IPPROTO_L2TP) against module
unload in a KASAN-enabled QEMU guest. The reproduction used a temporary
50 ms delay between reading prot->slab and calling kmem_cache_alloc().
proto_unregister() destroyed the cache before the allocation used it:
Oops: general protection fault, probably for non-canonical address
0xffff1108974182a0
KASAN: maybe wild-memory-access in range
[0xfff8a844ba0c1500-0xfff8a844ba0c1507]
RIP: kmem_cache_alloc_noprof+0x63/0x370
Call Trace:
sk_prot_alloc+0x74/0x2c0
sk_alloc+0x2b/0x6c0
inet_create+0x2cd/0xd40
__sock_create+0x1c3/0x430
__sys_socket+0x116/0x1d0
Cache the proto and its owner under RCU and acquire a temporary module
reference before dropping RCU. This prevents normal module unload while
the proto is in use. sk_prot_alloc() takes the socket's own reference;
drop the temporary reference on every exit after allocation. Use the
cached proto for the IPv6 backlog callback as well.
A failed module initialization can unwind published protocols despite
module references. Reject userspace socket creation while the owner is
MODULE_STATE_COMING, before dropping RCU. Exempt kernel sockets from this
state check: SCTP creates its kernel control socket from pernet init
before module initialization completes. These sockets pass kern=true,
which avoids pinning the network namespace they belong to. Both kernel
and userspace sockets still acquire the temporary module reference with
try_module_get().
Fixes: a79af59efd20 ("[NET]: Fix module reference counts for loadable protocol modules")
Suggested-by: Kuniyuki Iwashima <kuniyu@google.com>
Cc: stable@vger.kernel.org
Assisted-by: GPT-6-Astra
Assisted-by: OpenAI Codex
Signed-off-by: Chengfeng Ye <nicoyip.dev@gmail.com>
---
Changes in v5:
- Reject MODULE_STATE_COMING only for userspace sockets (!kern), as
suggested by Kuniyuki. Preserve SCTP kernel control socket creation
during module initialization and keep the temporary module reference
for both kernel and userspace sockets.
- Document how the bug was discovered and the test-only delay used in
the original KASAN reproduction, as requested by the discovery bot
and Jakub. Retain the original fault addresses.
- Keep the cached owner, balanced temporary reference and cached IPv6
backlog callback from v4.
- Rebase onto net/main at 8e2fbe0e75d3.
Validation:
- W=1 build with modular SCTP and compilation of both changed objects
with CONFIG_MODULES=n.
- SCTP module loading and 20 socket/netns cycles per boot on baseline
and v5, with IPv6 enabled and disabled; module references returned
to their initial count after namespace cleanup.
v4: https://lore.kernel.org/r/20261002173844.3537111-1-nicoyip.dev@gmail.com/
Review: https://lore.kernel.org/r/20261012200752.3771786-1-kuniyu@google.com/
v3: https://lore.kernel.org/r/20260904111514.584264-1-nicoyip.dev@gmail.com/
v3 review: https://lore.kernel.org/r/178887699602.219967.13594894510710999619@kernel.org/
v2: https://lore.kernel.org/r/20260825172349.232794-1-nicoyip.dev@gmail.com/
v1: https://lore.kernel.org/r/20260823171311.3857087-1-nicoyip.dev@gmail.com/
net/ipv4/af_inet.c | 15 ++++++++++++---
net/ipv6/af_inet6.c | 17 +++++++++++++----
2 files changed, 25 insertions(+), 7 deletions(-)
diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c
index 14ce01092fda6..5c02022c6c182 100644
--- a/net/ipv4/af_inet.c
+++ b/net/ipv4/af_inet.c
@@ -263,6 +263,7 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
struct inet_protosw *answer;
struct inet_sock *inet;
struct proto *answer_prot;
+ struct module *answer_owner;
unsigned char answer_flags;
int try_loading_module = 0;
int err;
@@ -322,9 +323,15 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
!ns_capable(net->user_ns, CAP_NET_RAW))
goto out_rcu_unlock;
- sock->ops = answer->ops;
answer_prot = answer->prot;
+ answer_owner = answer_prot->owner;
answer_flags = answer->flags;
+ if ((!kern && answer_owner && module_is_coming(answer_owner)) ||
+ !try_module_get(answer_owner)) {
+ err = -EPROTONOSUPPORT;
+ goto out_rcu_unlock;
+ }
+ sock->ops = answer->ops;
rcu_read_unlock();
WARN_ON(!answer_prot->slab);
@@ -332,7 +339,7 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
err = -ENOMEM;
sk = sk_alloc(net, PF_INET, GFP_KERNEL, answer_prot, kern);
if (!sk)
- goto out;
+ goto out_module_put;
err = 0;
if (INET_PROTOSW_REUSE & answer_flags)
@@ -398,6 +405,8 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
if (err)
goto out_sk_release;
}
+out_module_put:
+ module_put(answer_owner);
out:
return err;
out_rcu_unlock:
@@ -406,7 +415,7 @@ static int inet_create(struct net *net, struct socket *sock, int protocol,
out_sk_release:
sk_common_release(sk);
sock->sk = NULL;
- goto out;
+ goto out_module_put;
}
diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
index 282912a119999..99e57fd643322 100644
--- a/net/ipv6/af_inet6.c
+++ b/net/ipv6/af_inet6.c
@@ -110,6 +110,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
struct sock *sk;
struct inet_protosw *answer;
struct proto *answer_prot;
+ struct module *answer_owner;
unsigned char answer_flags;
int try_loading_module = 0;
int err;
@@ -167,9 +168,15 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
!ns_capable(net->user_ns, CAP_NET_RAW))
goto out_rcu_unlock;
- sock->ops = answer->ops;
answer_prot = answer->prot;
+ answer_owner = answer_prot->owner;
answer_flags = answer->flags;
+ if ((!kern && answer_owner && module_is_coming(answer_owner)) ||
+ !try_module_get(answer_owner)) {
+ err = -EPROTONOSUPPORT;
+ goto out_rcu_unlock;
+ }
+ sock->ops = answer->ops;
rcu_read_unlock();
WARN_ON(!answer_prot->slab);
@@ -177,7 +184,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
err = -ENOBUFS;
sk = sk_alloc(net, PF_INET6, GFP_KERNEL, answer_prot, kern);
if (!sk)
- goto out;
+ goto out_module_put;
sock_init_data(sock, sk);
@@ -201,7 +208,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
sk->sk_family = PF_INET6;
sk->sk_protocol = protocol;
- sk->sk_backlog_rcv = answer->prot->backlog_rcv;
+ sk->sk_backlog_rcv = answer_prot->backlog_rcv;
inet_sk(sk)->pinet6 = np = inet6_sk_generic(sk);
np->hop_limit = -1;
@@ -251,6 +258,8 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
if (err)
goto out_sk_release;
}
+out_module_put:
+ module_put(answer_owner);
out:
return err;
out_rcu_unlock:
@@ -259,7 +268,7 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
out_sk_release:
sk_common_release(sk);
sock->sk = NULL;
- goto out;
+ goto out_module_put;
}
int __inet6_bind(struct sock *sk, struct sockaddr_unsized *uaddr, int addr_len,
base-commit: 8e2fbe0e75d38690d551456658194c632933cb57
--
2.43.0
reply other threads:[~2026-10-13 7:56 UTC|newest]
Thread overview: [no followups] expand[flat|nested] mbox.gz Atom feed
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261013075604.868273-1-nicoyip.dev@gmail.com \
--to=nicoyip.dev@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=fmancera@suse.de \
--cc=horms@kernel.org \
--cc=kuba@kernel.org \
--cc=kuniyu@google.com \
--cc=linux-kernel@vger.kernel.org \
--cc=netdev@vger.kernel.org \
--cc=pabeni@redhat.com \
--cc=stable@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®