[PATCH 1/1] in order to prevent buffer overrun (which was observed while sending multiple high throughput UDP streams from different threads) I move the driver spinlock to protect Ring buffer Head.
Simon Rozman
simon.rozman at amebis.si
Fri Feb 20 07:28:22 UTC 2026
Hi, Oded!
First and foremost, thank you very much for taking time to look into
this and troubleshoot it.
It was believed, that ReadULongAcquire() and WriteULongRelease() alone
provide atomic manipulation with Ring->Head on all modern platforms.
Hence, these calls were made outside the spinlock, to squeeze an extra
micrometer of performance.
I could not directly apply your patch to the wintun repo, since it does
not follow our code style, commit message conventions and is not
Signed-off-by you.
However, it would have been a terrible waste of your research if your
contribution wouldn't get into wintun, so I reworked your PR and applied
it here:
https://git.zx2c4.com/wintun/commit/?id=607c181ea9fa0036d23598038e6019ad54db5ce4
Please, stay tuned for an official WHQL-signed release.
Lep pozdrav | Best regards,
Simon Rozman
Amebis, d. o. o., Kamnik
On 19. 2. 2026 20.32, odedkatz wrote:
> I observed that the Ring->Head was taken and manipulated later on with just a `ReadULongAcquire` which isn't OK when 2 are trying to manipulate it later on based on the same received value.
> ---
> driver/wintun.c | 11 ++++++-----
> 1 file changed, 6 insertions(+), 5 deletions(-)
>
> diff --git a/driver/wintun.c b/driver/wintun.c
> index d1f3b9f..65cd97e 100644
> --- a/driver/wintun.c
> +++ b/driver/wintun.c
> @@ -284,13 +284,14 @@ TunSendNetBufferLists(
> TUN_RING *Ring = Ctx->Device.Send.Ring;
> ULONG RingCapacity = Ctx->Device.Send.Capacity;
>
> + KLOCK_QUEUE_HANDLE LockHandle;
> + KeAcquireInStackQueuedSpinLock(&Ctx->Device.Send.Lock, &LockHandle);
> /* Allocate space for packets in the ring. */
> ULONG RingHead = ReadULongAcquire(&Ring->Head);
> - if (Status = NDIS_STATUS_ADAPTER_NOT_READY, RingHead >= RingCapacity)
> + if (Status = NDIS_STATUS_ADAPTER_NOT_READY, RingHead >= RingCapacity) {
> + KeReleaseInStackQueuedSpinLock(&LockHandle);
> goto skipNbl;
> -
> - KLOCK_QUEUE_HANDLE LockHandle;
> - KeAcquireInStackQueuedSpinLock(&Ctx->Device.Send.Lock, &LockHandle);
> + }
>
> ULONG RingTail = Ctx->Device.Send.RingTail;
> ASSERT(RingTail < RingCapacity);
> @@ -419,8 +420,8 @@ TunReturnNetBufferLists(NDIS_HANDLE MiniportAdapterContext, PNET_BUFFER_LIST Net
> Ctx->Device.Receive.ActiveNbls.Head = NET_BUFFER_LIST_NEXT_NBL_EX(CompletedNbl);
> if (!Ctx->Device.Receive.ActiveNbls.Head)
> KeSetEvent(&Ctx->Device.Receive.ActiveNbls.Empty, IO_NO_INCREMENT, FALSE);
> - KeReleaseInStackQueuedSpinLock(&LockHandle);
> WriteULongRelease(&Ring->Head, TunNblGetOffset(CompletedNbl));
> + KeReleaseInStackQueuedSpinLock(&LockHandle);
> const MDL *TargetMdl = Ctx->Device.Receive.Mdl;
> for (MDL *Mdl = NET_BUFFER_FIRST_MDL(NET_BUFFER_LIST_FIRST_NB(CompletedNbl)); Mdl; Mdl = Mdl->Next)
> {
More information about the WireGuard
mailing list