[PR #206] fix: check IS_ERR(wg) before using the device in wg_set_device #235

Open
opened 2026-10-13 21:07:52 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/pull/206
Author: @ExzoTikStyle
Created: 8/1/2026
Status: 🔄 Open

Base: master ← Head: fix/header-protection-null-deref


📝 Commits (2)

  • eef998e fix: check IS_ERR(wg) before using the device in wg_set_device
  • 1f5690f fix: take the write lock when setting the header protection key

📊 Changes

2 files changed (+13 additions, -6 deletions)

View changed files

📝 src/header_protection.c (+9 -4)
📝 src/netlink.c (+4 -2)

📄 Description

wg_set_device() initialises has_protection from awg_has_header_protection(wg)
in the declaration block, above the IS_ERR(wg) check. When the request names an
interface that doesn't exist, lookup_interface() returns an ERR_PTR and the
helper dereferences it right away.

The oops lands inside down_read(), and genl_rcv_msg() holds the genl family
lock across the doit callback, so the lock is never released. Everything on the
amneziawg family blocks in uninterruptible sleep from then on — including
awg setconf for tunnels that were up and working — and genl_unregister_family()
hangs too, so the module can neither be used nor unloaded. Only a reboot clears it.
rmmod -f isn't a way out either, since stock Ubuntu kernels build without
CONFIG_MODULE_FORCE_UNLOAD.

BUG: kernel NULL pointer dereference, address: 000000000000050d
Oops: 0002 [#1] PREEMPT SMP NOPTI
RIP: 0010:down_read+0x1e/0xc0
Call Trace:
 <TASK>
 awg_has_header_protection+0x19/0x50 [amneziawg]
 wg_set_device+0x43/0xe50 [amneziawg]
 ? genl_family_rcv_msg_attrs_parse.constprop.0+0x93/0x100
 genl_family_rcv_msg_doit+0xfa/0x160
 genl_family_rcv_msg+0x189/0x260
 ? __pfx_wg_set_device+0x10/0x10 [amneziawg]
 genl_rcv_msg+0x4c/0xb0
 genl_rcv+0x28/0x50
 </TASK>
note: awg[80691] exited with irqs disabled
note: awg[80691] exited with preempt_count 1

CR2 being 0x50d rather than a near-zero address is the ERR_PTR giveaway:
(void *)-19 plus the offset of header_protection.lock in struct wg_device.

The call is the left operand of the ||, so it runs whether or not
WGDEVICE_A_HEADER_PROTECTION_KEY was passed — a plain
awg set doesnotexist listen-port 51820 reaches it too. Introduced in #192.

The first commit moves the check above the use. It also adds an
IS_ERR_OR_NULL() guard inside awg_has_header_protection(), since it takes a
device pointer from several call sites.

The second commit is unrelated to the crash but in the same file:
awg_header_protection_set_key() writes p->key[] and p->has_protection
while holding only down_read(). Concurrent setters can interleave, and
awg_header_protection_init() can read a half-updated key while building the
chacha state — silently wrong header protection rather than a reported error.
Happy to split it out if you'd rather keep this PR to the crash.

Verification

Built and run against 6.8.0-136-generic (Ubuntu 22.04.5), on the same host
where the oops was originally hit.

Before, on 3.0.20260731-04 from ppa:amnezia/ppa:

# awg set doesnotexist header-protection-key /dev/null
Killed
# echo $?
137
# awg-quick up awg0        # hangs, unkillable
# ps -eo pid,stat,wchan:24,cmd | awk '$2 ~ /D/'
  81680 D    genl_rcv_msg             awg setconf awg0 /dev/fd/63
  82328 D    genl_unregister_fami     rmmod amneziawg

After, with this branch:

# awg set doesnotexist header-protection-key /dev/null
Unable to modify interface: No such device
# echo $?
1
# awg set doesnotexist listen-port 51820
Unable to modify interface: No such device
# echo $?
1
# ps -eo pid,stat,cmd | awk '$2 ~ /D/' | wc -l
0

dmesg is clean, and an existing AWG 3 tunnel (with HeaderProtectionKey set)
comes back up on the patched module and passes traffic as before.


🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/pull/206 **Author:** [@ExzoTikStyle](https://github.com/ExzoTikStyle) **Created:** 8/1/2026 **Status:** 🔄 Open **Base:** `master` ← **Head:** `fix/header-protection-null-deref` --- ### 📝 Commits (2) - [`eef998e`](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/commit/eef998e5e63209999ee94b2c581cd43b38250d78) fix: check IS_ERR(wg) before using the device in wg_set_device - [`1f5690f`](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module/commit/1f5690fb42f6fc7aa2203ff3556068ad8556584e) fix: take the write lock when setting the header protection key ### 📊 Changes **2 files changed** (+13 additions, -6 deletions) <details> <summary>View changed files</summary> 📝 `src/header_protection.c` (+9 -4) 📝 `src/netlink.c` (+4 -2) </details> ### 📄 Description `wg_set_device()` initialises `has_protection` from `awg_has_header_protection(wg)` in the declaration block, above the `IS_ERR(wg)` check. When the request names an interface that doesn't exist, `lookup_interface()` returns an `ERR_PTR` and the helper dereferences it right away. The oops lands inside `down_read()`, and `genl_rcv_msg()` holds the genl family lock across the `doit` callback, so the lock is never released. Everything on the `amneziawg` family blocks in uninterruptible sleep from then on — including `awg setconf` for tunnels that were up and working — and `genl_unregister_family()` hangs too, so the module can neither be used nor unloaded. Only a reboot clears it. `rmmod -f` isn't a way out either, since stock Ubuntu kernels build without `CONFIG_MODULE_FORCE_UNLOAD`. ``` BUG: kernel NULL pointer dereference, address: 000000000000050d Oops: 0002 [#1] PREEMPT SMP NOPTI RIP: 0010:down_read+0x1e/0xc0 Call Trace: <TASK> awg_has_header_protection+0x19/0x50 [amneziawg] wg_set_device+0x43/0xe50 [amneziawg] ? genl_family_rcv_msg_attrs_parse.constprop.0+0x93/0x100 genl_family_rcv_msg_doit+0xfa/0x160 genl_family_rcv_msg+0x189/0x260 ? __pfx_wg_set_device+0x10/0x10 [amneziawg] genl_rcv_msg+0x4c/0xb0 genl_rcv+0x28/0x50 </TASK> note: awg[80691] exited with irqs disabled note: awg[80691] exited with preempt_count 1 ``` `CR2` being `0x50d` rather than a near-zero address is the `ERR_PTR` giveaway: `(void *)-19` plus the offset of `header_protection.lock` in `struct wg_device`. The call is the left operand of the `||`, so it runs whether or not `WGDEVICE_A_HEADER_PROTECTION_KEY` was passed — a plain `awg set doesnotexist listen-port 51820` reaches it too. Introduced in #192. The first commit moves the check above the use. It also adds an `IS_ERR_OR_NULL()` guard inside `awg_has_header_protection()`, since it takes a device pointer from several call sites. The second commit is unrelated to the crash but in the same file: `awg_header_protection_set_key()` writes `p->key[]` and `p->has_protection` while holding only `down_read()`. Concurrent setters can interleave, and `awg_header_protection_init()` can read a half-updated key while building the chacha state — silently wrong header protection rather than a reported error. Happy to split it out if you'd rather keep this PR to the crash. ## Verification Built and run against 6.8.0-136-generic (Ubuntu 22.04.5), on the same host where the oops was originally hit. Before, on `3.0.20260731-04` from `ppa:amnezia/ppa`: ```console # awg set doesnotexist header-protection-key /dev/null Killed # echo $? 137 # awg-quick up awg0 # hangs, unkillable # ps -eo pid,stat,wchan:24,cmd | awk '$2 ~ /D/' 81680 D genl_rcv_msg awg setconf awg0 /dev/fd/63 82328 D genl_unregister_fami rmmod amneziawg ``` After, with this branch: ```console # awg set doesnotexist header-protection-key /dev/null Unable to modify interface: No such device # echo $? 1 # awg set doesnotexist listen-port 51820 Unable to modify interface: No such device # echo $? 1 # ps -eo pid,stat,cmd | awk '$2 ~ /D/' | wc -l 0 ``` `dmesg` is clean, and an existing AWG 3 tunnel (with `HeaderProtectionKey` set) comes back up on the patched module and passes traffic as before. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amneziawg-linux-kernel-module#235
No description provided.