From 992dc33fc5aabda31d55cb2758e56e6b8fd280b7 Mon Sep 17 00:00:00 2001 From: Cory Snider Date: Fri, 19 May 2023 18:16:00 -0400 Subject: [PATCH] libnetwork/osl: restore the right thread's netns osl.setIPv6 mistakenly captured the calling goroutine's thread's network namespace instead of the network namespace of the thread getting its namespace temporarily changed. As this function appears to only be called from contexts in the process's initial network namespace, this mistake would be of little consequence at runtime. The libnetwork unit tests, on the other hand, unshare network namespaces so as not to interfere with each other or the host's network namespace. But due to this bug, the isolation backfires and the network namespace of goroutines used by a test which are expected to be in the initial network namespace can randomly become the isolated network namespace of some other test. Symptoms include a loopback network server running in one goroutine being inexplicably and randomly being unreachable by a client in another goroutine. Capture the original network namespace of the thread from the thread to be tampered with, after locking the goroutine to the thread. Signed-off-by: Cory Snider (cherry picked from commit 6d798641351a18390f80570bf1ef554c54cfb71b) Signed-off-by: Cory Snider --- libnetwork/osl/namespace_linux.go | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/libnetwork/osl/namespace_linux.go b/libnetwork/osl/namespace_linux.go index 6da6f95b01..9f22d80772 100644 --- a/libnetwork/osl/namespace_linux.go +++ b/libnetwork/osl/namespace_linux.go @@ -600,24 +600,29 @@ func (n *networkNamespace) checkLoV6() { } func setIPv6(nspath, iface string, enable bool) error { - origNS, err := netns.Get() - if err != nil { - return fmt.Errorf("failed to get current network namespace: %w", err) - } - defer origNS.Close() - - namespace, err := netns.GetFromPath(nspath) - if err != nil { - return fmt.Errorf("failed get network namespace %q: %w", nspath, err) - } - defer namespace.Close() - errCh := make(chan error, 1) go func() { defer close(errCh) + namespace, err := netns.GetFromPath(nspath) + if err != nil { + errCh <- fmt.Errorf("failed get network namespace %q: %w", nspath, err) + return + } + defer namespace.Close() + runtime.LockOSThread() + + origNS, err := netns.Get() + if err != nil { + runtime.UnlockOSThread() + errCh <- fmt.Errorf("failed to get current network namespace: %w", err) + return + } + defer origNS.Close() + if err = netns.Set(namespace); err != nil { + runtime.UnlockOSThread() errCh <- fmt.Errorf("setting into container netns %q failed: %w", nspath, err) return }