On Wed, May 09, 2018 at 10:37:14AM +0200, Martin Pieuchot wrote:
> On 08/05/18(Tue) 14:57, Scott Cheloha wrote:
> > 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.
> 
> Could you use timespecfix() for that?  Look how pselect(2) or ppoll(2)
> do it.  Btw pirofti@ had a similar diff.

Yes, my diff attempted to fix kern/ in totality but guenther@ objected
with:

  > Could you use the timespecfix() function in the kernel?  I believe the
  > same should be done for futex(2).

  No.  timespecfix() is only appropriate for *relative* timespec values and
  not *absolute* timespec value and __thrsleep(2) takes an absolute value.
  All the tests of tv_sec in timespecfix() are inappropriate for absolute
  timespec values.

He did ok the futex_wait bits but wanted further testing:

  Per my other email, the __thrsleep(9) and dovutimens(9) changes are
  incorrect.  The sys___thrsigdivert(9) and futex_wait(9) changes seem sane,
  but have to be tested.

I was keeping that change for after the new semaphore implementation goes
in so I can more easily separate fallout sources if any.

So I will commit this change afterwards if you guys don't mind or don't
see this crashing actual applications in the wild.


> > 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)

This is better:


Index: sys/kern/sys_futex.c
===================================================================
RCS file: /cvs/src/sys/kern/sys_futex.c,v
retrieving revision 1.6
diff -u -p -u -p -r1.6 sys_futex.c
--- sys/kern/sys_futex.c        8 Jan 2018 22:33:13 -0000       1.6
+++ sys/kern/sys_futex.c        4 Apr 2018 11:53:59 -0000
@@ -210,6 +210,8 @@ futex_wait(uint32_t *uaddr, uint32_t val

                if ((error = copyin(timeout, &ts, sizeof(ts))))
                        return error;
+               if ((error = timespecfix(&ts)))
+                       return error;
 #ifdef KTRACE
                if (KTRPOINT(p, KTR_STRUCT))
                        ktrabstimespec(p, &ts);


> > 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);

I like the regress test :)

Reply via email to