Detailed Technical Analysis
1. Root Cause Analysis
The crash report shows a KASAN use-after-free read of 8 bytes at address 0xffff88814d151c00 inside __queue_work+0x609/0x1360 (kernel/workqueue.c:2463):
pwq = rcu_dereference(*per_cpu_ptr(wq->cpu_pwq, cpu));
pool = pwq->pool;
In struct pool_workqueue, the pointer struct worker_pool *pool is located at offset 0 (pool = pwq->pool). The address 0xffff88814d151c00 corresponds to a struct pool_workqueue (pwq) that resides on a slab page which has already been returned to the page allocator (KASAN_PAGE_FREE shadow value 0xff, physical page refcount 0).
The invocation originates from the WireGuard packet reception path:
wg_packet_handshake_send_worker() (drivers/net/wireguard/send.c:51) executes on workqueue wg-kex-wg1 to send a handshake initiation.
- In
wg_socket_send_skb_to_peer(), read_unlock_bh(&peer->endpoint_lock) enables bottom-halves (__local_bh_enable_ip), invoking pending softirqs.
- Softirq processing (
net_rx_action -> process_backlog -> udp_rcv -> wg_receive) receives a packet destined for a WireGuard UDP tunnel socket.
wg_receive() retrieves wg = sk->sk_user_data and invokes wg_packet_receive(wg, skb).
- For incoming handshake packets,
wg_packet_receive() chooses a CPU via wg_cpumask_next_online(&wg->handshake_queue.last_cpu) and attempts to enqueue work on wg->handshake_receive_wq:
queue_work_on(cpu, wg->handshake_receive_wq,
&per_cpu_ptr(wg->handshake_queue.worker, cpu)->work);
- In
__queue_work() (kernel/workqueue.c:2462-2463), the target CPU is passed as req_cpu. Under RCU read lock, pwq = rcu_dereference(*per_cpu_ptr(wq->cpu_pwq, cpu)) is dereferenced.
- If the
pool_workqueue (or workqueue attributes/topology) was destroyed or reallocated (for example during CPU offline/hotplug operations or asynchronous workqueue teardown), pwq points to memory that has been reclaimed by RCU (kfree_rcu(pwq, rcu)). Dereferencing pwq->pool at offset 0 accesses freed memory.
2. Exact Functions, Structs, and Files Involved
- Files:
- Functions:
wg_packet_receive()
queue_work_on() / __queue_work()
- Structs and Fields:
struct wg_device: handshake_receive_wq, handshake_queue
struct workqueue_struct: cpu_pwq
struct pool_workqueue: pool
3. Runtime Consequences in a Production Kernel
- MemoryCorruption:
true. Dereferencing freed memory inside __queue_work() leads to accessing wild/stale pool pointers and acquiring uninitialized/stale spinlocks (raw_spin_lock(&pool->lock)). Subsequent operations attempt to manipulate linked lists (insert_work()), leading to arbitrary kernel memory corruption.
- InfoLeak:
none. The defect does not expose or copy kernel memory buffers to userspace or network interfaces.
- RawDenialOfService:
true. The invalid dereference triggers a kernel oops / page fault panic in production environments.
- IsNetCandidate:
true. The issue occurs directly in network packet processing (WireGuard UDP encapsulation receiving path via net_rx_action / udp_rcv / wg_receive).
- IsKVMCandidate:
false. The issue is unrelated to KVM, virtualization, or guest/hypervisor boundaries.