github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/pkg/sentry/kernel/futex/futex_test.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package futex
    16  
    17  import (
    18  	"math"
    19  	"runtime"
    20  	"sync/atomic"
    21  	"testing"
    22  	"unsafe"
    23  
    24  	"github.com/SagerNet/gvisor/pkg/context"
    25  	"github.com/SagerNet/gvisor/pkg/errors/linuxerr"
    26  	"github.com/SagerNet/gvisor/pkg/hostarch"
    27  	"github.com/SagerNet/gvisor/pkg/sync"
    28  )
    29  
    30  // testData implements the Target interface, and allows us to
    31  // treat the address passed for futex operations as an index in
    32  // a byte slice for testing simplicity.
    33  type testData struct {
    34  	context.Context
    35  	data []byte
    36  }
    37  
    38  const sizeofInt32 = 4
    39  
    40  func newTestData(size uint) testData {
    41  	return testData{
    42  		data: make([]byte, size),
    43  	}
    44  }
    45  
    46  func (t testData) SwapUint32(addr hostarch.Addr, new uint32) (uint32, error) {
    47  	val := atomic.SwapUint32((*uint32)(unsafe.Pointer(&t.data[addr])), new)
    48  	return val, nil
    49  }
    50  
    51  func (t testData) CompareAndSwapUint32(addr hostarch.Addr, old, new uint32) (uint32, error) {
    52  	if atomic.CompareAndSwapUint32((*uint32)(unsafe.Pointer(&t.data[addr])), old, new) {
    53  		return old, nil
    54  	}
    55  	return atomic.LoadUint32((*uint32)(unsafe.Pointer(&t.data[addr]))), nil
    56  }
    57  
    58  func (t testData) LoadUint32(addr hostarch.Addr) (uint32, error) {
    59  	return atomic.LoadUint32((*uint32)(unsafe.Pointer(&t.data[addr]))), nil
    60  }
    61  
    62  func (t testData) GetSharedKey(addr hostarch.Addr) (Key, error) {
    63  	return Key{
    64  		Kind:   KindSharedMappable,
    65  		Offset: uint64(addr),
    66  	}, nil
    67  }
    68  
    69  func futexKind(private bool) string {
    70  	if private {
    71  		return "private"
    72  	}
    73  	return "shared"
    74  }
    75  
    76  func newPreparedTestWaiter(t *testing.T, m *Manager, ta Target, addr hostarch.Addr, private bool, val uint32, bitmask uint32) *Waiter {
    77  	w := NewWaiter()
    78  	if err := m.WaitPrepare(w, ta, addr, private, val, bitmask); err != nil {
    79  		t.Fatalf("WaitPrepare failed: %v", err)
    80  	}
    81  	return w
    82  }
    83  
    84  func TestFutexWake(t *testing.T) {
    85  	for _, private := range []bool{false, true} {
    86  		t.Run(futexKind(private), func(t *testing.T) {
    87  			m := NewManager()
    88  			d := newTestData(sizeofInt32)
    89  
    90  			// Start waiting for wakeup.
    91  			w := newPreparedTestWaiter(t, m, d, 0, private, 0, ^uint32(0))
    92  			defer m.WaitComplete(w, d)
    93  
    94  			// Perform a wakeup.
    95  			if n, err := m.Wake(d, 0, private, ^uint32(0), 1); err != nil || n != 1 {
    96  				t.Errorf("Wake: got (%d, %v), wanted (1, nil)", n, err)
    97  			}
    98  
    99  			// Expect the waiter to have been woken.
   100  			if !w.woken() {
   101  				t.Error("waiter not woken")
   102  			}
   103  		})
   104  	}
   105  }
   106  
   107  func TestFutexWakeBitmask(t *testing.T) {
   108  	for _, private := range []bool{false, true} {
   109  		t.Run(futexKind(private), func(t *testing.T) {
   110  			m := NewManager()
   111  			d := newTestData(sizeofInt32)
   112  
   113  			// Start waiting for wakeup.
   114  			w := newPreparedTestWaiter(t, m, d, 0, private, 0, 0x0000ffff)
   115  			defer m.WaitComplete(w, d)
   116  
   117  			// Perform a wakeup using the wrong bitmask.
   118  			if n, err := m.Wake(d, 0, private, 0xffff0000, 1); err != nil || n != 0 {
   119  				t.Errorf("Wake with non-matching bitmask: got (%d, %v), wanted (0, nil)", n, err)
   120  			}
   121  
   122  			// Expect the waiter to still be waiting.
   123  			if w.woken() {
   124  				t.Error("waiter woken unexpectedly")
   125  			}
   126  
   127  			// Perform a wakeup using the right bitmask.
   128  			if n, err := m.Wake(d, 0, private, 0x00000001, 1); err != nil || n != 1 {
   129  				t.Errorf("Wake with matching bitmask: got (%d, %v), wanted (1, nil)", n, err)
   130  			}
   131  
   132  			// Expect that the waiter was woken.
   133  			if !w.woken() {
   134  				t.Error("waiter not woken")
   135  			}
   136  		})
   137  	}
   138  }
   139  
   140  func TestFutexWakeTwo(t *testing.T) {
   141  	for _, private := range []bool{false, true} {
   142  		t.Run(futexKind(private), func(t *testing.T) {
   143  			m := NewManager()
   144  			d := newTestData(sizeofInt32)
   145  
   146  			// Start three waiters waiting for wakeup.
   147  			var ws [3]*Waiter
   148  			for i := range ws {
   149  				ws[i] = newPreparedTestWaiter(t, m, d, 0, private, 0, ^uint32(0))
   150  				defer m.WaitComplete(ws[i], d)
   151  			}
   152  
   153  			// Perform two wakeups.
   154  			const wakeups = 2
   155  			if n, err := m.Wake(d, 0, private, ^uint32(0), 2); err != nil || n != wakeups {
   156  				t.Errorf("Wake: got (%d, %v), wanted (%d, nil)", n, err, wakeups)
   157  			}
   158  
   159  			// Expect that exactly two waiters were woken.
   160  			// We don't get guarantees about exactly which two,
   161  			// (although we expect them to be w1 and w2).
   162  			awake := 0
   163  			for i := range ws {
   164  				if ws[i].woken() {
   165  					awake++
   166  				}
   167  			}
   168  			if awake != wakeups {
   169  				t.Errorf("got %d woken waiters, wanted %d", awake, wakeups)
   170  			}
   171  		})
   172  	}
   173  }
   174  
   175  func TestFutexWakeUnrelated(t *testing.T) {
   176  	for _, private := range []bool{false, true} {
   177  		t.Run(futexKind(private), func(t *testing.T) {
   178  			m := NewManager()
   179  			d := newTestData(2 * sizeofInt32)
   180  
   181  			// Start two waiters waiting for wakeup on different addresses.
   182  			w1 := newPreparedTestWaiter(t, m, d, 0*sizeofInt32, private, 0, ^uint32(0))
   183  			defer m.WaitComplete(w1, d)
   184  			w2 := newPreparedTestWaiter(t, m, d, 1*sizeofInt32, private, 0, ^uint32(0))
   185  			defer m.WaitComplete(w2, d)
   186  
   187  			// Perform two wakeups on the second address.
   188  			if n, err := m.Wake(d, 1*sizeofInt32, private, ^uint32(0), 2); err != nil || n != 1 {
   189  				t.Errorf("Wake: got (%d, %v), wanted (1, nil)", n, err)
   190  			}
   191  
   192  			// Expect that only the second waiter was woken.
   193  			if w1.woken() {
   194  				t.Error("w1 woken unexpectedly")
   195  			}
   196  			if !w2.woken() {
   197  				t.Error("w2 not woken")
   198  			}
   199  		})
   200  	}
   201  }
   202  
   203  func TestWakeOpEmpty(t *testing.T) {
   204  	for _, private := range []bool{false, true} {
   205  		t.Run(futexKind(private), func(t *testing.T) {
   206  			m := NewManager()
   207  			d := newTestData(2 * sizeofInt32)
   208  
   209  			// Perform wakeups with no waiters.
   210  			if n, err := m.WakeOp(d, 0, sizeofInt32, private, 10, 10, 0); err != nil || n != 0 {
   211  				t.Fatalf("WakeOp: got (%d, %v), wanted (0, nil)", n, err)
   212  			}
   213  		})
   214  	}
   215  }
   216  
   217  func TestWakeOpFirstNonEmpty(t *testing.T) {
   218  	for _, private := range []bool{false, true} {
   219  		t.Run(futexKind(private), func(t *testing.T) {
   220  			m := NewManager()
   221  			d := newTestData(8)
   222  
   223  			// Add two waiters on address 0.
   224  			w1 := newPreparedTestWaiter(t, m, d, 0, private, 0, ^uint32(0))
   225  			defer m.WaitComplete(w1, d)
   226  			w2 := newPreparedTestWaiter(t, m, d, 0, private, 0, ^uint32(0))
   227  			defer m.WaitComplete(w2, d)
   228  
   229  			// Perform 10 wakeups on address 0.
   230  			if n, err := m.WakeOp(d, 0, sizeofInt32, private, 10, 0, 0); err != nil || n != 2 {
   231  				t.Errorf("WakeOp: got (%d, %v), wanted (2, nil)", n, err)
   232  			}
   233  
   234  			// Expect that both waiters were woken.
   235  			if !w1.woken() {
   236  				t.Error("w1 not woken")
   237  			}
   238  			if !w2.woken() {
   239  				t.Error("w2 not woken")
   240  			}
   241  		})
   242  	}
   243  }
   244  
   245  func TestWakeOpSecondNonEmpty(t *testing.T) {
   246  	for _, private := range []bool{false, true} {
   247  		t.Run(futexKind(private), func(t *testing.T) {
   248  			m := NewManager()
   249  			d := newTestData(8)
   250  
   251  			// Add two waiters on address sizeofInt32.
   252  			w1 := newPreparedTestWaiter(t, m, d, sizeofInt32, private, 0, ^uint32(0))
   253  			defer m.WaitComplete(w1, d)
   254  			w2 := newPreparedTestWaiter(t, m, d, sizeofInt32, private, 0, ^uint32(0))
   255  			defer m.WaitComplete(w2, d)
   256  
   257  			// Perform 10 wakeups on address sizeofInt32 (contingent on
   258  			// d.Op(0), which should succeed).
   259  			if n, err := m.WakeOp(d, 0, sizeofInt32, private, 0, 10, 0); err != nil || n != 2 {
   260  				t.Errorf("WakeOp: got (%d, %v), wanted (2, nil)", n, err)
   261  			}
   262  
   263  			// Expect that both waiters were woken.
   264  			if !w1.woken() {
   265  				t.Error("w1 not woken")
   266  			}
   267  			if !w2.woken() {
   268  				t.Error("w2 not woken")
   269  			}
   270  		})
   271  	}
   272  }
   273  
   274  func TestWakeOpSecondNonEmptyFailingOp(t *testing.T) {
   275  	for _, private := range []bool{false, true} {
   276  		t.Run(futexKind(private), func(t *testing.T) {
   277  			m := NewManager()
   278  			d := newTestData(8)
   279  
   280  			// Add two waiters on address sizeofInt32.
   281  			w1 := newPreparedTestWaiter(t, m, d, sizeofInt32, private, 0, ^uint32(0))
   282  			defer m.WaitComplete(w1, d)
   283  			w2 := newPreparedTestWaiter(t, m, d, sizeofInt32, private, 0, ^uint32(0))
   284  			defer m.WaitComplete(w2, d)
   285  
   286  			// Perform 10 wakeups on address sizeofInt32 (contingent on
   287  			// d.Op(1), which should fail).
   288  			if n, err := m.WakeOp(d, 0, sizeofInt32, private, 0, 10, 1); err != nil || n != 0 {
   289  				t.Errorf("WakeOp: got (%d, %v), wanted (0, nil)", n, err)
   290  			}
   291  
   292  			// Expect that neither waiter was woken.
   293  			if w1.woken() {
   294  				t.Error("w1 woken unexpectedly")
   295  			}
   296  			if w2.woken() {
   297  				t.Error("w2 woken unexpectedly")
   298  			}
   299  		})
   300  	}
   301  }
   302  
   303  func TestWakeOpAllNonEmpty(t *testing.T) {
   304  	for _, private := range []bool{false, true} {
   305  		t.Run(futexKind(private), func(t *testing.T) {
   306  			m := NewManager()
   307  			d := newTestData(8)
   308  
   309  			// Add two waiters on address 0.
   310  			w1 := newPreparedTestWaiter(t, m, d, 0, private, 0, ^uint32(0))
   311  			defer m.WaitComplete(w1, d)
   312  			w2 := newPreparedTestWaiter(t, m, d, 0, private, 0, ^uint32(0))
   313  			defer m.WaitComplete(w2, d)
   314  
   315  			// Add two waiters on address sizeofInt32.
   316  			w3 := newPreparedTestWaiter(t, m, d, sizeofInt32, private, 0, ^uint32(0))
   317  			defer m.WaitComplete(w3, d)
   318  			w4 := newPreparedTestWaiter(t, m, d, sizeofInt32, private, 0, ^uint32(0))
   319  			defer m.WaitComplete(w4, d)
   320  
   321  			// Perform 10 wakeups on address 0 (unconditionally), and 10
   322  			// wakeups on address sizeofInt32 (contingent on d.Op(0), which
   323  			// should succeed).
   324  			if n, err := m.WakeOp(d, 0, sizeofInt32, private, 10, 10, 0); err != nil || n != 4 {
   325  				t.Errorf("WakeOp: got (%d, %v), wanted (4, nil)", n, err)
   326  			}
   327  
   328  			// Expect that all waiters were woken.
   329  			if !w1.woken() {
   330  				t.Error("w1 not woken")
   331  			}
   332  			if !w2.woken() {
   333  				t.Error("w2 not woken")
   334  			}
   335  			if !w3.woken() {
   336  				t.Error("w3 not woken")
   337  			}
   338  			if !w4.woken() {
   339  				t.Error("w4 not woken")
   340  			}
   341  		})
   342  	}
   343  }
   344  
   345  func TestWakeOpAllNonEmptyFailingOp(t *testing.T) {
   346  	for _, private := range []bool{false, true} {
   347  		t.Run(futexKind(private), func(t *testing.T) {
   348  			m := NewManager()
   349  			d := newTestData(8)
   350  
   351  			// Add two waiters on address 0.
   352  			w1 := newPreparedTestWaiter(t, m, d, 0, private, 0, ^uint32(0))
   353  			defer m.WaitComplete(w1, d)
   354  			w2 := newPreparedTestWaiter(t, m, d, 0, private, 0, ^uint32(0))
   355  			defer m.WaitComplete(w2, d)
   356  
   357  			// Add two waiters on address sizeofInt32.
   358  			w3 := newPreparedTestWaiter(t, m, d, sizeofInt32, private, 0, ^uint32(0))
   359  			defer m.WaitComplete(w3, d)
   360  			w4 := newPreparedTestWaiter(t, m, d, sizeofInt32, private, 0, ^uint32(0))
   361  			defer m.WaitComplete(w4, d)
   362  
   363  			// Perform 10 wakeups on address 0 (unconditionally), and 10
   364  			// wakeups on address sizeofInt32 (contingent on d.Op(1), which
   365  			// should fail).
   366  			if n, err := m.WakeOp(d, 0, sizeofInt32, private, 10, 10, 1); err != nil || n != 2 {
   367  				t.Errorf("WakeOp: got (%d, %v), wanted (2, nil)", n, err)
   368  			}
   369  
   370  			// Expect that only the first two waiters were woken.
   371  			if !w1.woken() {
   372  				t.Error("w1 not woken")
   373  			}
   374  			if !w2.woken() {
   375  				t.Error("w2 not woken")
   376  			}
   377  			if w3.woken() {
   378  				t.Error("w3 woken unexpectedly")
   379  			}
   380  			if w4.woken() {
   381  				t.Error("w4 woken unexpectedly")
   382  			}
   383  		})
   384  	}
   385  }
   386  
   387  func TestWakeOpSameAddress(t *testing.T) {
   388  	for _, private := range []bool{false, true} {
   389  		t.Run(futexKind(private), func(t *testing.T) {
   390  			m := NewManager()
   391  			d := newTestData(8)
   392  
   393  			// Add four waiters on address 0.
   394  			var ws [4]*Waiter
   395  			for i := range ws {
   396  				ws[i] = newPreparedTestWaiter(t, m, d, 0, private, 0, ^uint32(0))
   397  				defer m.WaitComplete(ws[i], d)
   398  			}
   399  
   400  			// Perform 1 wakeup on address 0 (unconditionally), and 1 wakeup
   401  			// on address 0 (contingent on d.Op(0), which should succeed).
   402  			const wakeups = 2
   403  			if n, err := m.WakeOp(d, 0, 0, private, 1, 1, 0); err != nil || n != wakeups {
   404  				t.Errorf("WakeOp: got (%d, %v), wanted (%d, nil)", n, err, wakeups)
   405  			}
   406  
   407  			// Expect that exactly two waiters were woken.
   408  			awake := 0
   409  			for i := range ws {
   410  				if ws[i].woken() {
   411  					awake++
   412  				}
   413  			}
   414  			if awake != wakeups {
   415  				t.Errorf("got %d woken waiters, wanted %d", awake, wakeups)
   416  			}
   417  		})
   418  	}
   419  }
   420  
   421  func TestWakeOpSameAddressFailingOp(t *testing.T) {
   422  	for _, private := range []bool{false, true} {
   423  		t.Run(futexKind(private), func(t *testing.T) {
   424  			m := NewManager()
   425  			d := newTestData(8)
   426  
   427  			// Add four waiters on address 0.
   428  			var ws [4]*Waiter
   429  			for i := range ws {
   430  				ws[i] = newPreparedTestWaiter(t, m, d, 0, private, 0, ^uint32(0))
   431  				defer m.WaitComplete(ws[i], d)
   432  			}
   433  
   434  			// Perform 1 wakeup on address 0 (unconditionally), and 1 wakeup
   435  			// on address 0 (contingent on d.Op(1), which should fail).
   436  			const wakeups = 1
   437  			if n, err := m.WakeOp(d, 0, 0, private, 1, 1, 1); err != nil || n != wakeups {
   438  				t.Errorf("WakeOp: got (%d, %v), wanted (%d, nil)", n, err, wakeups)
   439  			}
   440  
   441  			// Expect that exactly one waiter was woken.
   442  			awake := 0
   443  			for i := range ws {
   444  				if ws[i].woken() {
   445  					awake++
   446  				}
   447  			}
   448  			if awake != wakeups {
   449  				t.Errorf("got %d woken waiters, wanted %d", awake, wakeups)
   450  			}
   451  		})
   452  	}
   453  }
   454  
   455  const (
   456  	testMutexSize            = sizeofInt32
   457  	testMutexLocked   uint32 = 1
   458  	testMutexUnlocked uint32 = 0
   459  )
   460  
   461  // testMutex ties together a testData slice, an address, and a
   462  // futex manager in order to implement the sync.Locker interface.
   463  // Beyond being used as a Locker, this is a simple mechanism for
   464  // changing the underlying values for simpler tests.
   465  type testMutex struct {
   466  	a hostarch.Addr
   467  	d testData
   468  	m *Manager
   469  }
   470  
   471  func newTestMutex(addr hostarch.Addr, d testData, m *Manager) *testMutex {
   472  	return &testMutex{a: addr, d: d, m: m}
   473  }
   474  
   475  // Lock acquires the testMutex.
   476  // This may wait for it to be available via the futex manager.
   477  func (t *testMutex) Lock() {
   478  	for {
   479  		// Attempt to grab the lock.
   480  		if atomic.CompareAndSwapUint32(
   481  			(*uint32)(unsafe.Pointer(&t.d.data[t.a])),
   482  			testMutexUnlocked,
   483  			testMutexLocked) {
   484  			// Lock held.
   485  			return
   486  		}
   487  
   488  		// Wait for it to be "not locked".
   489  		w := NewWaiter()
   490  		err := t.m.WaitPrepare(w, t.d, t.a, true, testMutexLocked, ^uint32(0))
   491  		if linuxerr.Equals(linuxerr.EAGAIN, err) {
   492  			continue
   493  		}
   494  		if err != nil {
   495  			// Should never happen.
   496  			panic("WaitPrepare returned unexpected error: " + err.Error())
   497  		}
   498  		<-w.C
   499  		t.m.WaitComplete(w, t.d)
   500  	}
   501  }
   502  
   503  // Unlock releases the testMutex.
   504  // This will notify any waiters via the futex manager.
   505  func (t *testMutex) Unlock() {
   506  	// Unlock.
   507  	atomic.StoreUint32((*uint32)(unsafe.Pointer(&t.d.data[t.a])), testMutexUnlocked)
   508  
   509  	// Notify all waiters.
   510  	t.m.Wake(t.d, t.a, true, ^uint32(0), math.MaxInt32)
   511  }
   512  
   513  // This function was shamelessly stolen from mutex_test.go.
   514  func HammerMutex(l sync.Locker, loops int, cdone chan bool) {
   515  	for i := 0; i < loops; i++ {
   516  		l.Lock()
   517  		runtime.Gosched()
   518  		l.Unlock()
   519  	}
   520  	cdone <- true
   521  }
   522  
   523  func TestMutexStress(t *testing.T) {
   524  	m := NewManager()
   525  	d := newTestData(testMutexSize)
   526  	tm := newTestMutex(0*testMutexSize, d, m)
   527  	c := make(chan bool)
   528  
   529  	for i := 0; i < 10; i++ {
   530  		go HammerMutex(tm, 1000, c)
   531  	}
   532  
   533  	for i := 0; i < 10; i++ {
   534  		<-c
   535  	}
   536  }