futex: wake one waiter if futex_wake is called with a non-positive value

This change is needed to be compatible with the Linux kernel.

There is no glibc wrapper for the futex system call, so it is easy to
make a mistake and call syscall(__NR_futex, FUTEX_WAKE, addr) without
the fourth argument. This works on Linux, because it wakes one waiter
even if val is nonpositive.

PiperOrigin-RevId: 286494396
This commit is contained in:
Andrei Vagin
2019-12-19 17:26:44 -08:00
committed by gVisor bot
parent 7419e0e5d7
commit 29955a4797
2 changed files with 31 additions and 0 deletions
+10
View File
@@ -226,6 +226,11 @@ func Futex(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
if mask == 0 {
return 0, nil, syserror.EINVAL
}
if val <= 0 {
// The Linux kernel wakes one waiter even if val is
// non-positive.
val = 1
}
n, err := t.Futex().Wake(t, addr, private, mask, val)
return uintptr(n), nil, err
@@ -242,6 +247,11 @@ func Futex(t *kernel.Task, args arch.SyscallArguments) (uintptr, *kernel.Syscall
case linux.FUTEX_WAKE_OP:
op := uint32(val3)
if val <= 0 {
// The Linux kernel wakes one waiter even if val is
// non-positive.
val = 1
}
n, err := t.Futex().WakeOp(t, addr, naddr, private, val, nreq, op)
return uintptr(n), nil, err
+21
View File
@@ -239,6 +239,27 @@ TEST_P(PrivateAndSharedFutexTest, Wake1_NoRandomSave) {
EXPECT_THAT(futex_wake(IsPrivate(), &a, 1), SyscallSucceedsWithValue(1));
}
TEST_P(PrivateAndSharedFutexTest, Wake0_NoRandomSave) {
constexpr int kInitialValue = 1;
std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue);
// Prevent save/restore from interrupting futex_wait, which will cause it to
// return EAGAIN instead of the expected result if futex_wait is restarted
// after we change the value of a below.
DisableSave ds;
ScopedThread thread([&] {
EXPECT_THAT(futex_wait(IsPrivate(), &a, kInitialValue),
SyscallSucceedsWithValue(0));
});
absl::SleepFor(kWaiterStartupDelay);
// Change a so that if futex_wake happens before futex_wait, the latter
// returns EAGAIN instead of hanging the test.
a.fetch_add(1);
// The Linux kernel wakes one waiter even if val is 0 or negative.
EXPECT_THAT(futex_wake(IsPrivate(), &a, 0), SyscallSucceedsWithValue(1));
}
TEST_P(PrivateAndSharedFutexTest, WakeAll_NoRandomSave) {
constexpr int kInitialValue = 1;
std::atomic<int> a = ATOMIC_VAR_INIT(kInitialValue);