From netdev-bot+sinfo at kernel.org Thu Oct 1 04:12:58 2026 From: netdev-bot+sinfo at kernel.org (netdev-bot+sinfo at kernel.org) Date: Thu, 01 Oct 2026 04:12:58 +0000 Subject: [PATCH] wireguard: allowedips: prevent stack overflow without DEBUG In-Reply-To: =?utf-8?q?=3CTaZxFNfdnpo-3szeC-LoWZBxj-6XGhXkdEqnQi6yyaOHXp8cw4?= =?utf-8?q?Xtksw07sYrIpludwvJQrukFoCGw3r9rXDcInkdB5QD2EswKPNsXMU3qMQ=3D=40pr?= =?utf-8?q?oton=2Eme=3E?= References: =?utf-8?q?=3CTaZxFNfdnpo-3szeC-LoWZBxj-6XGhXkdEqnQi6yyaOHXp8cw4X?= =?utf-8?q?tksw07sYrIpludwvJQrukFoCGw3r9rXDcInkdB5QD2EswKPNsXMU3qMQ=3D=40pro?= =?utf-8?q?ton=2Eme=3E?= Message-ID: <179082797895.31693.1113127384991730242@kernel.org> Hi! This is an automated message. This series looks like a fix, but its commit messages seem to be missing some information: - How the issue was discovered, e.g. hit in production, hit during development, syzbot report, manual code inspection, LLM or static analysis tool scan. - Whether the issue was actually triggered, or is only theoretical (e.g. found by code inspection). If it was triggered please include the symptoms, like the stack trace or error messages. Please do not repost the series just to address the above. Instead, reply to this email with the missing information, so that reviewers can take it into account. If the series needs another revision for other reasons, please include the information in the commit messages then. The evaluation is done by an LLM so it may be wrong, if you think that is the case please reply and explain. From netdev-bot+sinfo at kernel.org Sat Oct 3 20:19:40 2026 From: netdev-bot+sinfo at kernel.org (netdev-bot+sinfo at kernel.org) Date: Sat, 03 Oct 2026 20:19:40 +0000 Subject: [PATCH net] wireguard: peer: free packets left on the per-peer queues on removal In-Reply-To: <20261003201625.4572-1-apasichnik9@gmail.com> References: <20261003201625.4572-1-apasichnik9@gmail.com> Message-ID: <179105878022.1402591.10615903497956533993@kernel.org> Hi! This is an automated message. This series looks like a fix, but its commit messages seem to be missing some information: - How the issue was discovered, e.g. hit in production, hit during development, syzbot report, manual code inspection, LLM or static analysis tool scan. Please do not repost the series just to address the above. Instead, reply to this email with the missing information, so that reviewers can take it into account. If the series needs another revision for other reasons, please include the information in the commit messages then. The evaluation is done by an LLM so it may be wrong, if you think that is the case please reply and explain. From netdev-bot+sashiko at kernel.org Mon Oct 5 04:25:52 2026 From: netdev-bot+sashiko at kernel.org (netdev-bot+sashiko at kernel.org) Date: Mon, 05 Oct 2026 04:25:52 +0000 Subject: [PATCH] wireguard: allowedips: prevent stack overflow without DEBUG In-Reply-To: =?utf-8?q?=3CTaZxFNfdnpo-3szeC-LoWZBxj-6XGhXkdEqnQi6yyaOHXp8cw4?= =?utf-8?q?Xtksw07sYrIpludwvJQrukFoCGw3r9rXDcInkdB5QD2EswKPNsXMU3qMQ=3D=40pr?= =?utf-8?q?oton=2Eme=3E?= References: =?utf-8?q?=3CTaZxFNfdnpo-3szeC-LoWZBxj-6XGhXkdEqnQi6yyaOHXp8cw4X?= =?utf-8?q?tksw07sYrIpludwvJQrukFoCGw3r9rXDcInkdB5QD2EswKPNsXMU3qMQ=3D=40pro?= =?utf-8?q?ton=2Eme=3E?= Message-ID: <179117435281.434549.17747961185403870914@kernel.org> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider. Critical: 0 ? High: 0 ? Medium: 1 ? Low: 1 If you believe none of the issues are legitimate, please reply to each issue of Medium severity or higher and explain why the AI is wrong. If any of them are legitimate and you plan to respin, please let patchwork know by including "pw-bot: cr" as a separate line at the end of your reply (one such reply per series is enough). - [Medium] The commit message says production builds have "an unchecked stack[129] write" in push_rcu() (drivers/net/wireguard/allowedips.c),? - [Low] The commit author is 'quantumvoid ' but the Signed-off-by trailer says 'quantumvoid0 '. -- wireguard: allowedips: prevent stack overflow without DEBUG This commit changes push_rcu() so that the len >= MAX_ALLOWEDIPS_DEPTH check is always evaluated and only the WARN is gated on DEBUG. The commit message presents this as a fix for an unchecked stack[129] write in production builds. > push_rcu() only bounds-checked the traversal stack when > DEBUG is defined. In production the check is compiled out, > leaving an unchecked stack[129] write. [Severity: Medium] Is the stack[129] write actually reachable in the current tree? The trie invariants seem to rule it out. add() rejects a cidr larger than the address width: if (unlikely(cidr > bits || !peer)) return -EINVAL; On an exact prefix match, node_placement() reuses the existing node. When a node is split, it gets this cidr: cidr = min(cidr, common_bits(down, key, bits)); That value is greater than the parent's cidr and smaller than the children's cidr. So cidr strictly increases along every root-to-leaf path, and a node with cidr == bits has no children. That caps any path at 129 nodes. root_free_rcu() and root_remove_peer_lists() pop one node before pushing at most two children. While a node at depth d is being handled, the stack holds at most d + 2 <= 129 entries. A push with *len == 129 never happens, so stack[129] is never written. The DEBUG-only gating also looks deliberate. The selftest in drivers/net/wireguard/selftest/allowedips.c builds a maximal-depth trie to exercise this assertion. Commit 46622219aae2 ("wireguard: allowedips: expand maximum node depth") sized the array to 129 for this reason and fixed the real off-by-one there. Could the commit message call this hardening of a debug-only invariant check? The other option is to show a path that breaks the depth invariant. As written, it reads as a fix for a reachable stack overflow, with no trigger and no Fixes: tag. > Signed-off-by: quantumvoid0 [Severity: Low] This isn't a bug, but the name in the Signed-off-by (quantumvoid0) does not match the Author name (quantumvoid), even though the email is the same. checkpatch.pl warns about this mismatch. Could the two be made consistent? -- Sashiko AI review ? https://netdev-ai.bots.linux.dev/sashiko/#/patchset/TaZxFNfdnpo-3szeC-LoWZBxj-6XGhXkdEqnQi6yyaOHXp8cw4Xtksw07sYrIpludwvJQrukFoCGw3r9rXDcInkdB5QD2EswKPNsXMU3qMQ%3D%40proton.me From apasichnik9 at gmail.com Sat Oct 3 20:16:25 2026 From: apasichnik9 at gmail.com (Andrii Pasichnyk) Date: Sat, 3 Oct 2026 23:16:25 +0300 Subject: [PATCH net] wireguard: peer: free packets left on the per-peer queues on removal Message-ID: <20261003201625.4572-1-apasichnik9@gmail.com> peer_remove_after_dead() flushes the crypt workqueue and then disables the peer's NAPI. Once a disable is pending, __napi_poll() completes the instance after the poll returns even if it used its whole budget, so a peer removed while more than one budget of decrypted packets waits on rx_queue keeps the rest there. Each entry holds a keypair and a peer reference, so the peer is never released; the WARN_ON in rcu_release() that checks for leftovers is never reached either. Free whatever is left on both per-peer queues once nothing can feed them any more: receive entries are single packets, transmit entries lists. Reproduced with the WireGuard selftest VM (x86 KVM, 1 vCPU, net-next): remove a peer while a UDP flood keeps its receive queue busy, re-add it, repeat, then run the selftest's created/destroyed object check. Over 2140 removals the unpatched kernel leaked a peer and its keypair 5 times ("wg0: Peer 27: merely created"); with this patch, 0 times in another 2140. Fixes: e7096c131e51 ("net: WireGuard secure network tunnel") Assisted-by: LLM Signed-off-by: Andrii Pasichnyk --- drivers/net/wireguard/peer.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/drivers/net/wireguard/peer.c b/drivers/net/wireguard/peer.c index 1cb502a..3e08898 100644 --- a/drivers/net/wireguard/peer.c +++ b/drivers/net/wireguard/peer.c @@ -91,6 +91,25 @@ static void peer_make_dead(struct wg_peer *peer) /* The caller must now synchronize_net() for this to take effect. */ } +/* Each queue entry holds a keypair and a peer reference. Transmit entries + * are lists of packets, receive entries single packets. + */ +static void peer_purge_queue(struct wg_peer *peer, struct prev_queue *queue, + bool lists) +{ + struct sk_buff *first; + + while ((first = wg_prev_queue_peek(queue)) != NULL) { + wg_prev_queue_drop_peeked(queue); + wg_noise_keypair_put(PACKET_CB(first)->keypair, false); + wg_peer_put(peer); + if (lists) + kfree_skb_list(first); + else + dev_kfree_skb(first); + } +} + static void peer_remove_after_dead(struct wg_peer *peer) { WARN_ON(!peer->is_dead); @@ -122,6 +141,11 @@ static void peer_remove_after_dead(struct wg_peer *peer) * here from process context. */ netif_napi_del(&peer->napi); + /* A NAPI being disabled completes after at most one more poll, which + * may leave packets on rx_queue that still hold references. + */ + peer_purge_queue(peer, &peer->rx_queue, false); + peer_purge_queue(peer, &peer->tx_queue, true); /* Ensure any workstructs we own (like transmit_handshake_work or * clear_peer_work) no longer are in use. -- 2.53.0 From apasichnik9 at gmail.com Sat Oct 3 20:41:27 2026 From: apasichnik9 at gmail.com (Andrii Pasichnyk) Date: Sat, 3 Oct 2026 23:41:27 +0300 Subject: [PATCH net] wireguard: peer: free packets left on the per-peer queues on removal In-Reply-To: <179105878022.1402591.10615903497956533993@kernel.org> References: <20261003201625.4572-1-apasichnik9@gmail.com> <179105878022.1402591.10615903497956533993@kernel.org> Message-ID: <20261003204127.15139-1-apasichnik9@gmail.com> On Sat, 3 Oct 2026 20:19:40 +0000 netdev-bot wrote: > - How the issue was discovered, e.g. hit in production, hit during > development, syzbot report, manual code inspection, LLM or static > analysis tool scan. It was found by an LLM-assisted review of the peer removal path, while I was working on a WireGuard change for single-CPU systems; it was not hit in production. I then checked the mechanism by hand against __napi_poll() and the WireGuard history, and confirmed it with a stress test in the WireGuard selftest VM: remove a peer while a UDP flood keeps its receive queue busy, re-add it, repeat, then run the selftest's created/destroyed object check. Unpatched net-next leaked a peer and its keypair 5 times in 2140 removals; with the patch, 0 times in another 2140. Thanks, Andrii From quantumvoid0 at proton.me Thu Oct 1 04:09:34 2026 From: quantumvoid0 at proton.me (quantumvoid) Date: Thu, 01 Oct 2026 04:09:34 +0000 Subject: [PATCH] wireguard: allowedips: prevent stack overflow without DEBUG Message-ID: push_rcu() only bounds-checked the traversal stack when DEBUG is defined. In production the check is compiled out, leaving an unchecked stack[129] write. Always check len >= MAX_ALLOWEDIPS_DEPTH and only gate the WARN splat on DEBUG. Signed-off-by: quantumvoid0 --- drivers/net/wireguard/allowedips.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/net/wireguard/allowedips.c b/drivers/net/wireguard/allowedips.c index 5ece9ac..dc383f5 100644 --- a/drivers/net/wireguard/allowedips.c +++ b/drivers/net/wireguard/allowedips.c @@ -42,8 +42,10 @@ static void push_rcu(struct allowedips_node **stack, struct allowedips_node __rcu *p, unsigned int *len) { if (rcu_access_pointer(p)) { - if (WARN_ON(IS_ENABLED(DEBUG) && *len >= MAX_ALLOWEDIPS_DEPTH)) + if (unlikely(*len >= MAX_ALLOWEDIPS_DEPTH)) { + WARN_ON(IS_ENABLED(DEBUG)); return; + } stack[(*len)++] = rcu_dereference_raw(p); } } -- 2.55.0 From quantumvoid0 at proton.me Mon Oct 5 12:13:02 2026 From: quantumvoid0 at proton.me (quantumvoid0) Date: Mon, 05 Oct 2026 12:13:02 +0000 Subject: [PATCH] wireguard: allowedips: prevent stack overflow without DEBUG In-Reply-To: <179117435281.434549.17747961185403870914@kernel.org> References: <179117435281.434549.17747961185403870914@kernel.org> Message-ID: <9NzB3VmT-7cBBfdx9sREmRkFb_gOAmI78VheKlX2Mdiqjl3B3N0OCZ5DT8C1SpEJuM5O3TzLltSpGh9ptxni28Lnq4ATz8u86R-f-jdcKPE=@proton.me> Thanks, both points are valid. Medium is correct, not reachable in tree. cidr strictly increases along root->leaf so depth <=129, DFS stack stays <=129. This is hardening only so a future bug cant smash stack[129] in production where WARN_ON(DEBUG && ...) compiles out. Ill reword v2 to say hardening, no Fixes: tag. Low is correct; author/Sob name mismatch. Will fix to match in v2. pw-bot: cr On Monday, October 5th, 2026 at 4:25 AM, netdev-bot+sashiko at kernel.org wrote: > Thank you for your contribution! Sashiko AI review found 2 potential > issue(s) to consider. > > Critical: 0 ? High: 0 ? Medium: 1 ? Low: 1 > > If you believe none of the issues are legitimate, please reply to each > issue of Medium severity or higher and explain why the AI is wrong. > If any of them are legitimate and you plan to respin, please let patchwork > know by including "pw-bot: cr" as a separate line at the end of your reply > (one such reply per series is enough). > > - [Medium] The commit message says production builds have "an unchecked > stack[129] write" in push_rcu() (drivers/net/wireguard/allowedips.c),? > - [Low] The commit author is 'quantumvoid ' but > the Signed-off-by trailer says 'quantumvoid0 '. > > -- > > wireguard: allowedips: prevent stack overflow without DEBUG > > This commit changes push_rcu() so that the len >= MAX_ALLOWEDIPS_DEPTH > check is always evaluated and only the WARN is gated on DEBUG. The commit > message presents this as a fix for an unchecked stack[129] write in > production builds. > > > push_rcu() only bounds-checked the traversal stack when > > DEBUG is defined. In production the check is compiled out, > > leaving an unchecked stack[129] write. > > [Severity: Medium] > Is the stack[129] write actually reachable in the current tree? The trie > invariants seem to rule it out. > > add() rejects a cidr larger than the address width: > > if (unlikely(cidr > bits || !peer)) > return -EINVAL; > > On an exact prefix match, node_placement() reuses the existing node. When > a node is split, it gets this cidr: > > cidr = min(cidr, common_bits(down, key, bits)); > > That value is greater than the parent's cidr and smaller than the > children's cidr. So cidr strictly increases along every root-to-leaf path, > and a node with cidr == bits has no children. That caps any path at 129 > nodes. > > root_free_rcu() and root_remove_peer_lists() pop one node before pushing > at most two children. While a node at depth d is being handled, the stack > holds at most d + 2 <= 129 entries. A push with *len == 129 never happens, > so stack[129] is never written. > > The DEBUG-only gating also looks deliberate. The selftest in > drivers/net/wireguard/selftest/allowedips.c builds a maximal-depth trie to > exercise this assertion. Commit 46622219aae2 ("wireguard: allowedips: > expand maximum node depth") sized the array to 129 for this reason and > fixed the real off-by-one there. > > Could the commit message call this hardening of a debug-only invariant > check? The other option is to show a path that breaks the depth invariant. > As written, it reads as a fix for a reachable stack overflow, with no > trigger and no Fixes: tag. > > > Signed-off-by: quantumvoid0 > > [Severity: Low] > This isn't a bug, but the name in the Signed-off-by (quantumvoid0) does > not match the Author name (quantumvoid), even though the email is the > same. checkpatch.pl warns about this mismatch. Could the two be made > consistent? > > -- > Sashiko AI review ? https://netdev-ai.bots.linux.dev/sashiko/#/patchset/TaZxFNfdnpo-3szeC-LoWZBxj-6XGhXkdEqnQi6yyaOHXp8cw4Xtksw07sYrIpludwvJQrukFoCGw3r9rXDcInkdB5QD2EswKPNsXMU3qMQ%3D%40proton.me > From Jason at zx2c4.com Mon Oct 5 12:19:07 2026 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Mon, 5 Oct 2026 14:19:07 +0200 Subject: [PATCH] wireguard: allowedips: prevent stack overflow without DEBUG In-Reply-To: <9NzB3VmT-7cBBfdx9sREmRkFb_gOAmI78VheKlX2Mdiqjl3B3N0OCZ5DT8C1SpEJuM5O3TzLltSpGh9ptxni28Lnq4ATz8u86R-f-jdcKPE=@proton.me> References: <179117435281.434549.17747961185403870914@kernel.org> <9NzB3VmT-7cBBfdx9sREmRkFb_gOAmI78VheKlX2Mdiqjl3B3N0OCZ5DT8C1SpEJuM5O3TzLltSpGh9ptxni28Lnq4ATz8u86R-f-jdcKPE=@proton.me> Message-ID: This is not reachable. The DEBUG is there because the point of the selftests is to reach it, but not for this to run in production. I coded it this way intentionally. I don't want to remove the DEBUG test. Jason From quantumvoid0 at proton.me Mon Oct 5 12:23:08 2026 From: quantumvoid0 at proton.me (quantumvoid0) Date: Mon, 05 Oct 2026 12:23:08 +0000 Subject: [PATCH] wireguard: allowedips: prevent stack overflow without DEBUG In-Reply-To: References: <179117435281.434549.17747961185403870914@kernel.org> <9NzB3VmT-7cBBfdx9sREmRkFb_gOAmI78VheKlX2Mdiqjl3B3N0OCZ5DT8C1SpEJuM5O3TzLltSpGh9ptxni28Lnq4ATz8u86R-f-jdcKPE=@proton.me> Message-ID: Understood, thanks for explaining. Ill drop this. quantumvoid0 On Monday, October 5th, 2026 at 12:19 PM, Jason A. Donenfeld wrote: > This is not reachable. The DEBUG is there because the point of the > selftests is to reach it, but not for this to run in production. I > coded it this way intentionally. I don't want to remove the DEBUG > test. > > Jason > From netdev-bot+sinfo at kernel.org Mon Oct 5 20:43:52 2026 From: netdev-bot+sinfo at kernel.org (netdev-bot+sinfo at kernel.org) Date: Mon, 05 Oct 2026 20:43:52 +0000 Subject: [PATCH net] wireguard: noise: reject responses for replaced initiations In-Reply-To: <20261005203555.3552816-2-Jeremy.Jean@oss.cyber.gouv.fr> References: <20261005203555.3552816-2-Jeremy.Jean@oss.cyber.gouv.fr> Message-ID: <179123303279.1402591.1890335379434041073@kernel.org> Hi! This is an automated message. This series looks like a fix, but its commit messages seem to be missing some information: - How the issue was discovered, e.g. hit in production, hit during development, syzbot report, manual code inspection, LLM or static analysis tool scan. - Whether the issue was actually triggered, or is only theoretical (e.g. found by code inspection). If it was triggered please include the symptoms, like the stack trace or error messages. Please do not repost the series just to address the above. Instead, reply to this email with the missing information, so that reviewers can take it into account. If the series needs another revision for other reasons, please include the information in the commit messages then. The evaluation is done by an LLM so it may be wrong, if you think that is the case please reply and explain. From Jason at zx2c4.com Mon Oct 5 21:18:33 2026 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Mon, 5 Oct 2026 23:18:33 +0200 Subject: [PATCH net] wireguard: noise: reject responses for replaced initiations In-Reply-To: <20261005203555.3552816-2-Jeremy.Jean@oss.cyber.gouv.fr> References: <20261005203555.3552816-2-Jeremy.Jean@oss.cyber.gouv.fr> Message-ID: Hi, On Mon, Oct 05, 2026 at 08:35:55PM +0000, J?r?my Jean wrote: > WireGuard can accept an old handshake response after starting a new > handshake. This reinstalls old keys and resets transport counters and > replay state, enabling nonce reuse, replay and packet forgery. This > breaks confidentiality and integrity guarantees. > > Compare ephemeral secrets under the write lock to reject responses for > replaced initiations. > > Fixes: e7096c131e51 ("net: WireGuard secure network tunnel") > Cc: stable at vger.kernel.org > Assisted-by: LLM > Signed-off-by: J?r?my Jean > --- > drivers/net/wireguard/noise.c | 8 +++++--- > 1 file changed, 5 insertions(+), 3 deletions(-) > > diff --git a/drivers/net/wireguard/noise.c b/drivers/net/wireguard/noise.c > index 9c0a09bf6c95..88cc9acc7dc7 100644 > --- a/drivers/net/wireguard/noise.c > +++ b/drivers/net/wireguard/noise.c > @@ -784,10 +784,12 @@ wg_noise_handshake_consume_response(struct message_handshake_response *src, > > /* Success! Copy everything to peer */ > down_write(&handshake->lock); > - /* It's important to check that the state is still the same, while we > - * have an exclusive lock. > + /* Check that this is still the initiation we authenticated against, > + * while we have an exclusive lock. > */ > - if (handshake->state != state) { > + if (handshake->state != state || > + crypto_memneq(handshake->ephemeral_private, ephemeral_private, > + NOISE_PUBLIC_KEY_LEN)) { > up_write(&handshake->lock); > goto fail; > } Could you describe the flow that you think causes a bug? Trying to recreate your mental model. Something like this? == thread 1 == down_read(&handshake->lock); state = handshake->state; memcpy(hash, handshake->hash, NOISE_HASH_LEN); memcpy(chaining_key, handshake->chaining_key, NOISE_HASH_LEN); memcpy(ephemeral_private, handshake->ephemeral_private, NOISE_PUBLIC_KEY_LEN); memcpy(preshared_key, handshake->preshared_key, NOISE_SYMMETRIC_KEY_LEN); up_read(&handshake->lock); if (state != HANDSHAKE_CREATED_INITIATION) goto fail; /* e */ message_ephemeral(e, src->unencrypted_ephemeral, chaining_key, hash); /* ee */ if (!mix_dh(chaining_key, NULL, ephemeral_private, e)) goto fail; /* se */ if (!mix_dh(chaining_key, NULL, wg->static_identity.static_private, e)) goto fail; /* psk */ mix_psk(chaining_key, hash, key, preshared_key); /* {} */ if (!message_decrypt(NULL, src->encrypted_nothing, sizeof(src->encrypted_nothing), key, hash)) goto fail; == thread 2 == down_read(&handshake->static_identity->lock); down_write(&handshake->lock); if (unlikely(!handshake->static_identity->has_identity)) goto out; dst->header.type = cpu_to_le32(MESSAGE_HANDSHAKE_INITIATION); handshake_init(handshake->chaining_key, handshake->hash, handshake->remote_static); /* e */ curve25519_generate_secret(handshake->ephemeral_private); if (!curve25519_generate_public(dst->unencrypted_ephemeral, handshake->ephemeral_private)) goto out; message_ephemeral(dst->unencrypted_ephemeral, dst->unencrypted_ephemeral, handshake->chaining_key, handshake->hash); /* es */ if (!mix_dh(handshake->chaining_key, key, handshake->ephemeral_private, handshake->remote_static)) goto out; /* s */ message_encrypt(dst->encrypted_static, handshake->static_identity->static_public, NOISE_PUBLIC_KEY_LEN, key, handshake->hash); /* ss */ if (!mix_precomputed_dh(handshake->chaining_key, key, handshake->precomputed_static_static)) goto out; /* {t} */ tai64n_now(timestamp); message_encrypt(dst->encrypted_timestamp, timestamp, NOISE_TIMESTAMP_LEN, key, handshake->hash); dst->sender_index = wg_index_hashtable_insert( handshake->entry.peer->device->index_hashtable, &handshake->entry); handshake->state = HANDSHAKE_CREATED_INITIATION; ret = true; out: up_write(&handshake->lock); up_read(&handshake->static_identity->lock); == thread 1 == down_write(&handshake->lock); /* It's important to check that the state is still the same, while we * have an exclusive lock. */ if (handshake->state != state) { up_write(&handshake->lock); goto fail; } memcpy(handshake->remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN); memcpy(handshake->hash, hash, NOISE_HASH_LEN); memcpy(handshake->chaining_key, chaining_key, NOISE_HASH_LEN); handshake->remote_index = src->sender_index; handshake->state = HANDSHAKE_CONSUMED_RESPONSE; up_write(&handshake->lock); ret_peer = peer; goto out; And now begin_session is called on the older completed handshake rather than the half-completed newer handshake? Or did you see some other flow? Let me know. Thanks, Jason From mail at kilian-ries.de Mon Oct 5 13:52:04 2026 From: mail at kilian-ries.de (Kilian Ries) Date: Mon, 05 Oct 2026 15:52:04 +0200 Subject: [PATCH wireguard-apple] =?utf-8?q?Keychain=3A?= use data protection keychain on macOS Message-ID: On macOS, tunnel configurations are stored in the file-based login keychain, with an access list that names the app and the network extension. On a Mac enrolled through account-driven enrollment, apps installed by MDM keep their data on a separate "Managed Data" volume. There is no login keychain for those apps, so SecItemAdd tries to create one, and the sandbox refuses: authd: Sandbox denied authorizing right 'system.keychain.create.loginkc' by client '/Applications/WireGuard.app' WireGuard: Unable to add config to keychain: -60005 WireGuard: Add: Saving configuration failed: Error Domain=NEVPNErrorDomain Code=1 "Missing protocol or protocol has invalid type" In that state no tunnel can be added or imported. Store new items in the data protection keychain instead, in the app group that the app and the network extension already share, as on iOS. The access list is not needed there, because the shared access group grants the network extension access. Items created by earlier versions stay in the file-based keychain. Reading, verifying and deleting a reference try the data protection keychain first and then the file-based one, so existing tunnels keep working. An existing tunnel moves to the data protection keychain the next time it is saved. Signed-off-by: Kilian Ries --- Notes: Seen on macOS 27.0.1 with WireGuard 1.0.16 from the App Store, on a Mac enrolled in Apple Business Manager through account-driven device enrollment. Apps installed there by MDM show a Personas entry in "pluginkit -mAvvv", and their containers live on the "Managed Data" APFS volume. Type-checked for macOS 12 and iOS 15. Not tested at runtime yet, since that needs a build signed for the app group. Sources/Shared/Keychain.swift | 96 +++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 45 deletions(-) diff --git a/Sources/Shared/Keychain.swift b/Sources/Shared/Keychain.swift index 2e0e7f0..c99ea85 100644 --- a/Sources/Shared/Keychain.swift +++ b/Sources/Shared/Keychain.swift @@ -7,9 +7,8 @@ import Security class Keychain { static func openReference(called ref: Data) -> String? { var result: CFTypeRef? - let ret = SecItemCopyMatching([kSecValuePersistentRef: ref, - kSecReturnData: true] as CFDictionary, - &result) + let ret = firstSuccess([kSecValuePersistentRef: ref, + kSecReturnData: true]) { SecItemCopyMatching($0, &result) } if ret != errSecSuccess || result == nil { wg_log(.error, message: "Unable to open config from keychain: \(ret)") return nil @@ -19,7 +18,6 @@ class Keychain { } static func makeReference(containing value: String, called name: String, previouslyReferencedBy oldRef: Data? = nil) -> Data? { - var ret: OSStatus guard var bundleIdentifier = Bundle.main.bundleIdentifier else { wg_log(.error, staticMessage: "Unable to determine bundle identifier") return nil @@ -40,38 +38,16 @@ class Keychain { items[kSecAttrAccessGroup] = FileManager.appGroupId items[kSecAttrAccessible] = kSecAttrAccessibleAfterFirstUnlock #elseif os(macOS) + items[kSecUseDataProtectionKeychain] = true + items[kSecAttrAccessGroup] = FileManager.appGroupId items[kSecAttrSynchronizable] = false items[kSecAttrAccessible] = kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly - - guard let extensionPath = Bundle.main.builtInPlugInsURL?.appendingPathComponent("WireGuardNetworkExtension.appex", isDirectory: true).path else { - wg_log(.error, staticMessage: "Unable to determine app extension path") - return nil - } - var extensionApp: SecTrustedApplication? - var mainApp: SecTrustedApplication? - ret = SecTrustedApplicationCreateFromPath(extensionPath, &extensionApp) - if ret != kOSReturnSuccess || extensionApp == nil { - wg_log(.error, message: "Unable to create keychain extension trusted application object: \(ret)") - return nil - } - ret = SecTrustedApplicationCreateFromPath(nil, &mainApp) - if ret != errSecSuccess || mainApp == nil { - wg_log(.error, message: "Unable to create keychain local trusted application object: \(ret)") - return nil - } - var access: SecAccess? - ret = SecAccessCreate(itemLabel as CFString, [extensionApp!, mainApp!] as CFArray, &access) - if ret != errSecSuccess || access == nil { - wg_log(.error, message: "Unable to create keychain ACL object: \(ret)") - return nil - } - items[kSecAttrAccess] = access! #else #error("Unimplemented") #endif var ref: CFTypeRef? - ret = SecItemAdd(items as CFDictionary, &ref) + let ret = SecItemAdd(items as CFDictionary, &ref) if ret != errSecSuccess || ref == nil { wg_log(.error, message: "Unable to add config to keychain: \(ret)") return nil @@ -83,32 +59,62 @@ class Keychain { } static func deleteReference(called ref: Data) { - let ret = SecItemDelete([kSecValuePersistentRef: ref] as CFDictionary) + let ret = firstSuccess([kSecValuePersistentRef: ref]) { SecItemDelete($0) } if ret != errSecSuccess { wg_log(.error, message: "Unable to delete config from keychain: \(ret)") } } static func deleteReferences(except whitelist: Set) { - var result: CFTypeRef? - let ret = SecItemCopyMatching([kSecClass: kSecClassGenericPassword, - kSecAttrService: Bundle.main.bundleIdentifier as Any, - kSecMatchLimit: kSecMatchLimitAll, - kSecReturnPersistentRef: true] as CFDictionary, - &result) - if ret != errSecSuccess || result == nil { - return - } - guard let items = result as? [Data] else { return } - for item in items { - if !whitelist.contains(item) { - deleteReference(called: item) + for query in keychainQueries([kSecClass: kSecClassGenericPassword, + kSecAttrService: Bundle.main.bundleIdentifier as Any, + kSecMatchLimit: kSecMatchLimitAll, + kSecReturnPersistentRef: true]) { + var result: CFTypeRef? + let ret = SecItemCopyMatching(query, &result) + if ret != errSecSuccess || result == nil { + continue + } + guard let items = result as? [Data] else { continue } + for item in items { + if !whitelist.contains(item) { + deleteReference(called: item) + } } } } static func verifyReference(called ref: Data) -> Bool { - return SecItemCopyMatching([kSecValuePersistentRef: ref] as CFDictionary, - nil) != errSecItemNotFound + return firstSuccess([kSecValuePersistentRef: ref]) { SecItemCopyMatching($0, nil) } != errSecItemNotFound + } + + /* On macOS, new items go into the data protection keychain. Items created by + * earlier versions live in the file-based login keychain, which does not exist + * for apps whose data the system keeps on a separate volume, such as apps + * installed by MDM. So every lookup tries the data protection keychain first + * and then the file-based one. + */ + private static func keychainQueries(_ query: [CFString: Any]) -> [CFDictionary] { + #if os(macOS) + var dataProtectionQuery = query + dataProtectionQuery[kSecUseDataProtectionKeychain] = true + return [dataProtectionQuery as CFDictionary, query as CFDictionary] + #else + return [query as CFDictionary] + #endif + } + + private static func firstSuccess(_ query: [CFString: Any], _ operation: (CFDictionary) -> OSStatus) -> OSStatus { + var ret = errSecItemNotFound + for query in keychainQueries(query) { + let status = operation(query) + if status == errSecSuccess { + return status + } + if status != errSecItemNotFound && status != errSecNoSuchKeychain { + ret = status + } + } + return ret } } -- 2.54.0 (Apple Git-157) From Jeremy.Jean at oss.cyber.gouv.fr Mon Oct 5 20:35:55 2026 From: Jeremy.Jean at oss.cyber.gouv.fr (=?UTF-8?q?J=C3=A9r=C3=A9my=20Jean?=) Date: Mon, 5 Oct 2026 20:35:55 +0000 Subject: [PATCH net] wireguard: noise: reject responses for replaced initiations Message-ID: <20261005203555.3552816-2-Jeremy.Jean@oss.cyber.gouv.fr> WireGuard can accept an old handshake response after starting a new handshake. This reinstalls old keys and resets transport counters and replay state, enabling nonce reuse, replay and packet forgery. This breaks confidentiality and integrity guarantees. Compare ephemeral secrets under the write lock to reject responses for replaced initiations. Fixes: e7096c131e51 ("net: WireGuard secure network tunnel") Cc: stable at vger.kernel.org Assisted-by: LLM Signed-off-by: J?r?my Jean --- drivers/net/wireguard/noise.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/net/wireguard/noise.c b/drivers/net/wireguard/noise.c index 9c0a09bf6c95..88cc9acc7dc7 100644 --- a/drivers/net/wireguard/noise.c +++ b/drivers/net/wireguard/noise.c @@ -784,10 +784,12 @@ wg_noise_handshake_consume_response(struct message_handshake_response *src, /* Success! Copy everything to peer */ down_write(&handshake->lock); - /* It's important to check that the state is still the same, while we - * have an exclusive lock. + /* Check that this is still the initiation we authenticated against, + * while we have an exclusive lock. */ - if (handshake->state != state) { + if (handshake->state != state || + crypto_memneq(handshake->ephemeral_private, ephemeral_private, + NOISE_PUBLIC_KEY_LEN)) { up_write(&handshake->lock); goto fail; } -- 2.47.3 From Jason at zx2c4.com Tue Oct 6 07:53:18 2026 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Tue, 6 Oct 2026 09:53:18 +0200 Subject: [PATCH net] wireguard: noise: reject responses for replaced initiations In-Reply-To: References: <20261005203555.3552816-2-Jeremy.Jean@oss.cyber.gouv.fr> Message-ID: On Mon, Oct 05, 2026 at 11:18:33PM +0200, Jason A. Donenfeld wrote: > Hi, > > On Mon, Oct 05, 2026 at 08:35:55PM +0000, J?r?my Jean wrote: > > WireGuard can accept an old handshake response after starting a new > > handshake. This reinstalls old keys and resets transport counters and > > replay state, enabling nonce reuse, replay and packet forgery. This > > breaks confidentiality and integrity guarantees. > > > > Compare ephemeral secrets under the write lock to reject responses for > > replaced initiations. > > > > Fixes: e7096c131e51 ("net: WireGuard secure network tunnel") > > Cc: stable at vger.kernel.org > > Assisted-by: LLM > > Signed-off-by: J?r?my Jean > > --- > > drivers/net/wireguard/noise.c | 8 +++++--- > > 1 file changed, 5 insertions(+), 3 deletions(-) > > > > diff --git a/drivers/net/wireguard/noise.c b/drivers/net/wireguard/noise.c > > index 9c0a09bf6c95..88cc9acc7dc7 100644 > > --- a/drivers/net/wireguard/noise.c > > +++ b/drivers/net/wireguard/noise.c > > @@ -784,10 +784,12 @@ wg_noise_handshake_consume_response(struct message_handshake_response *src, > > > > /* Success! Copy everything to peer */ > > down_write(&handshake->lock); > > - /* It's important to check that the state is still the same, while we > > - * have an exclusive lock. > > + /* Check that this is still the initiation we authenticated against, > > + * while we have an exclusive lock. > > */ > > - if (handshake->state != state) { > > + if (handshake->state != state || > > + crypto_memneq(handshake->ephemeral_private, ephemeral_private, > > + NOISE_PUBLIC_KEY_LEN)) { > > up_write(&handshake->lock); > > goto fail; > > } > > Could you describe the flow that you think causes a bug? Trying > to recreate your mental model. Something like this? > > == thread 1 == > > down_read(&handshake->lock); > state = handshake->state; > memcpy(hash, handshake->hash, NOISE_HASH_LEN); > memcpy(chaining_key, handshake->chaining_key, NOISE_HASH_LEN); > memcpy(ephemeral_private, handshake->ephemeral_private, > NOISE_PUBLIC_KEY_LEN); > memcpy(preshared_key, handshake->preshared_key, > NOISE_SYMMETRIC_KEY_LEN); > up_read(&handshake->lock); > > if (state != HANDSHAKE_CREATED_INITIATION) > goto fail; > > /* e */ > message_ephemeral(e, src->unencrypted_ephemeral, chaining_key, hash); > > /* ee */ > if (!mix_dh(chaining_key, NULL, ephemeral_private, e)) > goto fail; > > /* se */ > if (!mix_dh(chaining_key, NULL, wg->static_identity.static_private, e)) > goto fail; > > /* psk */ > mix_psk(chaining_key, hash, key, preshared_key); > > /* {} */ > if (!message_decrypt(NULL, src->encrypted_nothing, > sizeof(src->encrypted_nothing), key, hash)) > goto fail; > > == thread 2 == > > down_read(&handshake->static_identity->lock); > down_write(&handshake->lock); > > if (unlikely(!handshake->static_identity->has_identity)) > goto out; > > dst->header.type = cpu_to_le32(MESSAGE_HANDSHAKE_INITIATION); > > handshake_init(handshake->chaining_key, handshake->hash, > handshake->remote_static); > > /* e */ > curve25519_generate_secret(handshake->ephemeral_private); > if (!curve25519_generate_public(dst->unencrypted_ephemeral, > handshake->ephemeral_private)) > goto out; > message_ephemeral(dst->unencrypted_ephemeral, > dst->unencrypted_ephemeral, handshake->chaining_key, > handshake->hash); > > /* es */ > if (!mix_dh(handshake->chaining_key, key, handshake->ephemeral_private, > handshake->remote_static)) > goto out; > > /* s */ > message_encrypt(dst->encrypted_static, > handshake->static_identity->static_public, > NOISE_PUBLIC_KEY_LEN, key, handshake->hash); > > /* ss */ > if (!mix_precomputed_dh(handshake->chaining_key, key, > handshake->precomputed_static_static)) > goto out; > > /* {t} */ > tai64n_now(timestamp); > message_encrypt(dst->encrypted_timestamp, timestamp, > NOISE_TIMESTAMP_LEN, key, handshake->hash); > > dst->sender_index = wg_index_hashtable_insert( > handshake->entry.peer->device->index_hashtable, > &handshake->entry); > > handshake->state = HANDSHAKE_CREATED_INITIATION; > ret = true; > > out: > up_write(&handshake->lock); > up_read(&handshake->static_identity->lock); > > == thread 1 == > > down_write(&handshake->lock); > /* It's important to check that the state is still the same, while we > * have an exclusive lock. > */ > if (handshake->state != state) { > up_write(&handshake->lock); > goto fail; > } > memcpy(handshake->remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN); > memcpy(handshake->hash, hash, NOISE_HASH_LEN); > memcpy(handshake->chaining_key, chaining_key, NOISE_HASH_LEN); > handshake->remote_index = src->sender_index; > handshake->state = HANDSHAKE_CONSUMED_RESPONSE; > up_write(&handshake->lock); > ret_peer = peer; > goto out; > > And now begin_session is called on the older completed handshake rather > than the half-completed newer handshake? > > Or did you see some other flow? Okay I think I worked something plausible out: - two threads begin processing the same response - the first succeeds. the second gets halfway, when it blocks on taking a lock that a queued initiation has taken - the queued initiation does its thing and resets the state - the second thread resumes and completes the old handshake and reinstalls keys I'll continue analyzing real world feasibility, but in all cases, thank you for the patch. Jason From Jason at zx2c4.com Tue Oct 6 11:28:15 2026 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Tue, 6 Oct 2026 13:28:15 +0200 Subject: OpenBSD collaboration/peer-review request In-Reply-To: <188dc861-f452-4830-9b47-e51c15faf41d@philomathiclife.com> References: <188dc861-f452-4830-9b47-e51c15faf41d@philomathiclife.com> Message-ID: Hi Zack, Of course I'd be happy to work on this. Do you (or someone else) want to jump on a video call and we can share screens and you can show me the stuff you think I should be looking at? Jason From jeremy.jean at oss.cyber.gouv.fr Tue Oct 6 12:40:36 2026 From: jeremy.jean at oss.cyber.gouv.fr (=?UTF-8?Q?J=C3=A9r=C3=A9my_Jean?=) Date: Tue, 06 Oct 2026 14:40:36 +0200 Subject: [PATCH net] wireguard: noise: reject responses for replaced initiations In-Reply-To: References: <20261005203555.3552816-2-Jeremy.Jean@oss.cyber.gouv.fr> Message-ID: On 2026-10-06 09:53, Jason A. Donenfeld wrote: > On Mon, Oct 05, 2026 at 11:18:33PM +0200, Jason A. Donenfeld wrote: >> Hi, >> >> On Mon, Oct 05, 2026 at 08:35:55PM +0000, J?r?my Jean wrote: >> > WireGuard can accept an old handshake response after starting a new >> > handshake. This reinstalls old keys and resets transport counters and >> > replay state, enabling nonce reuse, replay and packet forgery. This >> > breaks confidentiality and integrity guarantees. >> > >> > Compare ephemeral secrets under the write lock to reject responses for >> > replaced initiations. >> > >> > Fixes: e7096c131e51 ("net: WireGuard secure network tunnel") >> > Cc: stable at vger.kernel.org >> > Assisted-by: LLM >> > Signed-off-by: J?r?my Jean >> > --- >> > drivers/net/wireguard/noise.c | 8 +++++--- >> > 1 file changed, 5 insertions(+), 3 deletions(-) >> > >> > diff --git a/drivers/net/wireguard/noise.c b/drivers/net/wireguard/noise.c >> > index 9c0a09bf6c95..88cc9acc7dc7 100644 >> > --- a/drivers/net/wireguard/noise.c >> > +++ b/drivers/net/wireguard/noise.c >> > @@ -784,10 +784,12 @@ wg_noise_handshake_consume_response(struct message_handshake_response *src, >> > >> > /* Success! Copy everything to peer */ >> > down_write(&handshake->lock); >> > - /* It's important to check that the state is still the same, while we >> > - * have an exclusive lock. >> > + /* Check that this is still the initiation we authenticated against, >> > + * while we have an exclusive lock. >> > */ >> > - if (handshake->state != state) { >> > + if (handshake->state != state || >> > + crypto_memneq(handshake->ephemeral_private, ephemeral_private, >> > + NOISE_PUBLIC_KEY_LEN)) { >> > up_write(&handshake->lock); >> > goto fail; >> > } >> >> Could you describe the flow that you think causes a bug? Trying >> to recreate your mental model. Something like this? >> >> == thread 1 == >> >> down_read(&handshake->lock); >> state = handshake->state; >> memcpy(hash, handshake->hash, NOISE_HASH_LEN); >> memcpy(chaining_key, handshake->chaining_key, NOISE_HASH_LEN); >> memcpy(ephemeral_private, handshake->ephemeral_private, >> NOISE_PUBLIC_KEY_LEN); >> memcpy(preshared_key, handshake->preshared_key, >> NOISE_SYMMETRIC_KEY_LEN); >> up_read(&handshake->lock); >> >> if (state != HANDSHAKE_CREATED_INITIATION) >> goto fail; >> >> /* e */ >> message_ephemeral(e, src->unencrypted_ephemeral, chaining_key, hash); >> >> /* ee */ >> if (!mix_dh(chaining_key, NULL, ephemeral_private, e)) >> goto fail; >> >> /* se */ >> if (!mix_dh(chaining_key, NULL, wg->static_identity.static_private, >> e)) >> goto fail; >> >> /* psk */ >> mix_psk(chaining_key, hash, key, preshared_key); >> >> /* {} */ >> if (!message_decrypt(NULL, src->encrypted_nothing, >> sizeof(src->encrypted_nothing), key, hash)) >> goto fail; >> >> == thread 2 == >> >> down_read(&handshake->static_identity->lock); >> down_write(&handshake->lock); >> >> if (unlikely(!handshake->static_identity->has_identity)) >> goto out; >> >> dst->header.type = cpu_to_le32(MESSAGE_HANDSHAKE_INITIATION); >> >> handshake_init(handshake->chaining_key, handshake->hash, >> handshake->remote_static); >> >> /* e */ >> curve25519_generate_secret(handshake->ephemeral_private); >> if (!curve25519_generate_public(dst->unencrypted_ephemeral, >> handshake->ephemeral_private)) >> goto out; >> message_ephemeral(dst->unencrypted_ephemeral, >> dst->unencrypted_ephemeral, handshake->chaining_key, >> handshake->hash); >> >> /* es */ >> if (!mix_dh(handshake->chaining_key, key, >> handshake->ephemeral_private, >> handshake->remote_static)) >> goto out; >> >> /* s */ >> message_encrypt(dst->encrypted_static, >> handshake->static_identity->static_public, >> NOISE_PUBLIC_KEY_LEN, key, handshake->hash); >> >> /* ss */ >> if (!mix_precomputed_dh(handshake->chaining_key, key, >> handshake->precomputed_static_static)) >> goto out; >> >> /* {t} */ >> tai64n_now(timestamp); >> message_encrypt(dst->encrypted_timestamp, timestamp, >> NOISE_TIMESTAMP_LEN, key, handshake->hash); >> >> dst->sender_index = wg_index_hashtable_insert( >> handshake->entry.peer->device->index_hashtable, >> &handshake->entry); >> >> handshake->state = HANDSHAKE_CREATED_INITIATION; >> ret = true; >> >> out: >> up_write(&handshake->lock); >> up_read(&handshake->static_identity->lock); >> >> == thread 1 == >> >> down_write(&handshake->lock); >> /* It's important to check that the state is still the same, while we >> * have an exclusive lock. >> */ >> if (handshake->state != state) { >> up_write(&handshake->lock); >> goto fail; >> } >> memcpy(handshake->remote_ephemeral, e, NOISE_PUBLIC_KEY_LEN); >> memcpy(handshake->hash, hash, NOISE_HASH_LEN); >> memcpy(handshake->chaining_key, chaining_key, NOISE_HASH_LEN); >> handshake->remote_index = src->sender_index; >> handshake->state = HANDSHAKE_CONSUMED_RESPONSE; >> up_write(&handshake->lock); >> ret_peer = peer; >> goto out; >> >> And now begin_session is called on the older completed handshake >> rather >> than the half-completed newer handshake? >> >> Or did you see some other flow? > > Okay I think I worked something plausible out: > > - two threads begin processing the same response > - the first succeeds. the second gets halfway, when it blocks on taking > a lock that a queued initiation has taken > - the queued initiation does its thing and resets the state > - the second thread resumes and completes the old handshake and > reinstalls keys > > I'll continue analyzing real world feasibility, but in all cases, thank > you for the patch. > > Jason Hello Jason, Thanks for your quick reply. Sorry that I could not answer in time. Your analysis is indeed the scenario that I had in mind, yet, the second thread does not really have to block, I'd say it can just be slow? As for real world feasibility and impacts, I must admit that I have no idea. Regards, J?r?my From Jason at zx2c4.com Tue Oct 6 15:18:58 2026 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Tue, 6 Oct 2026 17:18:58 +0200 Subject: [PATCH net] wireguard: noise: reject responses for replaced initiations In-Reply-To: References: <20261005203555.3552816-2-Jeremy.Jean@oss.cyber.gouv.fr> Message-ID: Hi J?r?my, On Tue, Oct 6, 2026 at 2:40?PM J?r?my Jean wrote: > Thanks for your quick reply. Sorry that I could not answer in time. > Your analysis is indeed the scenario that I had in mind, yet, the > second thread does not really have to block, I'd say it can just > be slow? That's right. > As for real world feasibility and impacts, I must admit that > I have no idea. I've been unable to synthetically reproduce this -- even with adding sleeps and additional synchronization primitives to the code to force it. But it does seem like a real issue in theory. So thanks for the patch. I'll add some detail to the commit message, and get this upstream and backported and such. Jason From zack at philomathiclife.com Tue Oct 6 15:40:34 2026 From: zack at philomathiclife.com (Zack Newman) Date: Tue, 6 Oct 2026 09:40:34 -0600 Subject: OpenBSD collaboration/peer-review request In-Reply-To: References: <188dc861-f452-4830-9b47-e51c15faf41d@philomathiclife.com> Message-ID: On 10/6/26 5:28 AM, Jason A. Donenfeld wrote: > Hi Zack, > > Of course I'd be happy to work on this. Do you (or someone else) want > to jump on a video call and we can share screens and you can show me > the stuff you think I should be looking at? > > Jason Hi Jason, Unfortunately it appears the devs aren't interested in continued collaboration or even using the Linux implementation as a rough guide. I was told privately to just "go use Linux" by Theo in his typical Theo way; and that's in fact what I've decided to do for my machines that are reliant on WireGuard since I demand reliability, compatibility, and security for something as important as a VPN. I'm sorry I can't be of more help. I'm not sure how this affects the status of wireguard-openbsd. The WireGuard implementation in OpenBSD is certainly active, but the wireguard-openbsd repo itself is quite a bit different than what is actually in the kernel. Over the last five years there have been issues with WireGuard in particular with its mbuf handling. I wish the devs would have continued their collaboration, but such is life. Anyway, feel free to disregard my request; and thank you for your time. Zack From Jason at zx2c4.com Tue Oct 6 16:02:36 2026 From: Jason at zx2c4.com (Jason A. Donenfeld) Date: Tue, 6 Oct 2026 18:02:36 +0200 Subject: OpenBSD collaboration/peer-review request In-Reply-To: References: <188dc861-f452-4830-9b47-e51c15faf41d@philomathiclife.com> Message-ID: Hi Zack, Oh, darn, that's really too bad. I'm CC'ing Matt on the thread; he was supposed to be in charge of keeping things sane. I don't like throw-it-over-the-fence coding. Have you witness insane things happening in the code base? Or are you just worried about the churn, in theory, going bad? Jason From zack at philomathiclife.com Tue Oct 6 17:31:31 2026 From: zack at philomathiclife.com (Zack Newman) Date: Tue, 6 Oct 2026 11:31:31 -0600 Subject: OpenBSD collaboration/peer-review request In-Reply-To: References: <188dc861-f452-4830-9b47-e51c15faf41d@philomathiclife.com> Message-ID: On 10/6/26 10:02 AM, Jason A. Donenfeld wrote: > Hi Zack, > > Oh, darn, that's really too bad. I'm CC'ing Matt on the thread; he was > supposed to be in charge of keeping things sane. I don't like > throw-it-over-the-fence coding. > > Have you witness insane things happening in the code base? Or are you > just worried about the churn, in theory, going bad? > > Jason Hi Jason, I'm not a C nor kernel developer, and I don't keep track of actual code changes. I can't speak about "insane things"; thus I would say I'm worried about churn going bad. I also don't want to come off as sensationalist. I would be surprised if anything were found that constituted as a security issue. I imagine the bugs that exist are reliability and compatibility ones. I can also be idyllic, so perhaps my expectations were unrealistic. I understand why you and the team care about how WireGuard is implemented because it can give it a bad name when it's done poorly, but I also understand the hesitation projects, especially relatively small projects like OpenBSD, have of requiring your team's involvement. The idealist in me likes code to be peer reviewed and collaboration to occur when possible. While I think the quality of code in general is rather high for OpenBSD, I don't subscribe to the idea that I should "just trust the developers". You can grok the OpenBSD mail archives and find issues over the last five years related to WireGuard. The biggest problems were kernel panics caused by mbuf mishandling. What ensued was what I'd personally consider a series of hodepodge patches hoping the bugs would be fixed. This is pure speculation, but I imagine the devs were hesitant to touch too much of the WireGuard code out of fear of mishandling it. Despite being somewhat concerned with how these WireGuard issues were handled, I selfishly/lazily ignored it since I wasn't obviously impacted (e.g., I never experienced kernel panics). Eventually one of the mbuf experts decided to take a more thorough attempt at fixing these issues, and he seemingly fixed the mbuf mishandling. Unfortunately by his own admission, he doesn't really use WireGuard; and his patch removed the padding "clamp" that would cause the MTU to be exceeded. I was affected by this bug and took days to figure out what was the culprit. After raising the issue, it was suggested to add the clamp when the plaintext was less than or equal to the MTU; but no special handling was suggested when the plaintext already exceeded the MTU. Unconvinced by this patch, I consulted the Linux implementation to see how it handles that situation and found that it indeed handles that situation specially. An LLM-Claude Opus 5.5 if that's relevant-also suggested that while Linux consistently uses the same MTU in the WireGuard code as the networking stack does when passing the plaintext to WireGuard, OpenBSD unconditionally uses the interface's MTU in the WireGuard code even though, like Linux, its networking stack uses the routing MTU. This causes yet another difference in padding behavior. Anyway, this is all to say that I personally would have preferred this been handled differently. Namely instead of putting off these bugs for a more legitimate fix, collaborate with the WireGuard team to ensure everything looks good while letting the mbuf experts handle what they're comfortable with. For whatever reason it appears no attempts were made to collaborate with your team though. Zack From d at ilvokhin.com Wed Oct 7 10:21:42 2026 From: d at ilvokhin.com (Dmitry Ilvokhin) Date: Wed, 7 Oct 2026 10:21:42 +0000 Subject: [PATCH] wg-quick: darwin: fix set_dns() and del_dns() race In-Reply-To: <20260930103041.102065-1-d@ilvokhin.com> References: <20260930103041.102065-1-d@ilvokhin.com> Message-ID: On Wed, Sep 30, 2026 at 10:30:41AM +0000, Dmitry Ilvokhin wrote: > The monitor deamon restores the DNS servers by running del_dns() from > its INT, TERM and EXIT trap. Every route event also schedules an ALRM 2 > seconds later, and the ALRM trap runs set_dns(). > > If ALRM arrives during cleanup, set_dns() can run in the middle of > del_dns(), and the DNS servers may not be restored after the WireGuard > interface is removed. > > Ignore ALRM in cleanup signal handler to avoid this race. > > Signed-off-by: Dmitry Ilvokhin > --- > Resending the patch, since mailing list didn't accept it first time, > because I was not a member of the list. Hello Jason, Any thoughts on the patch? I applied this patch locally, tested it for a few weeks and never hit the race again. I'd love to get the fix upstream. From syzbot+d0d2f1a65f45b319d25d at syzkaller.appspotmail.com Sat Oct 10 01:59:27 2026 From: syzbot+d0d2f1a65f45b319d25d at syzkaller.appspotmail.com (syzbot) Date: Fri, 09 Oct 2026 18:59:27 -0700 Subject: [syzbot] [wireguard?] BUG: workqueue lockup in wg_packet_decrypt_worker In-Reply-To: <6aa9b9b7.f81106d8.2ab401.006f.GAE@google.com> Message-ID: <6ac99bff.8cdf6287.1eb53.006f.GAE@google.com> syzbot has found a reproducer for the following issue on: HEAD commit: 71c3e5409eef Merge remote-tracking branch 'will/for-next/p.. git tree: git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git for-kernelci console output: https://syzkaller.appspot.com/x/log.txt?x=13fb40a5580000 kernel config: https://syzkaller.appspot.com/x/.config?x=42bd821301b42742 dashboard link: https://syzkaller.appspot.com/bug?extid=d0d2f1a65f45b319d25d compiler: Debian clang version 22.1.8 (++20260613092233+e80beda6e255-1~exp1~20260613092250.77), Debian LLD 22.1.8 userspace arch: arm64 syz repro: https://syzkaller.appspot.com/x/repro.syz?x=171efa8e580000 C reproducer: https://syzkaller.appspot.com/x/repro.c?x=1030649c580000 Downloadable assets: disk image: https://storage.googleapis.com/syzbot-assets/7596ece97a32/disk-71c3e540.raw.xz vmlinux: https://storage.googleapis.com/syzbot-assets/4d2c36351098/vmlinux-71c3e540.xz kernel image: https://storage.googleapis.com/syzbot-assets/396ce0b85b3c/Image-71c3e540.gz.xz IMPORTANT: if you fix the issue, please add the following tag to the commit: Reported-by: syzbot+d0d2f1a65f45b319d25d at syzkaller.appspotmail.com bridge0: received packet on veth1_to_bridge with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0) bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:1c, vlan:0) BUG: workqueue lockup - pool cpus=0 node=0 flags=0x2 nice=0 stuck for 58s! Showing busy workqueues and worker pools: workqueue events: flags=0x100 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=12 refcnt=13 in-flight: 4802:switchdev_deferred_process_work for 64s ,9:request_firmware_work_func for 147s ,4923:drm_fb_helper_damage_work for 58s ,10:nsim_fib_event_work for 68s pending: 2*ovs_dp_masks_rebalance, 2*nsim_dev_hwstats_traffic_work, psi_avgs_work, drm_fb_helper_damage_work, vmstat_shepherd, free_obj_work pwq 6: cpus=1 node=0 flags=0x0 nice=0 active=3 refcnt=4 in-flight: 26:free_ipc for 13s pending: nsim_dev_hwstats_traffic_work, psi_avgs_work workqueue events_unbound: flags=0x2 pwq 8: cpus=0-1 flags=0x6 nice=0 active=4 refcnt=5 in-flight: 4976:cfg80211_wiphy_work for 110s cfg80211_wiphy_work ,1204:cfg80211_wiphy_work for 114s cfg80211_wiphy_work pwq 8: cpus=0-1 flags=0x6 nice=0 active=12 refcnt=13 in-flight: 240:cfg80211_wiphy_work for 48s ,14:cfg80211_wiphy_work for 112s cfg80211_wiphy_work ,4979:nsim_dev_trap_report_work for 0s ,48:nsim_dev_trap_report_work for 0s ,1190:cfg80211_wiphy_work for 105s cfg80211_wiphy_work ,4980:nsim_dev_trap_report_work for 0s ,4978:macvlan_process_broadcast for 3s pending: flush_memcg_stats_dwork, nsim_dev_trap_report_work, toggle_allocation_gate workqueue events_power_efficient: flags=0x82 pwq 8: cpus=0-1 flags=0x6 nice=0 active=5 refcnt=6 in-flight: 3700:neigh_periodic_work for 36s pending: 2*neigh_managed_work, fb_flashcursor, gc_worker workqueue rcu_gp: flags=0x108 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=1 refcnt=4 MAYDAY pending: srcu_invoke_callbacks workqueue netns: flags=0x6000a pwq 8: cpus=0-1 flags=0x6 nice=0 active=1 refcnt=4 in-flight: 12:cleanup_net for 116s workqueue mm_percpu_wq: flags=0x108 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=1 refcnt=4 MAYDAY pending: vmstat_update workqueue writeback: flags=0x4a pwq 8: cpus=0-1 flags=0x6 nice=0 active=1 refcnt=2 in-flight: 59:wb_workfn for 111s workqueue mld: flags=0x40108 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=1 refcnt=55 MAYDAY pending: mld_dad_work inactive: mld_dad_work, 3*mld_ifc_work, mld_dad_work, 5*mld_ifc_work, 2*mld_dad_work, mld_ifc_work, mld_dad_work, 3*mld_ifc_work, mld_dad_work, 9*mld_ifc_work, 25*mld_dad_work pwq 6: cpus=1 node=0 flags=0x0 nice=0 active=1 refcnt=79 MAYDAY in-flight: 1495:mld_ifc_work for 0s inactive: mld_ifc_work, mld_dad_work, mld_ifc_work, 3*mld_dad_work, 4*mld_ifc_work, 2*mld_dad_work, mld_ifc_work, 2*mld_dad_work, mld_ifc_work, mld_dad_work, mld_ifc_work, 2*mld_dad_work, 4*mld_ifc_work, mld_dad_work, 4*mld_ifc_work, 2*mld_dad_work, 4*mld_ifc_work, 3*mld_dad_work, 4*mld_ifc_work, 4*mld_dad_work, 4*mld_ifc_work, mld_dad_work, 4*mld_ifc_work, mld_dad_work, 3*mld_ifc_work, 2*mld_dad_work, 4*mld_ifc_work, 10*mld_dad_work workqueue ipv6_addrconf: flags=0x6000a pwq 8: cpus=0-1 flags=0x6 nice=0 active=1 refcnt=222 MAYDAY in-flight: 2825(RESCUER):addrconf_dad_work for 64s inactive: 215*addrconf_dad_work, addrconf_verify_work workqueue bat_events: flags=0x6000a pwq 8: cpus=0-1 flags=0x6 nice=0 active=1 refcnt=50 MAYDAY in-flight: 2857(RESCUER):batadv_tt_purge for 1s pending: mayday_cursor_func inactive: batadv_mcast_mla_update, 2*batadv_iv_send_outstanding_bat_ogm_packet, 4*batadv_mcast_mla_update, batadv_iv_send_outstanding_bat_ogm_packet, batadv_purge_orig, 9*batadv_iv_send_outstanding_bat_ogm_packet, batadv_mcast_mla_update, 3*batadv_purge_orig, 4*batadv_iv_send_outstanding_bat_ogm_packet, 2*batadv_purge_orig, batadv_dat_purge, batadv_bla_periodic_work, batadv_dat_purge, batadv_bla_periodic_work, batadv_dat_purge, batadv_bla_periodic_work, batadv_dat_purge, batadv_bla_periodic_work, batadv_dat_purge, batadv_bla_periodic_work, batadv_dat_purge, batadv_bla_periodic_work, 4*batadv_tt_purge workqueue wg-kex-wg0: flags=0x124 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=1 refcnt=2 pending: wg_packet_handshake_receive_worker pwq 6: cpus=1 node=0 flags=0x0 nice=0 active=1 refcnt=2 in-flight: 24:wg_packet_handshake_receive_worker for 1s workqueue wg-crypt-wg0: flags=0x128 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=3 refcnt=6 MAYDAY pending: wg_packet_encrypt_worker, wg_packet_tx_worker, wg_packet_decrypt_worker workqueue wg-crypt-wg1: flags=0x128 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=3 refcnt=6 MAYDAY pending: wg_packet_encrypt_worker, wg_packet_decrypt_worker, wg_packet_tx_worker pwq 6: cpus=1 node=0 flags=0x0 nice=0 active=1 refcnt=3 MAYDAY pending: wg_packet_decrypt_worker workqueue wg-kex-wg2: flags=0x124 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=1 refcnt=2 pending: wg_packet_handshake_receive_worker pwq 6: cpus=1 node=0 flags=0x0 nice=0 active=1 refcnt=2 pending: wg_packet_handshake_receive_worker workqueue wg-crypt-wg2: flags=0x128 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=4 refcnt=7 MAYDAY in-flight: 4811:wg_packet_decrypt_worker for 64s wg_packet_decrypt_worker pending: wg_packet_encrypt_worker, wg_packet_tx_worker workqueue wg-kex-wg0: flags=0x6 pwq 8: cpus=0-1 flags=0x6 nice=0 active=1 refcnt=2 pending: wg_packet_handshake_send_worker workqueue wg-crypt-wg0: flags=0x128 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=4 refcnt=7 MAYDAY in-flight: 4471:wg_packet_decrypt_worker for 63s pending: wg_packet_decrypt_worker, wg_packet_encrypt_worker, wg_packet_tx_worker workqueue wg-kex-wg0: flags=0x124 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=1 refcnt=2 pending: wg_packet_handshake_receive_worker workqueue wg-kex-wg0: flags=0x6 pwq 8: cpus=0-1 flags=0x6 nice=0 active=1 refcnt=2 pending: wg_packet_handshake_send_worker workqueue wg-crypt-wg0: flags=0x128 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=3 refcnt=6 MAYDAY pending: wg_packet_encrypt_worker, wg_packet_decrypt_worker, wg_packet_tx_worker pwq 6: cpus=1 node=0 flags=0x0 nice=0 active=1 refcnt=3 MAYDAY pending: wg_packet_decrypt_worker workqueue wg-crypt-wg1: flags=0x128 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=3 refcnt=6 MAYDAY pending: wg_packet_decrypt_worker, wg_packet_encrypt_worker, wg_packet_tx_worker workqueue wg-kex-wg1: flags=0x124 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=1 refcnt=2 pending: wg_packet_handshake_receive_worker workqueue wg-crypt-wg1: flags=0x128 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=3 refcnt=6 MAYDAY pending: wg_packet_decrypt_worker, wg_packet_encrypt_worker, wg_packet_tx_worker pwq 6: cpus=1 node=0 flags=0x0 nice=0 active=2 refcnt=4 MAYDAY pending: wg_packet_decrypt_worker, wg_packet_tx_worker workqueue wg-crypt-wg2: flags=0x128 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=3 refcnt=6 MAYDAY pending: wg_packet_decrypt_worker, wg_packet_encrypt_worker, wg_packet_tx_worker workqueue wg-kex-wg2: flags=0x124 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=2 refcnt=3 in-flight: 4977:wg_packet_handshake_receive_worker for 107s wg_packet_handshake_receive_worker pwq 6: cpus=1 node=0 flags=0x0 nice=0 active=2 refcnt=3 in-flight: 1496:wg_packet_handshake_receive_worker for 12s wg_packet_handshake_receive_worker workqueue wg-crypt-wg2: flags=0x128 pwq 2: cpus=0 node=0 flags=0x2 nice=0 active=3 refcnt=6 MAYDAY pending: wg_packet_tx_worker, wg_packet_decrypt_worker, wg_packet_encrypt_worker pool 2: cpus=0 node=0 flags=0x2 nice=0 hung=58s workers=8 manager: 1091 pool 6: cpus=1 node=0 flags=0x0 nice=0 hung=0s workers=7 idle: 4930 866 4981 pool 8: cpus=0-1 flags=0x6 nice=0 hung=0s workers=13 manager: 40 Showing backtraces of busy workers in stalled worker pools: pool 2: task:kworker/0:5 state:R running task stack:0 pid:4811 tgid:4811 ppid:2 task_flags:0x4208060 flags:0x20000010 Workqueue: wg-crypt-wg2 wg_packet_decrypt_worker Call trace: __switch_to+0x2b4/0x6e4 arch/arm64/kernel/process.c:775 (T) context_switch kernel/sched/core.c:5533 [inline] __schedule+0x135c/0x2b00 kernel/sched/core.c:7284 preempt_schedule_common+0xd4/0x190 kernel/sched/core.c:7463 preempt_schedule+0x60/0x78 kernel/sched/core.c:7487 __local_bh_enable_ip+0x16c/0x234 kernel/softirq.c:480 local_bh_enable include/linux/bottom_half.h:33 [inline] put_cpu_fpsimd_context arch/arm64/kernel/fpsimd.c:251 [inline] kernel_neon_begin+0x29c/0x400 arch/arm64/kernel/fpsimd.c:1924 class_ksimd_constructor arch/arm64/include/asm/simd.h:48 [inline] poly1305_blocks+0x110/0x204 lib/crypto/arm64/poly1305.h:30 poly1305_update+0x168/0x39c lib/crypto/poly1305.c:59 chacha20poly1305_crypt_sg_inplace+0x888/0xc30 lib/crypto/chacha20poly1305.c:304 chacha20poly1305_decrypt_sg_inplace+0x6c/0x94 lib/crypto/chacha20poly1305.c:353 decrypt_packet drivers/net/wireguard/receive.c:278 [inline] wg_packet_decrypt_worker+0x490/0xa00 drivers/net/wireguard/receive.c:501 process_one_work kernel/workqueue.c:3396 [inline] process_scheduled_works+0x91c/0x1250 kernel/workqueue.c:3479 worker_thread+0x798/0xbd0 kernel/workqueue.c:3560 kthread+0x304/0x3d4 kernel/kthread.c:436 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:854 pool 2: task:kworker/0:7 state:R running task stack:0 pid:4977 tgid:4977 ppid:2 task_flags:0x4208060 flags:0x20000010 Workqueue: wg-kex-wg2 wg_packet_handshake_receive_worker Call trace: __switch_to+0x2b4/0x6e4 arch/arm64/kernel/process.c:775 (T) context_switch kernel/sched/core.c:5533 [inline] __schedule+0x135c/0x2b00 kernel/sched/core.c:7284 preempt_schedule_common+0xd4/0x190 kernel/sched/core.c:7463 preempt_schedule+0x60/0x78 kernel/sched/core.c:7487 __local_bh_enable_ip+0x16c/0x234 kernel/softirq.c:480 local_bh_enable include/linux/bottom_half.h:33 [inline] put_cpu_fpsimd_context arch/arm64/kernel/fpsimd.c:251 [inline] kernel_neon_begin+0x29c/0x400 arch/arm64/kernel/fpsimd.c:1924 class_ksimd_constructor arch/arm64/include/asm/simd.h:48 [inline] poly1305_blocks+0x110/0x204 lib/crypto/arm64/poly1305.h:30 poly1305_update+0x168/0x39c lib/crypto/poly1305.c:59 __chacha20poly1305_decrypt+0x240/0x39c lib/crypto/chacha20poly1305.c:151 chacha20poly1305_decrypt+0x238/0x5e4 lib/crypto/chacha20poly1305.c:186 message_decrypt+0x50/0x90 drivers/net/wireguard/noise.c:477 wg_noise_handshake_consume_initiation+0x1a0/0x53c drivers/net/wireguard/noise.c:612 wg_receive_handshake_packet drivers/net/wireguard/receive.c:144 [inline] wg_packet_handshake_receive_worker+0x54c/0xcf8 drivers/net/wireguard/receive.c:213 process_one_work kernel/workqueue.c:3396 [inline] process_scheduled_works+0x91c/0x1250 kernel/workqueue.c:3479 worker_thread+0x798/0xbd0 kernel/workqueue.c:3560 kthread+0x304/0x3d4 kernel/kthread.c:436 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:854 pool 2: task:kworker/0:6 state:R running task stack:0 pid:4923 tgid:4923 ppid:2 task_flags:0x4208060 flags:0x00000010 Workqueue: events drm_fb_helper_damage_work Call trace: __switch_to+0x2b4/0x6e4 arch/arm64/kernel/process.c:775 (T) context_switch kernel/sched/core.c:5533 [inline] __schedule+0x135c/0x2b00 kernel/sched/core.c:7284 __schedule_loop kernel/sched/core.c:7361 [inline] schedule+0x13c/0x20c kernel/sched/core.c:7376 schedule_timeout+0x13c/0x28c kernel/time/sleep_timeout.c:99 drm_crtc_wait_one_vblank+0x19c/0x440 drivers/gpu/drm/drm_vblank.c:1325 drm_client_modeset_wait_for_vblank+0xc4/0xec drivers/gpu/drm/drm_client_modeset.c:1331 drm_fb_helper_fb_dirty drivers/gpu/drm/drm_fb_helper.c:327 [inline] drm_fb_helper_damage_work+0x520/0xbc0 drivers/gpu/drm/drm_fb_helper.c:365 process_one_work kernel/workqueue.c:3396 [inline] process_scheduled_works+0x91c/0x1250 kernel/workqueue.c:3479 worker_thread+0x798/0xbd0 kernel/workqueue.c:3560 kthread+0x304/0x3d4 kernel/kthread.c:436 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:854 pool 2: task:kworker/0:1 state:R running task stack:0 pid:10 tgid:10 ppid:2 task_flags:0x4208060 flags:0x00000010 Workqueue: events nsim_fib_event_work Call trace: __switch_to+0x2b4/0x6e4 arch/arm64/kernel/process.c:775 (T) context_switch kernel/sched/core.c:5533 [inline] __schedule+0x135c/0x2b00 kernel/sched/core.c:7284 __schedule_loop kernel/sched/core.c:7361 [inline] schedule+0x13c/0x20c kernel/sched/core.c:7376 schedule_timeout+0x13c/0x28c kernel/time/sleep_timeout.c:99 schedule_timeout_uninterruptible+0x78/0xbc kernel/time/sleep_timeout.c:158 msleep+0x3c/0x68 kernel/time/sleep_timeout.c:318 nsim_fib6_rt_add drivers/net/netdevsim/fib.c:693 [inline] nsim_fib6_rt_insert drivers/net/netdevsim/fib.c:759 [inline] nsim_fib6_event drivers/net/netdevsim/fib.c:856 [inline] nsim_fib_event+0x57a0/0x60c4 drivers/net/netdevsim/fib.c:889 nsim_fib_event_work+0x1d8/0x320 drivers/net/netdevsim/fib.c:1493 process_one_work kernel/workqueue.c:3396 [inline] process_scheduled_works+0x91c/0x1250 kernel/workqueue.c:3479 worker_thread+0x798/0xbd0 kernel/workqueue.c:3560 kthread+0x304/0x3d4 kernel/kthread.c:436 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:854 pool 2: task:kworker/0:3 state:R running task stack:0 pid:4471 tgid:4471 ppid:2 task_flags:0x4208060 flags:0x00000010 Workqueue: wg-crypt-wg0 wg_packet_decrypt_worker Call trace: __switch_to+0x2b4/0x6e4 arch/arm64/kernel/process.c:775 (T) context_switch kernel/sched/core.c:5533 [inline] __schedule+0x135c/0x2b00 kernel/sched/core.c:7284 preempt_schedule_common+0xd4/0x190 kernel/sched/core.c:7463 preempt_schedule+0x60/0x78 kernel/sched/core.c:7487 __local_bh_enable_ip+0x16c/0x234 kernel/softirq.c:480 __raw_spin_unlock_bh include/linux/spinlock_api_smp.h:237 [inline] _raw_spin_unlock_bh+0x3c/0x4c kernel/locking/spinlock.c:245 spin_unlock_bh include/linux/spinlock.h:407 [inline] ptr_ring_consume_bh include/linux/ptr_ring.h:399 [inline] wg_packet_decrypt_worker+0x254/0xa00 drivers/net/wireguard/receive.c:499 process_one_work kernel/workqueue.c:3396 [inline] process_scheduled_works+0x91c/0x1250 kernel/workqueue.c:3479 worker_thread+0x798/0xbd0 kernel/workqueue.c:3560 kthread+0x304/0x3d4 kernel/kthread.c:436 ret_from_fork+0x10/0x20 arch/arm64/kernel/entry.S:854 net_ratelimit: 70318 callbacks suppressed bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0) bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0) bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:1c, vlan:0) bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0) bridge0: received packet on veth1_to_bridge with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0) bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:1c, vlan:0) bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0) bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:0c, vlan:0) bridge0: received packet on bridge_slave_1 with own address as source address (addr:aa:aa:aa:aa:aa:1c, vlan:0) --- If you want syzbot to run the reproducer, reply with: #syz test: git://repo/address.git branch-or-commit-hash If you attach or paste a git patch, syzbot will apply it before testing. From moody at posixcafe.org Mon Oct 12 19:14:52 2026 From: moody at posixcafe.org (Jacob Moody) Date: Mon, 12 Oct 2026 14:14:52 -0500 Subject: Implementation for the 9front operating system In-Reply-To: <0ab57e49-8904-4e4b-bc3a-38e10b214ab3@posixcafe.org> References: <0ab57e49-8904-4e4b-bc3a-38e10b214ab3@posixcafe.org> Message-ID: On 8/4/26 16:08, Jacob Moody wrote: > All, > > I've been working on an implementation of wireguard for 9front. > I am starting to get to the point where I feel like the implementation is getting > pretty close, and its in a good spot to review before I do some tighter integration into 9front. > > I know in the past I've seen some desire from the wg community to ensure that code using the wireguard > name at least be somewhat decent. So I wanted to share it here and get feedback if people felt inclined. > > Grab the code from your choice of git hosts: > git://shithub.us/moody/wg > https://github.com/majiru/wg > > I am happy to answer any questions regarding plan 9/9front specifics. > > One note is that there's some stubs right now, the dynamic runtime addition > and subtraction of peers, and likewise the condition under which we send > cookie's is not implemented, that's pending the more tighter integration. > > Thanks, > moody > Just giving a bump to this, sorry I had sent two mails because I was not sure if the first got dropped with the moderation queue taking so long. It's totally fine if there's no interest, 9front is fairly niche, I just wanted to avoid a faux pas in shipping something without consulting the broader community. With that said, after haven taken a break from this for a bit I plan to work on this a bit more and upstream it. If folks are interested but just need more time to look and would prefer that happen before I merge anything just LMK. Thanks, moody