Re: [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.
Oded Katz <[email protected]> Fri, 27 Feb 2026 17:12:32 -0800
| Newsgroups | com.zx2c4.lists.wireguard |
|---|---|
| Message-ID | <CAApnxP3P2evKEjAa-JxCpCLGzCV9M1RaYGce1+WGbCwGC=OyRg@mail.gmail.com> |
Thanks for the update. Have a wonderful weekend Oded On Fri, Feb 27, 2026 at 2:03=E2=80=AFPM Simon Rozman <[email protected]= i> wrote: > > Yes, Oded. Your research is much appreciated. I have stare-reviewed it > immediately, and it makes sense: by slightly extending the critical > section a race condition should be fixed. Unfortunately, haven't had > time to apply it to the master branch yet. Hopefully next week. > Definitely before a release. > > Lep pozdrav | Best regards, > Simon Rozman > Amebis, d. o. o., Kamnik > > On 27.2.2026 22:28, Oded Katz wrote: > > Hi Simon, > > > > We have found another issue with the driver code. > > some race condition between "alertable" flag between driver and > > user-side. causing driver to stall until new packet wakes it up. > > We have seen some cases where it can be for periods of 5 sec. > > > > we already made the fix, and I sent 2 approaches for that fix. > > please let us know if you are planning to solve it in your repo? > > > > regards and thanks, > > Oded > > > > On Fri, Feb 20, 2026 at 8:55=E2=80=AFAM Oded Katz <[email protected]>= wrote: > >> > >> Hi Simon, > >> > >> Thanks for the update. > >> FYI: the ReadULongAcquire() and WriteULongRelease() themself are good > >> enough for getting and updating an atomic operation. > >> however, after on thread updates the head, the second thread can't > >> stay with the same atomic read value of the Head. > >> - so assuming 2 thread read the Head on the same time so the 2 threads > >> has same head. > >> - now 1st thread takes the spin lock and updates the head > >> - 2nd thread is pending the job until the spinlock is released and > >> then it doing the job, but with the non-updated Head value > >> - so it will overrun the previous thread data and mess the ringb= uffer > >> > >> > >> again thanks for providing the next release > >> Regards, > >> Oded > >> > >> On Thu, Feb 19, 2026 at 11:28=E2=80=AFPM Simon Rozman <simon.rozman@am= ebis.si> wrote: > >>> > >>> 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() alon= e > >>> provide atomic manipulation with Ring->Head on all modern platforms. > >>> Hence, these calls were made outside the spinlock, to squeeze an extr= a > >>> micrometer of performance. > >>> > >>> I could not directly apply your patch to the wintun repo, since it do= es > >>> 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 appl= ied > >>> it here: > >>> https://git.zx2c4.com/wintun/commit/?id=3D607c181ea9fa0036d23598038e6= 019ad54db5ce4 > >>> > >>> 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 mani= pulate 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 =3D Ctx->Device.Send.Ring; > >>>> ULONG RingCapacity =3D Ctx->Device.Send.Capacity; > >>>> > >>>> + KLOCK_QUEUE_HANDLE LockHandle; > >>>> + KeAcquireInStackQueuedSpinLock(&Ctx->Device.Send.Lock, &LockHan= dle); > >>>> /* Allocate space for packets in the ring. */ > >>>> ULONG RingHead =3D ReadULongAcquire(&Ring->Head); > >>>> - if (Status =3D NDIS_STATUS_ADAPTER_NOT_READY, RingHead >=3D Rin= gCapacity) > >>>> + if (Status =3D NDIS_STATUS_ADAPTER_NOT_READY, RingHead >=3D Rin= gCapacity) { > >>>> + KeReleaseInStackQueuedSpinLock(&LockHandle); > >>>> goto skipNbl; > >>>> - > >>>> - KLOCK_QUEUE_HANDLE LockHandle; > >>>> - KeAcquireInStackQueuedSpinLock(&Ctx->Device.Send.Lock, &LockHan= dle); > >>>> + } > >>>> > >>>> ULONG RingTail =3D Ctx->Device.Send.RingTail; > >>>> ASSERT(RingTail < RingCapacity); > >>>> @@ -419,8 +420,8 @@ TunReturnNetBufferLists(NDIS_HANDLE MiniportAdap= terContext, PNET_BUFFER_LIST Net > >>>> Ctx->Device.Receive.ActiveNbls.Head =3D NET_BUFFER_LI= ST_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(Comple= tedNbl)); > >>>> + KeReleaseInStackQueuedSpinLock(&LockHandle); > >>>> const MDL *TargetMdl =3D Ctx->Device.Receive.Mdl; > >>>> for (MDL *Mdl =3D NET_BUFFER_FIRST_MDL(NET_BUFFER_LIS= T_FIRST_NB(CompletedNbl)); Mdl; Mdl =3D Mdl->Next) > >>>> { > >>> >