Hi,

futex(2) doesn't do any range checking for timeout for FUTEX_WAIT,
though recent Linux does so.  I assume we'd also want to validate
timeout before waiting.

The attached returns EINVAL if timeout's fields are non-normal,
documents it, and adds a test case.

ok?

--
Scott Cheloha

Index: lib/libc/sys/futex.2
===================================================================
RCS file: /cvs/src/lib/libc/sys/futex.2,v
retrieving revision 1.4
diff -u -p -r1.4 futex.2
--- lib/libc/sys/futex.2        24 Apr 2018 17:19:35 -0000      1.4
+++ lib/libc/sys/futex.2        8 May 2018 19:49:27 -0000
@@ -116,6 +116,10 @@ The value pointed to by
 .Fa uaddr
 is not the same as the expected value
 .Fa val .
+.It Bq Er EINVAL
+.Fa timeout
+specified a second value less than zero,
+or a nanosecond value less than zero or greater than or equal to 1000 million.
 .It Bq Er ETIMEDOUT
 The
 .Fa timeout
Index: sys/kern/sys_futex.c
===================================================================
RCS file: /cvs/src/sys/kern/sys_futex.c,v
retrieving revision 1.7
diff -u -p -r1.7 sys_futex.c
--- sys/kern/sys_futex.c        24 Apr 2018 17:19:35 -0000      1.7
+++ sys/kern/sys_futex.c        8 May 2018 19:49:27 -0000
@@ -214,6 +214,8 @@ futex_wait(uint32_t *uaddr, uint32_t val
                if (KTRPOINT(p, KTR_STRUCT))
                        ktrabstimespec(p, &ts);
 #endif
+               if (ts.tv_sec < 0 || ts.tv_nsec < 0 || ts.tv_nsec >= 1000000000)
+                       return EINVAL;
                to_ticks = (uint64_t)hz * ts.tv_sec +
                    (ts.tv_nsec + tick * 1000 - 1) / (tick * 1000) + 1;
                if (to_ticks > INT_MAX)
Index: regress/sys/kern/futex/futex.c
===================================================================
RCS file: /cvs/src/regress/sys/kern/futex/futex.c,v
retrieving revision 1.2
diff -u -p -r1.2 futex.c
--- regress/sys/kern/futex/futex.c      30 Apr 2017 10:11:03 -0000      1.2
+++ regress/sys/kern/futex/futex.c      8 May 2018 19:49:27 -0000
@@ -37,7 +37,7 @@ int
 main(int argc, char *argv[])
 {
        struct sigaction sa;
-       struct timespec abs = { 0, 5000 };
+       struct timespec abs = { 0, 5000 }, bogus;
        pthread_t thread;
 
        /* Invalid operation */
@@ -48,6 +48,17 @@ main(int argc, char *argv[])
 
        /* If (lock != 1) return EAGAIN */
        assert(futex_twait(&lock, 1, 0, NULL) == EAGAIN);
+
+       /* Invalid timeout */
+       bogus.tv_sec = -1;
+       bogus.tv_nsec = 0;
+       assert(futex(&lock, FUTEX_WAIT, lock, &bogus, NULL) == EINVAL);
+       bogus.tv_sec = 0;
+       bogus.tv_nsec = -1;
+       assert(futex(&lock, FUTEX_WAIT, lock, &bogus, NULL) == EINVAL);
+       bogus.tv_sec = 0;
+       bogus.tv_nsec = 1000000001;
+       assert(futex(&lock, FUTEX_WAIT, lock, &bogus, NULL) == EINVAL);
 
        /* Deadlock for 5000ns */
        assert(futex_twait(&lock, 0, CLOCK_REALTIME, &abs) == ETIMEDOUT);

Reply via email to