libnet/osl: stop assuming caller thread is clean

(*networkNamespace).InvokeFunc() cleaned up the state of the locked
thread by setting its network namespace to the netns of the goroutine
which called InvokeFunc(). If InvokeFunc() was to be called after the
caller had modified its thread's network namespace, InvokeFunc() would
incorrectly "restore" the state of its goroutine thread to the wrong
namespace, violating the invariant that unlocked threads are fungible.
Change the implementation to restore the thread's netns to the netns
that particular thread had before InvokeFunc() modified it.

Signed-off-by: Cory Snider <csnider@mirantis.com>
This commit is contained in:
Cory Snider 2022-11-04 20:41:03 -04:00
parent 4f3c5b2568
commit 8404507b9b

View file

@ -420,12 +420,6 @@ func (n *networkNamespace) DisableARPForVIP(srcName string) (Err error) {
}
func (n *networkNamespace) InvokeFunc(f func()) error {
origNS, err := netns.Get()
if err != nil {
return fmt.Errorf("failed to get original network namespace: %w", err)
}
defer origNS.Close()
path := n.nsPath()
newNS, err := netns.GetFromPath(path)
if err != nil {
@ -436,6 +430,18 @@ func (n *networkNamespace) InvokeFunc(f func()) error {
done := make(chan error, 1)
go func() {
runtime.LockOSThread()
// InvokeFunc() could have been called from a goroutine with
// tampered thread state, e.g. from another InvokeFunc()
// callback. The outer goroutine's thread state cannot be
// trusted.
origNS, err := netns.Get()
if err != nil {
runtime.UnlockOSThread()
done <- fmt.Errorf("failed to get original network namespace: %w", err)
return
}
defer origNS.Close()
if err := netns.Set(newNS); err != nil {
runtime.UnlockOSThread()
done <- err