aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Rozman <simon.rozman@amebis.si>2026-02-24 15:47:17 -0800
committerSimon Rozman <simon.rozman@amebis.si>2026-03-18 18:49:30 +0100
commitec0a6b98456fe1ba52567bb2add4bbf5f64315a1 (patch)
tree476257f3f18cca7ee32bcc26528b6d45de867cef
parentdriver: fix race condition at Ring->Head (diff)
downloadwintun-master.tar.xz
wintun-master.zip
driver: fix missed-wakeup race in ring buffer Alertable signalingHEADmaster
Add MemoryBarrier() between store-load pairs in the Dekker-style synchronization used by the Receive ring's Alertable/Tail protocol. On x86-64, WriteRelease/ReadAcquire only prevent compiler reordering and provide acquire/release semantics, but do not emit MFENCE — the only instruction that prevents store-load reordering across cores. Without a full barrier, both the userspace producer and the kernel consumer can simultaneously read stale values: Userspace: STORE(Tail) ... LOAD(Alertable) -> sees FALSE (stale) Driver: STORE(Alertable=TRUE) ... LOAD(Tail) -> sees old tail The driver then enters KeWaitForMultipleObjects with no pending SetEvent, sleeping until a TCP retransmission (typically 4-5s later) re-triggers the send path and wins the race. The fix adds MemoryBarrier() (MFENCE on x86) on both sides: - api/session.c WintunSendPacket: between WriteULongRelease(Tail) and ReadAcquire(Alertable) - driver/twintun.c TunProcessReceiveData: between WriteRelease(Alertable, TRUE) and ReadULongAcquire(Tail) This guarantees that at least one side always observes the other's store, preventing the missed wakeup while preserving the Alertable optimization that avoids unnecessary SetEvent syscalls. Reported-by: Alexey Lapuka <alexey@twingate.com> Reference: https://lists.zx2c4.com/pipermail/wireguard/2026-February/009523.html Signed-off-by: Simon Rozman <simon.rozman@amebis.si>
-rw-r--r--api/session.c1
-rw-r--r--driver/wintun.c1
2 files changed, 2 insertions, 0 deletions
diff --git a/api/session.c b/api/session.c
index ab96c64..13d5bca 100644
--- a/api/session.c
+++ b/api/session.c
@@ -302,6 +302,7 @@ WintunSendPacket(TUN_SESSION *Session, const BYTE *Packet)
if (Session->Descriptor.Receive.Ring->Tail != Session->Receive.TailRelease)
{
WriteULongRelease(&Session->Descriptor.Receive.Ring->Tail, Session->Receive.TailRelease);
+ MemoryBarrier();
if (ReadAcquire(&Session->Descriptor.Receive.Ring->Alertable))
SetEvent(Session->Descriptor.Receive.TailMoved);
}
diff --git a/driver/wintun.c b/driver/wintun.c
index 82e346b..72ba5d3 100644
--- a/driver/wintun.c
+++ b/driver/wintun.c
@@ -481,6 +481,7 @@ TunProcessReceiveData(_Inout_ TUN_CTX *Ctx)
if (RingHead == RingTail)
{
WriteRelease(&Ring->Alertable, TRUE);
+ MemoryBarrier();
RingTail = ReadULongAcquire(&Ring->Tail);
if (RingHead == RingTail)
{