[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