From ed1b678a0fe3acd9a950416238f5c2bbe35988ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= Date: Mon, 6 Jul 2026 09:56:56 +0200 Subject: [PATCH] integration/networking: Restore netns on the locked thread MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Host.Do captured the original network namespace before locking its OS thread. netns.Get is thread-local: it opens /proc//task//ns/net. If the goroutine moves to another thread before LockOSThread, Host.Do can save one thread's namespace and later restore that namespace onto a different locked thread. Lock the OS thread before reading the original namespace so the saved handle matches the thread that enters the test namespace. If restore fails, keep the goroutine locked to the thread so the runtime can retire the contaminated thread instead of returning it to the scheduler. Signed-off-by: Paweł Gronowski --- .../testutils/networking/l3_segment_linux.go | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/integration/internal/testutils/networking/l3_segment_linux.go b/integration/internal/testutils/networking/l3_segment_linux.go index eb923eff2a..0a9b1abfe5 100644 --- a/integration/internal/testutils/networking/l3_segment_linux.go +++ b/integration/internal/testutils/networking/l3_segment_linux.go @@ -149,11 +149,16 @@ func (h Host) Do(t *testing.T, fn func()) { t.Helper() if h.ns != CurrentNetns { - targetNs, err := netns.GetFromName(h.ns) - if err != nil { - t.Fatalf("failed to get netns handle: %v", err) - } - defer targetNs.Close() + runtime.LockOSThread() + // If restore fails, keep the goroutine locked to the thread so the + // runtime can retire the contaminated thread instead of returning it + // to the scheduler. + unlockThread := true + defer func() { + if unlockThread { + runtime.UnlockOSThread() + } + }() origNs, err := netns.Get() if err != nil { @@ -161,13 +166,21 @@ func (h Host) Do(t *testing.T, fn func()) { } defer origNs.Close() - runtime.LockOSThread() - defer runtime.UnlockOSThread() + targetNs, err := netns.GetFromName(h.ns) + if err != nil { + t.Fatalf("failed to get netns handle: %v", err) + } + defer targetNs.Close() if err := netns.Set(targetNs); err != nil { t.Fatalf("failed to enter netns: %v", err) } - defer netns.Set(origNs) + defer func() { + if err := netns.Set(origNs); err != nil { + unlockThread = false + t.Errorf("failed to restore netns: %v", err) + } + }() } fn()