Fix handling of overflow and non-canonical values in timespec APIs.
Support zero relative and absolute times in the timer API.
This commit is contained in:
parent
109a229b42
commit
4daedc31f7
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2011, 2012, 2013 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2011, 2012, 2013, 2021 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -47,9 +47,15 @@ int sys_alarmns(const struct timespec* user_delay, struct timespec* user_odelay)
|
||||||
struct itimerspec delay, odelay;
|
struct itimerspec delay, odelay;
|
||||||
if ( !CopyFromUser(&delay.it_value, user_delay, sizeof(*user_delay)) )
|
if ( !CopyFromUser(&delay.it_value, user_delay, sizeof(*user_delay)) )
|
||||||
return -1;
|
return -1;
|
||||||
|
if ( !timespec_is_canonical(delay.it_value) )
|
||||||
|
return errno = EINVAL, -1;
|
||||||
delay.it_interval = timespec_nul();
|
delay.it_interval = timespec_nul();
|
||||||
|
|
||||||
process->alarm_timer.Set(&delay, &odelay, 0, alarm_handler, (void*) process);
|
int flags = 0;
|
||||||
|
if ( !delay.it_value.tv_sec && !delay.it_value.tv_nsec )
|
||||||
|
flags |= TIMER_DISARM;
|
||||||
|
|
||||||
|
process->alarm_timer.Set(&delay, &odelay, flags, alarm_handler, process);
|
||||||
|
|
||||||
if ( !CopyToUser(user_odelay, &odelay.it_value, sizeof(*user_odelay)) )
|
if ( !CopyToUser(user_odelay, &odelay.it_value, sizeof(*user_odelay)) )
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2013, 2014, 2015, 2016, 2017 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2012-2017, 2021 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -579,7 +579,7 @@ ssize_t Descriptor::pwritev(ioctx_t* ctx, const struct iovec* iov_ptr,
|
||||||
|
|
||||||
static inline bool valid_utimens_timespec(struct timespec ts)
|
static inline bool valid_utimens_timespec(struct timespec ts)
|
||||||
{
|
{
|
||||||
return ts.tv_nsec < 1000000000 ||
|
return (0 <= ts.tv_nsec && ts.tv_nsec < 1000000000) ||
|
||||||
ts.tv_nsec == UTIME_NOW ||
|
ts.tv_nsec == UTIME_NOW ||
|
||||||
ts.tv_nsec == UTIME_OMIT;
|
ts.tv_nsec == UTIME_OMIT;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, 2014, 2015, 2016 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2013, 2014, 2015, 2016, 2021 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -52,9 +52,10 @@ static inline void ahci_port_flush(volatile struct port_regs* port_regs)
|
||||||
(void) port_regs->pxcmd;
|
(void) port_regs->pxcmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void delay(int usecs)
|
static inline void delay(unsigned int usecs)
|
||||||
{
|
{
|
||||||
struct timespec delay = timespec_make(usecs / 1000000, usecs * 1000);
|
struct timespec delay =
|
||||||
|
timespec_make(usecs / 1000000, (usecs % 1000000) * 1000);
|
||||||
Clock* clock = Time::GetClock(CLOCK_BOOT);
|
Clock* clock = Time::GetClock(CLOCK_BOOT);
|
||||||
clock->SleepDelay(delay);
|
clock->SleepDelay(delay);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, 2016, 2017 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2013, 2016, 2017, 2021 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -57,6 +57,7 @@ static const int TIMER_FUNC_ADVANCE_THREAD = 1 << 4;
|
||||||
// otherwise delay the destruction until the timer handler, which also grabs the
|
// otherwise delay the destruction until the timer handler, which also grabs the
|
||||||
// mutex and checks whether object destruction is supposed to happen.
|
// mutex and checks whether object destruction is supposed to happen.
|
||||||
static const int TIMER_FUNC_MAY_DEALLOCATE_TIMER = 1 << 5;
|
static const int TIMER_FUNC_MAY_DEALLOCATE_TIMER = 1 << 5;
|
||||||
|
static const int TIMER_DISARM = 1 << 6;
|
||||||
|
|
||||||
class Timer
|
class Timer
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2012, 2014, 2015, 2018 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2012, 2014, 2015, 2018, 2021 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -177,6 +177,8 @@ static bool FetchTimespec(struct timespec* dest, const struct timespec* user)
|
||||||
dest->tv_nsec = 0;
|
dest->tv_nsec = 0;
|
||||||
else if ( !CopyFromUser(dest, user, sizeof(*dest)) )
|
else if ( !CopyFromUser(dest, user, sizeof(*dest)) )
|
||||||
return false;
|
return false;
|
||||||
|
else if ( !timespec_is_canonical(*dest) )
|
||||||
|
return errno = EINVAL, -1;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, 2016, 2017 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2013, 2016, 2017, 2021 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -129,14 +129,21 @@ void Timer::Set(struct itimerspec* new_value, struct itimerspec* old_value,
|
||||||
GetInternal(old_value);
|
GetInternal(old_value);
|
||||||
|
|
||||||
// Arm the timer if a value was specified.
|
// Arm the timer if a value was specified.
|
||||||
if ( timespec_lt(timespec_nul(), new_value->it_value) )
|
if ( !(new_flags & TIMER_DISARM) )
|
||||||
{
|
{
|
||||||
value = *new_value;
|
value = *new_value;
|
||||||
flags = new_flags;
|
flags = new_flags;
|
||||||
callback = new_callback;
|
callback = new_callback;
|
||||||
user = new_user;
|
user = new_user;
|
||||||
|
if ( !(flags & TIMER_ABSOLUTE) && value.it_value.tv_sec < 0 )
|
||||||
|
value.it_value = timespec_nul();
|
||||||
clock->Register(this);
|
clock->Register(this);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
value.it_value = timespec_nul();
|
||||||
|
value.it_interval = timespec_nul();
|
||||||
|
}
|
||||||
|
|
||||||
clock->UnlockClock();
|
clock->UnlockClock();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2013, 2014, 2021 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -194,6 +194,10 @@ int sys_timer_settime(timer_t timerid,
|
||||||
if ( !CopyFromUser(&value, user_value, sizeof(value)) )
|
if ( !CopyFromUser(&value, user_value, sizeof(value)) )
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if ( !timespec_is_canonical(value.it_value) ||
|
||||||
|
!timespec_is_canonical(value.it_interval) )
|
||||||
|
return errno = EINVAL, -1;
|
||||||
|
|
||||||
if ( timespec_lt(value.it_value, timespec_nul()) ||
|
if ( timespec_lt(value.it_value, timespec_nul()) ||
|
||||||
timespec_lt(value.it_interval, timespec_nul()) ||
|
timespec_lt(value.it_interval, timespec_nul()) ||
|
||||||
(flags & ~(TIMER_ABSTIME)) != 0 )
|
(flags & ~(TIMER_ABSTIME)) != 0 )
|
||||||
|
@ -202,6 +206,8 @@ int sys_timer_settime(timer_t timerid,
|
||||||
int timer_flags = 0;
|
int timer_flags = 0;
|
||||||
if ( flags & TIMER_ABSTIME )
|
if ( flags & TIMER_ABSTIME )
|
||||||
timer_flags |= TIMER_ABSOLUTE;
|
timer_flags |= TIMER_ABSOLUTE;
|
||||||
|
if ( !value.it_value.tv_sec && !value.it_value.tv_nsec )
|
||||||
|
timer_flags |= TIMER_DISARM;
|
||||||
|
|
||||||
timer->Set(&value, &ovalue, timer_flags, timer_callback, user_timer);
|
timer->Set(&value, &ovalue, timer_flags, timer_callback, user_timer);
|
||||||
|
|
||||||
|
@ -240,6 +246,11 @@ int sys_clock_settimeres(clockid_t clockid,
|
||||||
(res && !CopyFromUser(&kres, res, sizeof(kres))) )
|
(res && !CopyFromUser(&kres, res, sizeof(kres))) )
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if ( time && !timespec_is_canonical(ktime) )
|
||||||
|
return errno = EINVAL, -1;
|
||||||
|
if ( res && !timespec_is_canonical(kres) )
|
||||||
|
return errno = EINVAL, -1;
|
||||||
|
|
||||||
clock->Set(time ? &ktime : NULL, res ? &kres : NULL);
|
clock->Set(time ? &ktime : NULL, res ? &kres : NULL);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -259,6 +270,9 @@ int sys_clock_nanosleep(clockid_t clockid,
|
||||||
if ( !CopyFromUser(&time, user_duration, sizeof(time)) )
|
if ( !CopyFromUser(&time, user_duration, sizeof(time)) )
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
if ( !timespec_is_canonical(time) )
|
||||||
|
return errno = EINVAL, -1;
|
||||||
|
|
||||||
time = flags & TIMER_ABSTIME ? clock->SleepUntil(time) :
|
time = flags & TIMER_ABSTIME ? clock->SleepUntil(time) :
|
||||||
clock->SleepDelay(time);
|
clock->SleepDelay(time);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2013, 2021 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -28,25 +28,22 @@
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef __time_t_defined
|
#ifndef __time_t_defined
|
||||||
#define __time_t_defined
|
#define __time_t_defined
|
||||||
typedef __time_t time_t;
|
typedef __time_t time_t;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
} /* extern "C" */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <sortix/timespec.h>
|
#include <sortix/timespec.h>
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static __inline bool timespec_is_canonical(struct timespec t)
|
||||||
|
{
|
||||||
|
return 0 <= t.tv_nsec && t.tv_nsec <= 999999999;
|
||||||
|
}
|
||||||
|
|
||||||
static __inline bool timespec_eq(struct timespec a, struct timespec b)
|
static __inline bool timespec_eq(struct timespec a, struct timespec b)
|
||||||
{
|
{
|
||||||
return a.tv_sec == b.tv_sec && a.tv_nsec == b.tv_nsec;
|
return a.tv_sec == b.tv_sec && a.tv_nsec == b.tv_nsec;
|
||||||
|
@ -91,6 +88,9 @@ static __inline struct timespec timespec_make(time_t sec, long nsec)
|
||||||
|
|
||||||
static __inline struct timespec timespec_neg(struct timespec t)
|
static __inline struct timespec timespec_neg(struct timespec t)
|
||||||
{
|
{
|
||||||
|
if ( t.tv_sec == __TIME_MIN )
|
||||||
|
return timespec_make(__TIME_MAX,
|
||||||
|
!t.tv_nsec ? 999999999 : 1000000000 - t.tv_nsec);
|
||||||
if ( t.tv_nsec )
|
if ( t.tv_nsec )
|
||||||
return timespec_make(-t.tv_sec - 1, 1000000000 - t.tv_nsec);
|
return timespec_make(-t.tv_sec - 1, 1000000000 - t.tv_nsec);
|
||||||
return timespec_make(-t.tv_sec, 0);
|
return timespec_make(-t.tv_sec, 0);
|
||||||
|
@ -101,9 +101,11 @@ static __inline struct timespec timespec_nul(void)
|
||||||
return timespec_make(0, 0);
|
return timespec_make(0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timespec timespec_canonalize(struct timespec t);
|
struct timespec timespec_canonalize(struct timespec);
|
||||||
struct timespec timespec_add(struct timespec a, struct timespec b);
|
struct timespec timespec_add(struct timespec, struct timespec);
|
||||||
struct timespec timespec_sub(struct timespec a, struct timespec b);
|
struct timespec timespec_sub(struct timespec, struct timespec);
|
||||||
|
bool timespec_add_overflow(struct timespec, struct timespec, struct timespec*);
|
||||||
|
bool timespec_sub_overflow(struct timespec, struct timespec, struct timespec*);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
} /* extern "C" */
|
} /* extern "C" */
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2013, 2021 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -17,11 +17,14 @@
|
||||||
* Utility functions for manipulation of struct timespec.
|
* Utility functions for manipulation of struct timespec.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <timespec.h>
|
#include <timespec.h>
|
||||||
|
|
||||||
static const long NANOSECONDS_PER_SECOND = 1000000000L;
|
static const long NANOSECONDS_PER_SECOND = 1000000000L;
|
||||||
|
|
||||||
// TODO: The C modulo operator doesn't do exactly what we desire.
|
// The C modulo operator doesn't do exactly what we desire.
|
||||||
static long proper_modulo(long a, long b)
|
static long proper_modulo(long a, long b)
|
||||||
{
|
{
|
||||||
if ( 0 <= a )
|
if ( 0 <= a )
|
||||||
|
@ -37,19 +40,72 @@ struct timespec timespec_canonalize(struct timespec t)
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool timespec_add_overflow(struct timespec a,
|
||||||
|
struct timespec b,
|
||||||
|
struct timespec* res)
|
||||||
|
{
|
||||||
|
assert(timespec_is_canonical(a) && timespec_is_canonical(b));
|
||||||
|
struct timespec ret;
|
||||||
|
ret.tv_nsec = a.tv_nsec + b.tv_nsec;
|
||||||
|
if ( NANOSECONDS_PER_SECOND <= ret.tv_nsec )
|
||||||
|
{
|
||||||
|
ret.tv_nsec -= NANOSECONDS_PER_SECOND;
|
||||||
|
if ( a.tv_sec != TIME_MAX )
|
||||||
|
a.tv_sec++;
|
||||||
|
else if ( b.tv_sec != TIME_MAX )
|
||||||
|
b.tv_sec++;
|
||||||
|
else
|
||||||
|
goto overflow;
|
||||||
|
}
|
||||||
|
if ( __builtin_add_overflow(a.tv_sec, b.tv_sec, &ret.tv_sec) )
|
||||||
|
goto overflow;
|
||||||
|
*res = ret;
|
||||||
|
return false;
|
||||||
|
overflow:
|
||||||
|
*res = b.tv_sec < 0 ?
|
||||||
|
timespec_make(TIME_MIN, 0) :
|
||||||
|
timespec_make(TIME_MAX, NANOSECONDS_PER_SECOND - 1);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
struct timespec timespec_add(struct timespec a, struct timespec b)
|
struct timespec timespec_add(struct timespec a, struct timespec b)
|
||||||
{
|
{
|
||||||
struct timespec ret;
|
struct timespec ret;
|
||||||
a = timespec_canonalize(a);
|
timespec_add_overflow(a, b, &ret);
|
||||||
b = timespec_canonalize(b);
|
return ret;
|
||||||
ret.tv_sec = a.tv_sec + b.tv_sec;
|
}
|
||||||
ret.tv_nsec = a.tv_nsec + b.tv_nsec;
|
|
||||||
return timespec_canonalize(ret);
|
bool timespec_sub_overflow(struct timespec a,
|
||||||
|
struct timespec b,
|
||||||
|
struct timespec* res)
|
||||||
|
{
|
||||||
|
assert(timespec_is_canonical(a) && timespec_is_canonical(b));
|
||||||
|
struct timespec ret;
|
||||||
|
ret.tv_nsec = a.tv_nsec - b.tv_nsec;
|
||||||
|
if ( ret.tv_nsec < 0 )
|
||||||
|
{
|
||||||
|
ret.tv_nsec += NANOSECONDS_PER_SECOND;
|
||||||
|
if ( a.tv_sec != TIME_MIN )
|
||||||
|
a.tv_sec--;
|
||||||
|
else if ( b.tv_sec != TIME_MAX )
|
||||||
|
b.tv_sec++;
|
||||||
|
else
|
||||||
|
goto overflow;
|
||||||
|
}
|
||||||
|
if ( __builtin_sub_overflow(a.tv_sec, b.tv_sec, &ret.tv_sec) )
|
||||||
|
goto overflow;
|
||||||
|
*res = ret;
|
||||||
|
return false;
|
||||||
|
overflow:
|
||||||
|
*res = b.tv_sec >= 0 ?
|
||||||
|
timespec_make(TIME_MIN, 0) :
|
||||||
|
timespec_make(TIME_MAX, NANOSECONDS_PER_SECOND - 1);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct timespec timespec_sub(struct timespec a, struct timespec b)
|
struct timespec timespec_sub(struct timespec a, struct timespec b)
|
||||||
{
|
{
|
||||||
a = timespec_canonalize(a);
|
struct timespec ret;
|
||||||
b = timespec_canonalize(b);
|
timespec_sub_overflow(a, b, &ret);
|
||||||
return timespec_add(a, timespec_neg(b));
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2013 Jonas 'Sortie' Termansen.
|
* Copyright (c) 2013, 2021 Jonas 'Sortie' Termansen.
|
||||||
*
|
*
|
||||||
* Permission to use, copy, modify, and distribute this software for any
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
* purpose with or without fee is hereby granted, provided that the above
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
@ -23,6 +23,6 @@
|
||||||
|
|
||||||
int usleep(useconds_t usecs)
|
int usleep(useconds_t usecs)
|
||||||
{
|
{
|
||||||
struct timespec delay = timespec_canonalize(timespec_make(0, usecs * 1000));
|
struct timespec delay = timespec_make(usecs / 1000, (usecs % 1000) * 1000);
|
||||||
return nanosleep(&delay, NULL);
|
return nanosleep(&delay, NULL);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue