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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.