gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/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 "testing" 21 "unsafe" 22 23 "gvisor.dev/gvisor/pkg/atomicbitops" 24 "gvisor.dev/gvisor/pkg/context" 25 "gvisor.dev/gvisor/pkg/errors/linuxerr" 26 "gvisor.dev/gvisor/pkg/hostarch" 27 "gvisor.dev/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 := (*atomicbitops.Uint32)(unsafe.Pointer(&t.data[addr])).Swap(new) 48 return val, nil 49 } 50 51 func (t testData) CompareAndSwapUint32(addr hostarch.Addr, old, new uint32) (uint32, error) { 52 if (*atomicbitops.Uint32)(unsafe.Pointer(&t.data[addr])).CompareAndSwap(old, new) { 53 return old, nil 54 } 55 return (*atomicbitops.Uint32)(unsafe.Pointer(&t.data[addr])).Load(), nil 56 } 57 58 func (t testData) LoadUint32(addr hostarch.Addr) (uint32, error) { 59 return (*atomicbitops.Uint32)(unsafe.Pointer(&t.data[addr])).Load(), 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 (*atomicbitops.Uint32)(unsafe.Pointer(&t.d.data[t.a])).CompareAndSwap( 481 testMutexUnlocked, 482 testMutexLocked) { 483 // Lock held. 484 return 485 } 486 487 // Wait for it to be "not locked". 488 w := NewWaiter() 489 err := t.m.WaitPrepare(w, t.d, t.a, true, testMutexLocked, ^uint32(0)) 490 if linuxerr.Equals(linuxerr.EAGAIN, err) { 491 continue 492 } 493 if err != nil { 494 // Should never happen. 495 panic("WaitPrepare returned unexpected error: " + err.Error()) 496 } 497 <-w.C 498 t.m.WaitComplete(w, t.d) 499 } 500 } 501 502 // Unlock releases the testMutex. 503 // This will notify any waiters via the futex manager. 504 func (t *testMutex) Unlock() { 505 // Unlock. 506 (*atomicbitops.Uint32)(unsafe.Pointer(&t.d.data[t.a])).Store(testMutexUnlocked) 507 508 // Notify all waiters. 509 t.m.Wake(t.d, t.a, true, ^uint32(0), math.MaxInt32) 510 } 511 512 // This function was shamelessly stolen from mutex_test.go. 513 func HammerMutex(l sync.Locker, loops int, cdone chan bool) { 514 for i := 0; i < loops; i++ { 515 l.Lock() 516 runtime.Gosched() 517 l.Unlock() 518 } 519 cdone <- true 520 } 521 522 func TestMutexStress(t *testing.T) { 523 m := NewManager() 524 d := newTestData(testMutexSize) 525 tm := newTestMutex(0*testMutexSize, d, m) 526 c := make(chan bool) 527 528 for i := 0; i < 10; i++ { 529 go HammerMutex(tm, 1000, c) 530 } 531 532 for i := 0; i < 10; i++ { 533 <-c 534 } 535 }