From: David Laight <david.laight.linux@gmail.com>
To: Cen Zhang <zzzccc427@gmail.com>
Cc: davem@davemloft.net, edumazet@kernel.org, kuba@kernel.org,
pabeni@redhat.com, horms@kernel.org, kuniyu@google.com,
fmancera@suse.de, willemb@google.com, kees@kernel.org,
netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
baijiaju1990@gmail.com, jjzuming@gmail.com
Subject: Re: [PATCH net] ipv6: Keep the SCTP provider alive through socket allocation
Date: Wed, 14 Oct 2026 13:23:19 +0100 [thread overview]
Message-ID: <20261014132319.734ca449@pumpkin> (raw)
In-Reply-To: <pm-ip-core-objects-candidate-0001-v2-efc0e8a00d4f22c36ab8@gmail.com>
On Mon, 12 Oct 2026 12:42:15 +0800
Cen Zhang <zzzccc427@gmail.com> wrote:
> The sctpv6_prot slab must remain alive through socket allocation.
> inet6_create() selects that proto under RCU, but drops RCU before
> sk_alloc(). sk_prot_alloc() obtains the owner reference only after
> allocating from the slab.
>
> SCTP publishes its IPv6 stream and seqpacket entries while its module
> is still COMING. A concurrent user socket can select an entry before
> register_pernet_subsys(&sctp_ctrlsock_ops), sctp_v4_add_protocol() or
> sctp_v6_add_protocol() fails. The subsequent initialization unwind
> removes both entries and destroys the cache without waiting for owner
> references.
>
> After the constructor captures the cache pointer, initialization can
> fail and destroy the cache before the allocator consumes that pointer:
>
> User socket creation SCTP module initialization
> -------------------- --------------------------
> publish IPv6 protosw entries
> select sctpv6_prot under RCU
> drop RCU
> capture prot->slab
> fail a later setup step
> sctp_v6_protosw_exit()
> unlink both entries
> synchronize_net()
> proto_unregister()
> destroy slab cache
> kmem_cache_alloc(saved slab)
>
> The allocator can then read retired cache metadata and fault. The grace
> period drains lookup readers, but the constructor has already left RCU.
> An early owner reference alone cannot prevent initialization failure
> from destroying the cache, since try_module_get() accepts COMING.
>
> The initialization-failure path follows from source inspection. The
> Oops below came from an unload experiment using a modified kernel with
> allocator bootstrap changes, scheduling instrumentation and SCTP's
> internal control socket omitted. With that socket present, its owner
> reference blocks ordinary unload. The experiment selected and captured
> the same cache that teardown destroyed; the seqpacket constructor then
> faulted before the allocator returned.
>
> Reject user SCTP sockets while the owner is COMING and take a temporary
> owner reference under the lookup's RCU protection before dropping RCU.
> User allocation then requires completed initialization, and the
> reference prevents normal module exit until sk_alloc() takes the
> socket's own reference or fails. Release the temporary reference in
> either case. Keep kernel control socket creation allowed during init,
> where SCTP's per-net registration orders construction and cleanup.
Isn't this the same basic problem than lets a second sock_create() call
enter the protocol code before initialisation is complete and then hit
all sorts of invalid pointers.
That is a real problem that doesn't require module load to fail (it doesn't)
or module unload to happen at an unexpected time (I doubt anyone really
unloads sctp - it could even be marked not-unloadable?)
David
>
> KASAN report as below:
>
> Oops: general protection fault, probably for non-canonical address
> 0xffff11036c3d1430: 0000 [#1] SMP KASAN NOPTI
> KASAN: maybe wild-memory-access in range
> [0xfff8a81b61e8a180-0xfff8a81b61e8a187]
> CPU: 2 UID: 0 PID: 532 Comm: sctp-race-fixtu Not tainted
> 7.2.0-rc5-pmb-ipcore-functional-v2+ #1 PREEMPT(lazy)
> Hardware name: QEMU Ubuntu 24.04 PC v2 (i440FX + PIIX, arch_caps fix,
> 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
> RIP: 0010:kmem_cache_alloc_noprof+0xd6/0x600
> Code: 85 c0 0f 8e 93 04 00 00 44 8b 74 24 18 0f 1f 44 00 00 41 bf ff ff
> ff ff 65 ff 05 b5 e6 0d 09 65 4c 8b 2d a5 e6 0d 09 4c 03 2b <41> 0f b6
> 45 30 84 c0 0f 85 11 04 00 00 41 c6 45 30 01 45 31 c9 41
> RSP: 0018:ffff8881135dfcc0 EFLAGS: 00010287
> RAX: 0000000000000001 RBX: ffff88810891ec80 RCX: dffffc0000000000
> RDX: 0000000000000cc0 RSI: 0000000000000890 RDI: ffff88810891ec80
> RBP: 0000000000000c00 R08: 0000000000000001 R09: 00000000000000bd
> R10: 0000000000000028 R11: ffff888113e5ba00 R12: 0000000000000cc0
> R13: ffff11036c3d1400 R14: 0000000000000000 R15: 00000000ffffffff
> FS: 00007ff3c2206780(0000) GS:ffff88826b86a000(0000)
> knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 000055b973e7b10c CR3: 00000001158b5005 CR4: 0000000000770ef0
> PKRU: 55555554
> Call Trace:
> <TASK>
> ? srso_alias_return_thunk+0x5/0xfbef5
> ? sk_prot_alloc+0x6f/0x2c0
> sk_prot_alloc+0x6f/0x2c0
> sk_alloc+0x37/0xb40
> inet6_create+0x31b/0x1230
> ? inet6_create+0x5a/0x1230
> __sock_create+0x2c4/0x730
> __sys_socket+0x141/0x210
> ? __pfx___sys_socket+0x10/0x10
> ? srso_alias_return_thunk+0x5/0xfbef5
> ? handle_mm_fault+0x12e/0x580
> __x64_sys_socket+0x77/0xc0
> do_syscall_64+0x115/0x6a0
> entry_SYSCALL_64_after_hwframe+0x77/0x7f
> RIP: 0033:0x7ff3c231b437
> Code: 8b 54 24 08 64 48 2b 14 25 28 00 00 00 75 05 48 83 c4 18 c3 e8 ba
> a7 00 00 66 2e 0f 1f 84 00 00 00 00 00 b8 29 00 00 00 0f 05 <48> 3d 01
> f0 ff ff 73 01 c3 48 8b 0d a9 29 0d 00 f7 d8 64 89 01 48
> RSP: 002b:00007ffed24e4ab8 EFLAGS: 00000246 ORIG_RAX: 0000000000000029
> RAX: ffffffffffffffda RBX: 00007ffed24e4bd8 RCX: 00007ff3c231b437
> RDX: 0000000000000084 RSI: 0000000000000005 RDI: 000000000000000a
> RBP: 0000000000000002 R08: 0000000000000000 R09: 00007ff3c2413aa0
> R10: 00007ffed24e4800 R11: 0000000000000246 R12: 0000000000000000
> R13: 00007ffed24e4bf0 R14: 00007ff3c2444000 R15: 000055b973e7cd48
> </TASK>
> Modules linked in: [last unloaded: sctp]
> ---[ end trace 0000000000000000 ]---
> RIP: 0010:kmem_cache_alloc_noprof+0xd6/0x600
> Code: 85 c0 0f 8e 93 04 00 00 44 8b 74 24 18 0f 1f 44 00 00 41 bf ff ff
> ff ff 65 ff 05 b5 e6 0d 09 65 4c 8b 2d a5 e6 0d 09 4c 03 2b <41> 0f b6
> 45 30 84 c0 0f 85 11 04 00 00 41 c6 45 30 01 45 31 c9 41
> RSP: 0018:ffff8881135dfcc0 EFLAGS: 00010287
> RAX: 0000000000000001 RBX: ffff88810891ec80 RCX: dffffc0000000000
> RDX: 0000000000000cc0 RSI: 0000000000000890 RDI: ffff88810891ec80
> RBP: 0000000000000c00 R08: 0000000000000001 R09: 00000000000000bd
> R10: 0000000000000028 R11: ffff888113e5ba00 R12: 0000000000000cc0
> R13: ffff11036c3d1400 R14: 0000000000000000 R15: 00000000ffffffff
> FS: 00007ff3c2206780(0000) GS:ffff88826b86a000(0000)
> knlGS:0000000000000000
> CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
> CR2: 000055b973e7b10c CR3: 00000001158b5005 CR4: 0000000000770ef0
> PKRU: 55555554
> Kernel panic - not syncing: Fatal exception
> Kernel Offset: disabled
> ---[ end Kernel panic - not syncing: Fatal exception ]---
>
> Signed-off-by: Cen Zhang <zzzccc427@gmail.com>
> Assisted-by: LLM
>
> ---
>
> diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c
> index a9b8b4b42a1af72f09fb9544b0e13aade00a1e3d..fb7f84baeeb66f5d3926169df790cc439f98224f 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 *owner = NULL;
> unsigned char answer_flags;
> int try_loading_module = 0;
> int err;
> @@ -169,6 +170,17 @@ static int inet6_create(struct net *net, struct socket *sock, int protocol,
>
> sock->ops = answer->ops;
> answer_prot = answer->prot;
> + if (answer->protocol == IPPROTO_SCTP) {
> + owner = answer_prot->owner;
> + err = -EPROTONOSUPPORT;
> + /* SCTP init can unwind despite an owner reference.
> + * Allow the kernel control socket during initialization.
> + */
> + if (!kern && owner && module_is_coming(owner))
> + goto out_rcu_unlock;
> + if (!try_module_get(owner))
> + goto out_rcu_unlock;
> + }
> answer_flags = answer->flags;
> rcu_read_unlock();
>
> @@ -176,6 +188,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);
> + module_put(owner);
> if (!sk)
> goto out;
>
>
prev parent reply other threads:[~2026-10-14 12:23 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-12 4:42 [PATCH net] ipv6: Keep the SCTP provider alive through socket allocation Cen Zhang
2026-10-12 19:56 ` Kuniyuki Iwashima
2026-10-13 2:39 ` Cen Zhang
2026-10-13 5:44 ` netdev-bot+sashiko
2026-10-14 12:23 ` David Laight [this message]
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=20261014132319.734ca449@pumpkin \
--to=david.laight.linux@gmail.com \
--cc=baijiaju1990@gmail.com \
--cc=davem@davemloft.net \
--cc=edumazet@kernel.org \
--cc=fmancera@suse.de \
--cc=horms@kernel.org \
--cc=jjzuming@gmail.com \
--cc=kees@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=willemb@google.com \
--cc=zzzccc427@gmail.com \
/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