From: Arran Cudbard-Bell Date: Thu, 28 Oct 2021 16:32:07 +0000 (-0400) Subject: Add generic time scaling functions X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=4d55383a2be17cd18a0687b9d7534c9b17579913;p=thirdparty%2Ffreeradius-server.git Add generic time scaling functions Deal with integer overflow in time manipulation and scaling functions by returning min/max. --- diff --git a/src/lib/util/time.c b/src/lib/util/time.c index bec951f7c4a..aeb5377d1f5 100644 --- a/src/lib/util/time.c +++ b/src/lib/util/time.c @@ -656,31 +656,6 @@ fr_unix_time_t fr_unix_time_from_tm(struct tm *tm) return fr_unix_time_from_sec((days - 2472692) * 86400 + (tm->tm_hour * 3600) + (tm->tm_min * 60) + tm->tm_sec + tm->tm_gmtoff); } -int64_t fr_time_delta_scale(fr_time_delta_t delta, fr_time_res_t hint) -{ - switch (hint) { - case FR_TIME_RES_SEC: - return fr_time_delta_to_sec(delta); - - case FR_TIME_RES_CSEC: - return fr_time_delta_to_csec(delta); - - case FR_TIME_RES_MSEC: - return fr_time_delta_to_msec(delta); - - case FR_TIME_RES_USEC: - return fr_time_delta_to_usec(delta); - - case FR_TIME_RES_NSEC: - return fr_time_delta_unwrap(delta); - - default: - break; - } - - return 0; -} - /** Scale an input time to NSEC, clamping it at max / min. * * @param t input time / time delta diff --git a/src/lib/util/time.h b/src/lib/util/time.h index 5308e61de6f..f187ca84bd6 100644 --- a/src/lib/util/time.h +++ b/src/lib/util/time.h @@ -26,15 +26,9 @@ */ RCSIDH(time_h, "$Id$") -/* - * For sys/time.h and time.h - */ -#include -#include -#include +#include #include #include -#include #include #include @@ -42,6 +36,20 @@ RCSIDH(time_h, "$Id$") extern "C" { #endif +/** The base resolution for print parse operations + */ +typedef enum { + FR_TIME_RES_INVALID = -1, + FR_TIME_RES_SEC = 0, + FR_TIME_RES_MIN, + FR_TIME_RES_HOUR, + FR_TIME_RES_DAY, + FR_TIME_RES_CSEC, + FR_TIME_RES_MSEC, + FR_TIME_RES_USEC, + FR_TIME_RES_NSEC +} fr_time_res_t; + /** "server local" time. This is the time in nanoseconds since the application started. * * This time is our *private* view of time. It should only be used @@ -54,11 +62,6 @@ typedef struct fr_time_s { ///< entries. } fr_time_t; -#define fr_time_max() (fr_time_t){ .value = INT64_MAX } -#define fr_time_min() (fr_time_t){ .value = INT64_MIN } -#define fr_time_wrap(_time) (fr_time_t){ .value = (_time) } -static inline int64_t fr_time_unwrap(fr_time_t time) { return time.value; } /* func to stop mixing with fr_time_delta_t */ - /** A time delta, a difference in time measured in nanoseconds. * * This is easier to distinguish where server epoch time is being @@ -68,11 +71,6 @@ typedef struct fr_time_delta_s { int64_t value; } fr_time_delta_t; -#define fr_time_delta_max() (fr_time_delta_t){ .value = INT64_MAX } -#define fr_time_delta_min() (fr_time_delta_t){ .value = INT64_MIN } -#define fr_time_delta_wrap(_time) (fr_time_delta_t){ .value = (_time) } -static inline int64_t fr_time_delta_unwrap(fr_time_delta_t time) { return time.value; } /* func to stop mixing with fr_time_t */ - /** "Unix" time. This is the time in nanoseconds since midnight January 1, 1970 * * Note that it is *unsigned*, as we don't use dates before 1970. Having it @@ -88,13 +86,70 @@ typedef struct fr_unix_time_s { uint64_t value; } fr_unix_time_t; +#ifdef __cplusplus +} +#endif + +/* + * For sys/time.h and time.h + */ +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + extern int64_t const fr_time_multiplier_by_res[]; extern fr_table_num_ordered_t const fr_time_precision_table[]; extern size_t fr_time_precision_table_len; + +static bool fr_time_op_ispos(bool a, bool op, bool b) +{ + return ((a == op) == b); +} + +/** Determine, if an overflow has occurred, which direction it occurred in + * + * @param[in] _a First operand. + * @param[in] _op Operator, true if add or multiply, false if subtract. + * @param[in] _b Second operand. + */ +#define fr_time_overflow_ispos(_a, _op, _b) \ +fr_time_op_ispos( \ + _Generic(&(_a), \ + fr_time_t *: (fr_time_unwrap(*((fr_time_t *)&(_a))) >= 0), \ + fr_time_delta_t *: (fr_time_delta_unwrap(*((fr_time_delta_t *)&(_a))) >= 0), \ + fr_unix_time_t *: true), \ + _op, \ + _Generic(&(_b), \ + fr_time_t *: (fr_time_unwrap(*((fr_time_t *)&(_b))) >= 0), \ + fr_time_delta_t *: (fr_time_delta_unwrap(*((fr_time_delta_t *)&(_b))) >= 0), \ + fr_unix_time_t *: true)\ + ) + +#define fr_time_max() (fr_time_t){ .value = INT64_MAX } +#define fr_time_min() (fr_time_t){ .value = INT64_MIN } +#define fr_time_wrap(_time) (fr_time_t){ .value = (_time) } +static inline int64_t fr_time_unwrap(fr_time_t time) { return time.value; } /* func to stop mixing with fr_time_delta_t */ +#define fr_time_overflow_add(_a, _b) (fr_time_overflow_ispos(_a, true, _b) ? fr_time_max() : fr_time_min()) +#define fr_time_overflow_sub(_a, _b) (fr_time_overflow_ispos(_a, false, _b) ? fr_time_max() : fr_time_min()) + +#define fr_time_delta_max() (fr_time_delta_t){ .value = INT64_MAX } +#define fr_time_delta_min() (fr_time_delta_t){ .value = INT64_MIN } +#define fr_time_delta_wrap(_time) (fr_time_delta_t){ .value = (_time) } +static inline int64_t fr_time_delta_unwrap(fr_time_delta_t time) { return time.value; } /* func to stop mixing with fr_time_t */ +#define fr_time_delta_overflow_add(_a, _b) (fr_time_overflow_ispos(_a, true, _b) ? fr_time_delta_max() : fr_time_delta_min()) +#define fr_time_delta_overflow_sub(_a, _b) (fr_time_overflow_ispos(_a, false, _b) ? fr_time_delta_max() : fr_time_delta_min()) + #define fr_unix_time_max() (fr_unix_time_t){ .value = UINT64_MAX } #define fr_unix_time_min() (fr_unix_time_t){ .value = 0 } #define fr_unix_time_wrap(_time) (fr_unix_time_t){ .value = (_time) } static inline uint64_t fr_unix_time_unwrap(fr_unix_time_t time) { return time.value; } /* func to stop mixing with fr_time_t */ +#define fr_unix_time_overflow_add(_a, _b) (fr_time_overflow_ispos(_a, true, _b) ? fr_unix_time_max() : fr_unix_time_min()) +#define fr_unix_time_overflow_sub(_a, _b) (fr_time_overflow_ispos(_a, false, _b) ? fr_unix_time_max() : fr_unix_time_min()) /** @name fr_time_t arithmetic and comparison macros * @@ -104,8 +159,19 @@ static inline uint64_t fr_unix_time_unwrap(fr_unix_time_t time) { return time.va * @{ */ /* Don't add fr_time_add_time_time, it's almost always a type error */ -static inline fr_time_t fr_time_add_time_delta(fr_time_t a, fr_time_delta_t b) { return fr_time_wrap(fr_time_unwrap(a) + fr_time_delta_unwrap(b)); } -static inline fr_time_t fr_time_add_delta_time(fr_time_delta_t a, fr_time_t b) { return fr_time_wrap(fr_time_delta_unwrap(a) + fr_time_unwrap(b)); } +static inline fr_time_t fr_time_add_time_delta(fr_time_t a, fr_time_delta_t b) +{ + typeof_field(fr_time_t, value) out; + if (!fr_add(&out, fr_time_unwrap(a), fr_time_delta_unwrap(b))) return fr_time_overflow_add(a, b); + return fr_time_wrap(out); +} + +static inline fr_time_t fr_time_add_delta_time(fr_time_delta_t a, fr_time_t b) +{ + typeof_field(fr_time_t, value) out; + if (!fr_add(&out, fr_time_delta_unwrap(a), fr_time_unwrap(b))) return fr_time_overflow_add(a, b); + return fr_time_wrap(out); +} /** Add a time/time delta together * @@ -127,8 +193,18 @@ static inline fr_time_t fr_time_add_delta_time(fr_time_delta_t a, fr_time_t b) { ) \ )(_a, _b) -static inline fr_time_delta_t fr_time_sub_time_time(fr_time_t a, fr_time_t b) { return fr_time_delta_wrap(fr_time_unwrap(a) - fr_time_unwrap(b)); } -static inline fr_time_t fr_time_sub_time_delta(fr_time_t a, fr_time_delta_t b) { return fr_time_wrap(fr_time_unwrap(a) - fr_time_delta_unwrap(b)); } +static inline fr_time_delta_t fr_time_sub_time_time(fr_time_t a, fr_time_t b) +{ + typeof_field(fr_time_t, value) out; + if (!fr_sub(&out, fr_time_unwrap(a), fr_time_unwrap(b))) return fr_time_delta_overflow_sub(a, b); + return fr_time_delta_wrap(out); +} +static inline fr_time_t fr_time_sub_time_delta(fr_time_t a, fr_time_delta_t b) +{ + typeof_field(fr_time_t, value) out; + if (!fr_sub(&out, fr_time_unwrap(a), fr_time_delta_unwrap(b))) return fr_time_overflow_sub(a, b); + return fr_time_wrap(out); +} /** Subtract one time from another * @@ -165,10 +241,30 @@ static inline fr_time_t fr_time_sub_time_delta(fr_time_t a, fr_time_delta_t b) { * The macros below allow basic arithmetic and comparisons to be performed. * @{ */ -static inline fr_time_delta_t fr_time_delta_add(fr_time_delta_t a, fr_time_delta_t b) { return fr_time_delta_wrap(fr_time_delta_unwrap(a) + fr_time_delta_unwrap(b)); } -static inline fr_time_delta_t fr_time_delta_sub(fr_time_delta_t a, fr_time_delta_t b) { return fr_time_delta_wrap(fr_time_delta_unwrap(a) - fr_time_delta_unwrap(b)); } -static inline fr_time_delta_t fr_time_delta_div(fr_time_delta_t a, fr_time_delta_t b) { return fr_time_delta_wrap(fr_time_delta_unwrap(a) / fr_time_delta_unwrap(b)); } -static inline fr_time_delta_t fr_time_delta_mul(fr_time_delta_t a, fr_time_delta_t b) { return fr_time_delta_wrap(fr_time_delta_unwrap(a) * fr_time_delta_unwrap(b)); } +static inline fr_time_delta_t fr_time_delta_add(fr_time_delta_t a, fr_time_delta_t b) +{ + typeof_field(fr_time_delta_t, value) out; + if (!fr_add(&out, fr_time_delta_unwrap(a), fr_time_delta_unwrap(b))) return fr_time_delta_overflow_add(a, b); + return fr_time_delta_wrap(out); +} +static inline fr_time_delta_t fr_time_delta_sub(fr_time_delta_t a, fr_time_delta_t b) +{ + typeof_field(fr_time_delta_t, value) out; + if (!fr_sub(&out, fr_time_delta_unwrap(a), fr_time_delta_unwrap(b))) return fr_time_delta_overflow_sub(a, b); + return fr_time_delta_wrap(out); +} +static inline fr_time_delta_t fr_time_delta_div(fr_time_delta_t a, fr_time_delta_t b) +{ + return fr_time_delta_wrap(fr_time_delta_unwrap(a) / fr_time_delta_unwrap(b)); +} +static inline fr_time_delta_t fr_time_delta_mul(fr_time_delta_t a, fr_time_delta_t b) +{ + typeof_field(fr_time_delta_t, value) out; + if (!fr_multiply(&out, fr_time_delta_unwrap(a), fr_time_delta_unwrap(b))) { + return fr_time_delta_overflow_add(a, b); + } + return fr_time_delta_wrap(out); +} #define fr_time_delta_cond(_a, _op, _b) (fr_time_delta_unwrap(_a) _op fr_time_delta_unwrap(_b)) #define fr_time_delta_gt(_a, _b) (fr_time_delta_unwrap(_a) > fr_time_delta_unwrap(_b)) @@ -190,8 +286,18 @@ static inline fr_time_delta_t fr_time_delta_mul(fr_time_delta_t a, fr_time_delta * @{ */ /* Don't add fr_unix_time_add_time_time, it's almost always a type error */ -static inline fr_unix_time_t fr_unix_time_add_time_delta(fr_unix_time_t a, fr_time_delta_t b) { return fr_unix_time_wrap(fr_unix_time_unwrap(a) + fr_time_delta_unwrap(b)); } -static inline fr_unix_time_t fr_unix_time_add_delta_time(fr_time_delta_t a, fr_unix_time_t b) { return fr_unix_time_wrap(fr_time_delta_unwrap(a) + fr_unix_time_unwrap(b)); } +static inline fr_unix_time_t fr_unix_time_add_time_delta(fr_unix_time_t a, fr_time_delta_t b) +{ + typeof_field(fr_unix_time_t, value) out; + if (!fr_add(&out, fr_unix_time_unwrap(a), fr_time_delta_unwrap(b))) return fr_unix_time_overflow_add(a, b); + return fr_unix_time_wrap(out); +} +static inline fr_unix_time_t fr_unix_time_add_delta_time(fr_time_delta_t a, fr_unix_time_t b) +{ + typeof_field(fr_unix_time_t, value) out; + if (!fr_add(&out, fr_time_delta_unwrap(a), fr_unix_time_unwrap(b))) return fr_unix_time_overflow_add(a, b); + return fr_unix_time_wrap(out); +} /** Add a time/time delta together * @@ -213,8 +319,18 @@ static inline fr_unix_time_t fr_unix_time_add_delta_time(fr_time_delta_t a, fr_u ) \ )(_a, _b) -static inline fr_time_delta_t fr_unix_time_sub_time_time(fr_unix_time_t a, fr_unix_time_t b) { return fr_time_delta_wrap(fr_unix_time_unwrap(a) - fr_unix_time_unwrap(b)); } -static inline fr_unix_time_t fr_unix_time_sub_time_delta(fr_unix_time_t a, fr_time_delta_t b) { return fr_unix_time_wrap(fr_unix_time_unwrap(a) - fr_time_delta_unwrap(b)); } +static inline fr_time_delta_t fr_unix_time_sub_time_time(fr_unix_time_t a, fr_unix_time_t b) +{ + typeof_field(fr_time_delta_t, value) out; + if (!fr_sub(&out, fr_unix_time_unwrap(a), fr_unix_time_unwrap(b))) return fr_time_delta_overflow_sub(a, b); + return fr_time_delta_wrap(out); +} +static inline fr_unix_time_t fr_unix_time_sub_time_delta(fr_unix_time_t a, fr_time_delta_t b) +{ + typeof_field(fr_unix_time_t, value) out; + if (!fr_sub(&out, fr_unix_time_unwrap(a), fr_time_delta_unwrap(b))) return fr_unix_time_overflow_sub(a, b); + return fr_unix_time_wrap(out); +} /** Subtract one time from another * @@ -243,16 +359,6 @@ static inline fr_unix_time_t fr_unix_time_sub_time_delta(fr_unix_time_t a, fr_ti #define fr_unix_time_ispos(_a) (fr_unix_time_unwrap(_a) > 0) /** @} */ -/** The base resolution for print parse operations - */ -typedef enum { - FR_TIME_RES_SEC = 0, - FR_TIME_RES_CSEC, - FR_TIME_RES_MSEC, - FR_TIME_RES_USEC, - FR_TIME_RES_NSEC -} fr_time_res_t; - typedef struct { uint64_t array[8]; //!< 100ns to 100s } fr_time_elapsed_t; @@ -275,6 +381,18 @@ extern uint64_t our_mach_epoch; * * @{ */ +static inline fr_unix_time_t fr_unix_time_from_integer(bool *overflow, int64_t integer, fr_time_res_t res) +{ + int64_t out; + if (res == FR_TIME_RES_INVALID) return fr_unix_time_max(); + if (!fr_multiply(&out, integer, fr_time_multiplier_by_res[res])) { + if (overflow) *overflow = true; + return fr_unix_time_max(); + } + if (overflow) *overflow = false; + return fr_unix_time_wrap(out); +} + static inline fr_unix_time_t fr_unix_time_from_nsec(int64_t nsec) { return fr_unix_time_wrap(nsec); @@ -282,32 +400,65 @@ static inline fr_unix_time_t fr_unix_time_from_nsec(int64_t nsec) static inline fr_unix_time_t fr_unix_time_from_usec(int64_t usec) { - return fr_unix_time_wrap(usec * (NSEC / USEC)); + uint64_t out; + if (!fr_multiply(&out, usec, (NSEC / USEC))) return (usec > 0) ? fr_unix_time_max() : fr_unix_time_min(); + return fr_unix_time_wrap(out); } static inline fr_unix_time_t fr_unix_time_from_msec(int64_t msec) { - return fr_unix_time_wrap(msec * (NSEC / MSEC)); + uint64_t out; + if (!fr_multiply(&out, msec, (NSEC / MSEC))) return (msec > 0) ? fr_unix_time_max() : fr_unix_time_min(); + return fr_unix_time_wrap(out); } static inline fr_unix_time_t fr_unix_time_from_csec(int64_t csec) { - return fr_unix_time_wrap(csec * (NSEC / CSEC)); + uint64_t out; + if (!fr_multiply(&out, csec, (NSEC / CSEC))) return (csec > 0) ? fr_unix_time_max() : fr_unix_time_min(); + return fr_unix_time_wrap(out); } static inline fr_unix_time_t fr_unix_time_from_sec(int64_t sec) { - return fr_unix_time_wrap(sec * NSEC); + uint64_t out; + if (!fr_multiply(&out, sec, NSEC)) return (sec > 0) ? fr_unix_time_max() : fr_unix_time_min(); + return fr_unix_time_wrap(out); } static inline CC_HINT(nonnull) fr_unix_time_t fr_unix_time_from_timeval(struct timeval const *tv) { - return fr_unix_time_wrap((((typeof_field(fr_unix_time_t, value)) tv->tv_sec) * NSEC) + (((typeof_field(fr_unix_time_t, value)) tv->tv_usec) * (NSEC / USEC))); + typeof_field(fr_unix_time_t, value) integer, fraction, out; + + if (!fr_multiply(&integer, (typeof_field(fr_unix_time_t, value)) tv->tv_sec, NSEC)) { + overflow: + return fr_unix_time_max(); + } + + if (!fr_multiply(&fraction, + (typeof_field(fr_unix_time_t, value)) tv->tv_usec, (NSEC / USEC))) goto overflow; + + if (!fr_add(&out, integer, fraction)) goto overflow; + + return fr_unix_time_wrap(out); } static inline CC_HINT(nonnull) fr_unix_time_t fr_unix_time_from_timespec(struct timespec const *ts) { - return fr_unix_time_wrap((((typeof_field(fr_unix_time_t, value))ts->tv_sec) * NSEC) + ts->tv_nsec); + typeof_field(fr_unix_time_t, value) integer, out; + + if (!fr_multiply(&integer, (typeof_field(fr_unix_time_t, value)) ts->tv_sec, NSEC)) { + overflow: + return fr_unix_time_max(); + } + if (!fr_add(&out, integer, ts->tv_nsec)) goto overflow; + + return fr_unix_time_wrap(out); +} + +static inline int64_t fr_unix_time_to_integer(fr_unix_time_t delta, fr_time_res_t res) +{ + return fr_unix_time_unwrap(delta) / fr_time_multiplier_by_res[res]; } static inline int64_t fr_unix_time_to_usec(fr_unix_time_t delta) @@ -330,6 +481,21 @@ static inline int64_t fr_unix_time_to_sec(fr_unix_time_t delta) return (fr_unix_time_unwrap(delta) / NSEC); } +static inline int64_t fr_unix_time_to_min(fr_unix_time_t delta) +{ + return (fr_unix_time_unwrap(delta) / NSEC) / 60; +} + +static inline int64_t fr_unix_time_to_hour(fr_unix_time_t delta) +{ + return (fr_unix_time_unwrap(delta) / NSEC) / 3600; +} + +static inline int64_t fr_unix_time_to_day(fr_unix_time_t delta) +{ + return (fr_unix_time_unwrap(delta) / NSEC) / 386400; +} + /** Covert a time_t into out internal fr_unix_time_t * * Our internal unix time representation is unsigned and in nanoseconds which @@ -342,7 +508,7 @@ static inline int64_t fr_unix_time_to_sec(fr_unix_time_t delta) */ static inline CC_HINT(nonnull) fr_unix_time_t fr_unix_time_from_time(time_t time) { - if (time < 0) return fr_unix_time_wrap(0); + if (time < 0) return fr_unix_time_min(); return fr_unix_time_wrap(time * NSEC); } @@ -352,6 +518,21 @@ static inline CC_HINT(nonnull) fr_unix_time_t fr_unix_time_from_time(time_t time * * @{ */ +static inline fr_time_delta_t fr_time_delta_from_integer(bool *overflow, int64_t integer, fr_time_res_t res) +{ + int64_t out; + if (res == FR_TIME_RES_INVALID) { + if (overflow) *overflow = true; + return fr_time_delta_max(); + } + if (!fr_multiply(&out, integer, fr_time_multiplier_by_res[res])) { + if (overflow) *overflow = true; + return fr_time_delta_wrap(integer > 0 ? INT64_MAX: INT64_MIN); + } + if (overflow) *overflow = false; + return fr_time_delta_wrap(out); +} + static inline fr_time_delta_t fr_time_delta_from_nsec(int64_t nsec) { return fr_time_delta_wrap(nsec); @@ -359,32 +540,65 @@ static inline fr_time_delta_t fr_time_delta_from_nsec(int64_t nsec) static inline fr_time_delta_t fr_time_delta_from_usec(int64_t usec) { - return fr_time_delta_wrap(usec * (NSEC / USEC)); + int64_t out; + if (!fr_multiply(&out, usec, (NSEC / USEC))) return (usec > 0) ? fr_time_delta_max() : fr_time_delta_min(); + return fr_time_delta_wrap(out); } static inline fr_time_delta_t fr_time_delta_from_msec(int64_t msec) { - return fr_time_delta_wrap(msec * (NSEC / MSEC)); + int64_t out; + if (!fr_multiply(&out, msec, (NSEC / MSEC))) return (msec > 0) ? fr_time_delta_max() : fr_time_delta_min(); + return fr_time_delta_wrap(out); } static inline fr_time_delta_t fr_time_delta_from_csec(int64_t csec) { - return fr_time_delta_wrap(csec * (NSEC / CSEC)); + int64_t out; + if (!fr_multiply(&out, csec, (NSEC / CSEC))) return (csec > 0) ? fr_time_delta_max() : fr_time_delta_min(); + return fr_time_delta_wrap(out); } static inline fr_time_delta_t fr_time_delta_from_sec(int64_t sec) { - return fr_time_delta_wrap(sec * NSEC); + int64_t out; + if (!fr_multiply(&out, sec, NSEC)) return (sec > 0) ? fr_time_delta_max() : fr_time_delta_min(); + return fr_time_delta_wrap(out); } static inline CC_HINT(nonnull) fr_time_delta_t fr_time_delta_from_timeval(struct timeval const *tv) { - return fr_time_delta_wrap((((typeof_field(fr_time_delta_t, value))tv->tv_sec) * NSEC) + (((typeof_field(fr_time_delta_t, value)) tv->tv_usec) * (NSEC / USEC))); + typeof_field(fr_time_delta_t, value) integer, fraction, out; + + if (!fr_multiply(&integer, (typeof_field(fr_time_delta_t, value)) tv->tv_sec, NSEC)) { + overflow: + return fr_time_delta_max(); + } + + if (!fr_multiply(&fraction, + (typeof_field(fr_time_delta_t, value)) tv->tv_usec, (NSEC / USEC))) goto overflow; + + if (!fr_add(&out, integer, fraction)) goto overflow; + + return fr_time_delta_wrap(out); } static inline CC_HINT(nonnull) fr_time_delta_t fr_time_delta_from_timespec(struct timespec const *ts) { - return fr_time_delta_wrap((((typeof_field(fr_time_delta_t, value))ts->tv_sec) * NSEC) + ts->tv_nsec); + typeof_field(fr_time_delta_t, value) integer, out; + + if (!fr_multiply(&integer, (typeof_field(fr_time_delta_t, value)) ts->tv_sec, NSEC)) { + overflow: + return fr_time_delta_max(); + } + if (!fr_add(&out, integer, ts->tv_nsec)) goto overflow; + + return fr_time_delta_wrap(out); +} + +static inline int64_t fr_time_delta_to_integer(fr_time_delta_t delta, fr_time_res_t res) +{ + return fr_time_delta_unwrap(delta) / fr_time_multiplier_by_res[res]; } static inline int64_t fr_time_delta_to_usec(fr_time_delta_t delta) @@ -440,12 +654,33 @@ static inline int64_t fr_time_wallclock_at_last_sync(void) return atomic_load_explicit(&our_realtime, memory_order_consume); } +/** Convert an fr_time_t (internal time) to arbitrary unit as wallclock time + * + */ +static inline int64_t fr_time_to_integer(bool *overflow, fr_time_t when, fr_time_res_t res) +{ + int64_t out; + + if (!fr_add(&out, fr_time_unwrap(when) / fr_time_multiplier_by_res[res], + atomic_load_explicit(&our_realtime, memory_order_consume) / fr_time_multiplier_by_res[res])) { + if (overflow) *overflow = true; + return fr_time_unwrap(when) > 0 ? INT64_MAX : INT64_MIN; + } + if (overflow) *overflow = false; + return out; +} + /** Convert an fr_time_t (internal time) to our version of unix time (wallclock time) * */ static inline fr_unix_time_t fr_time_to_unix_time(fr_time_t when) { - return fr_unix_time_wrap(fr_time_unwrap(when) + atomic_load_explicit(&our_realtime, memory_order_consume)); + int64_t out; + + if (!fr_add(&out, fr_time_unwrap(when), atomic_load_explicit(&our_realtime, memory_order_consume))) { + return fr_time_unwrap(when) ? fr_unix_time_max() : fr_unix_time_min(); + } + return fr_unix_time_wrap(out); } /** Convert an fr_time_t (internal time) to number of usec since the unix epoch (wallclock time) @@ -453,7 +688,9 @@ static inline fr_unix_time_t fr_time_to_unix_time(fr_time_t when) */ static inline int64_t fr_time_to_usec(fr_time_t when) { - return ((fr_time_unwrap(when) + atomic_load_explicit(&our_realtime, memory_order_consume)) / (NSEC / USEC)); + /* Divide each operand separately to avoid overflow on addition */ + return (((fr_time_unwrap(when) / (NSEC / USEC)) + + (atomic_load_explicit(&our_realtime, memory_order_consume) / (NSEC / USEC)))); } /** Convert an fr_time_t (internal time) to number of msec since the unix epoch (wallclock time) @@ -461,7 +698,9 @@ static inline int64_t fr_time_to_usec(fr_time_t when) */ static inline int64_t fr_time_to_msec(fr_time_t when) { - return ((fr_time_unwrap(when) + atomic_load_explicit(&our_realtime, memory_order_consume)) / (NSEC / MSEC)); + /* Divide each operand separately to avoid overflow on addition */ + return (((fr_time_unwrap(when) / (NSEC / MSEC)) + + (atomic_load_explicit(&our_realtime, memory_order_consume) / (NSEC / MSEC)))); } /** Convert an fr_time_t (internal time) to number of csec since the unix epoch (wallclock time) @@ -469,7 +708,9 @@ static inline int64_t fr_time_to_msec(fr_time_t when) */ static inline int64_t fr_time_to_csec(fr_time_t when) { - return ((fr_time_unwrap(when) + atomic_load_explicit(&our_realtime, memory_order_consume)) / (NSEC / CSEC)); + /* Divide each operand separately to avoid overflow on addition */ + return (((fr_time_unwrap(when) / (NSEC / CSEC)) + + (atomic_load_explicit(&our_realtime, memory_order_consume) / (NSEC / CSEC)))); } /** Convert an fr_time_t (internal time) to number of sec since the unix epoch (wallclock time) @@ -477,7 +718,9 @@ static inline int64_t fr_time_to_csec(fr_time_t when) */ static inline int64_t fr_time_to_sec(fr_time_t when) { - return ((fr_time_unwrap(when) + atomic_load_explicit(&our_realtime, memory_order_consume)) / NSEC); + /* Divide each operand separately to avoid overflow on addition */ + return (((fr_time_unwrap(when) / NSEC) + + (atomic_load_explicit(&our_realtime, memory_order_consume) / NSEC))); } /** Convert server epoch time to unix epoch time @@ -492,6 +735,34 @@ static inline int64_t fr_time_to_sec(fr_time_t when) */ #define fr_time_to_timespec(_when) fr_time_delta_to_timespec(fr_time_delta_wrap(fr_time_wallclock_at_last_sync() + fr_time_unwrap(_when))) +/** Convert wallclock time to a fr_time_t (internal time) + * + * @param[out] overflow Whether the conversion overflowed. + * @param[in] when The timestamp to convert. + * @param[in] res The scale the integer value is in. + * @return + * - >0 number of nanoseconds since the server started. + * - 0 when the server started. + * - <0 number of nanoseconds before the server started. + */ +static inline fr_time_t fr_time_from_integer(bool *overflow, int64_t when, fr_time_res_t res) +{ + typeof_field(fr_time_t, value) out; + + if (!fr_multiply(&out, when, fr_time_multiplier_by_res[res])) { + if (overflow) *overflow = true; + return when > 0 ? fr_time_max() : fr_time_min(); + } + + if (!fr_sub(&out, out, atomic_load_explicit(&our_realtime, memory_order_consume))) { + if (overflow) *overflow = true; + return when < 0 ? fr_time_max() : fr_time_min(); + } + + if (overflow) *overflow = false; + return fr_time_wrap(out); +} + /** Convert a nsec (wallclock time) to a fr_time_t (internal time) * * @param[in] when The timestamp to convert. @@ -502,7 +773,12 @@ static inline int64_t fr_time_to_sec(fr_time_t when) */ static inline fr_time_t fr_time_from_nsec(int64_t when) { - return fr_time_wrap((when * NSEC) - atomic_load_explicit(&our_realtime, memory_order_consume)); + typeof_field(fr_time_t, value) out = fr_time_delta_unwrap(fr_time_delta_from_nsec(when)); + + if (!fr_sub(&out, out, atomic_load_explicit(&our_realtime, memory_order_consume))) { + return when > 0 ? fr_time_min() : fr_time_max(); + } + return fr_time_wrap(out); } /** Convert usec (wallclock time) to a fr_time_t (internal time) @@ -515,7 +791,12 @@ static inline fr_time_t fr_time_from_nsec(int64_t when) */ static inline fr_time_t fr_time_from_usec(int64_t when) { - return fr_time_wrap((when * USEC) - atomic_load_explicit(&our_realtime, memory_order_consume)); + typeof_field(fr_time_t, value) out = fr_time_delta_unwrap(fr_time_delta_from_usec(when)); + + if (!fr_sub(&out, out, atomic_load_explicit(&our_realtime, memory_order_consume))) { + return when > 0 ? fr_time_min() : fr_time_max(); + } + return fr_time_wrap(out); } /** Convert msec (wallclock time) to a fr_time_t (internal time) @@ -528,7 +809,12 @@ static inline fr_time_t fr_time_from_usec(int64_t when) */ static inline fr_time_t fr_time_from_msec(int64_t when) { - return fr_time_wrap((when * MSEC) - atomic_load_explicit(&our_realtime, memory_order_consume)); + typeof_field(fr_time_t, value) out = fr_time_delta_unwrap(fr_time_delta_from_msec(when)); + + if (!fr_sub(&out, out, atomic_load_explicit(&our_realtime, memory_order_consume))) { + return when > 0 ? fr_time_min() : fr_time_max(); + } + return fr_time_wrap(out); } /** Convert csec (wallclock time) to a fr_time_t (internal time) @@ -541,7 +827,12 @@ static inline fr_time_t fr_time_from_msec(int64_t when) */ static inline fr_time_t fr_time_from_csec(int64_t when) { - return fr_time_wrap((when * CSEC) - atomic_load_explicit(&our_realtime, memory_order_consume)); + typeof_field(fr_time_t, value) out = fr_time_delta_unwrap(fr_time_delta_from_csec(when)); + + if (!fr_sub(&out, out, atomic_load_explicit(&our_realtime, memory_order_consume))) { + return when > 0 ? fr_time_min() : fr_time_max(); + } + return fr_time_wrap(out); } /** Convert a time_t (wallclock time) to a fr_time_t (internal time) @@ -554,9 +845,16 @@ static inline fr_time_t fr_time_from_csec(int64_t when) */ static inline fr_time_t fr_time_from_sec(time_t when) { - return fr_time_wrap((when * NSEC) - atomic_load_explicit(&our_realtime, memory_order_consume)); + typeof_field(fr_time_t, value) out = fr_time_delta_unwrap(fr_time_delta_from_sec(when)); + + if (!fr_sub(&out, out, atomic_load_explicit(&our_realtime, memory_order_consume))) { + return when > 0 ? fr_time_min() : fr_time_max(); + } + return fr_time_wrap(out); } + + /** Convert a timespec (wallclock time) to a fr_time_t (internal time) * * @param[in] when_ts The timestamp to convert. @@ -567,7 +865,12 @@ static inline fr_time_t fr_time_from_sec(time_t when) */ static inline CC_HINT(nonnull) fr_time_t fr_time_from_timespec(struct timespec const *when_ts) { - return fr_time_wrap(fr_time_delta_unwrap(fr_time_delta_from_timespec(when_ts)) - atomic_load_explicit(&our_realtime, memory_order_consume)); + typeof_field(fr_time_t, value) tmp = fr_time_delta_unwrap(fr_time_delta_from_timespec(when_ts)), out; + + if (!fr_sub(&out, tmp, atomic_load_explicit(&our_realtime, memory_order_consume))) { + return tmp > 0 ? fr_time_min() : fr_time_max(); + } + return fr_time_wrap(out); } /** Convert a timeval (wallclock time) to a fr_time_t (internal time) @@ -580,7 +883,12 @@ static inline CC_HINT(nonnull) fr_time_t fr_time_from_timespec(struct timespec c */ static inline CC_HINT(nonnull) fr_time_t fr_time_from_timeval(struct timeval const *when_tv) { - return fr_time_wrap(fr_time_delta_unwrap(fr_time_delta_from_timeval(when_tv)) - atomic_load_explicit(&our_realtime, memory_order_consume)); + typeof_field(fr_time_t, value) tmp = fr_time_delta_unwrap(fr_time_delta_from_timeval(when_tv)), out; + + if (!fr_sub(&out, tmp, atomic_load_explicit(&our_realtime, memory_order_consume))) { + return tmp > 0 ? fr_time_min() : fr_time_max(); + } + return fr_time_wrap(out); } /** @} */ @@ -662,8 +970,6 @@ int fr_time_delta_from_str(fr_time_delta_t *out, char const *in, fr_time_res_t size_t fr_time_strftime_local(fr_sbuff_t *out, fr_time_t time, char const *fmt) CC_HINT(format(strftime, 3, 0)); size_t fr_time_strftime_utc(fr_sbuff_t *out, fr_time_t time, char const *fmt) CC_HINT(format(strftime, 3, 0)); -int64_t fr_time_delta_scale(fr_time_delta_t delta, fr_time_res_t hint); - void fr_time_elapsed_update(fr_time_elapsed_t *elapsed, fr_time_t start, fr_time_t end) CC_HINT(nonnull); void fr_time_elapsed_fprint(FILE *fp, fr_time_elapsed_t const *elapsed, char const *prefix, int tabs) CC_HINT(nonnull(1,2)); diff --git a/src/lib/util/value.c b/src/lib/util/value.c index 3483332a399..e7d33d9a0d1 100644 --- a/src/lib/util/value.c +++ b/src/lib/util/value.c @@ -1491,32 +1491,14 @@ ssize_t fr_value_box_to_network(fr_dbuff_t *dbuff, fr_value_box_t const *value) case FR_TYPE_DATE: { uint64_t date = 0; + fr_time_res_t res; if (!value->enumv) { - goto date_seconds; - - } else switch (value->enumv->flags.flag_time_res) { - date_seconds: - case FR_TIME_RES_SEC: - date = fr_unix_time_to_sec(value->vb_date); - break; - - case FR_TIME_RES_CSEC: - date = fr_unix_time_to_csec(value->vb_date); - break; - - case FR_TIME_RES_MSEC: - date = fr_unix_time_to_msec(value->vb_date); - break; - - case FR_TIME_RES_USEC: - date = fr_unix_time_to_usec(value->vb_date); - break; - - case FR_TIME_RES_NSEC: - date = fr_unix_time_to_usec(value->vb_date); - break; + res = FR_TIME_RES_SEC; + } else { + res = value->enumv->flags.flag_time_res; } + date = fr_unix_time_to_integer(value->vb_date, res); if (!value->enumv) { goto date_size4; @@ -1547,32 +1529,10 @@ ssize_t fr_value_box_to_network(fr_dbuff_t *dbuff, fr_value_box_t const *value) case FR_TYPE_TIME_DELTA: { int64_t date = 0; /* may be negative */ + fr_time_res_t res = FR_TIME_RES_SEC; + if (value->enumv) res = value->enumv->flags.flag_time_res; - if (!value->enumv) { - goto delta_seconds; - - } switch (value->enumv->flags.flag_time_res) { - delta_seconds: - case FR_TIME_RES_SEC: - date = fr_time_delta_to_sec(value->vb_time_delta); - break; - - case FR_TIME_RES_CSEC: - date = fr_time_delta_to_csec(value->vb_time_delta); - break; - - case FR_TIME_RES_MSEC: - date = fr_time_delta_to_msec(value->vb_time_delta); - break; - - case FR_TIME_RES_USEC: - date = fr_time_delta_to_usec(value->vb_time_delta); - break; - - case FR_TIME_RES_NSEC: - date = fr_time_delta_unwrap(value->vb_time_delta); - break; - } + date = fr_time_delta_to_integer(value->vb_time_delta, res); if (!value->enumv) { goto delta_size4; @@ -1878,7 +1838,12 @@ ssize_t fr_value_box_from_network(TALLOC_CTX *ctx, FR_DBUFF_OUT_UINT64V_RETURN(&date, &work_dbuff, length); - dst->vb_date = fr_unix_time_wrap(fr_time_scale(date, precision)); + if (!fr_multiply(&date, date, fr_time_multiplier_by_res[precision])) { + fr_strerror_const("date would overflow"); + return 0; + } + + dst->vb_date = fr_unix_time_wrap(date); } break; @@ -2809,30 +2774,12 @@ static inline int fr_value_box_cast_integer_to_integer(UNUSED TALLOC_CTX *ctx, f * nanoseconds -> seconds. */ case FR_TYPE_DATE: - if (dst->enumv) { - switch (dst->enumv->flags.flag_time_res) { - date_src_seconds: - case FR_TIME_RES_SEC: - tmp = fr_unix_time_to_sec(src->vb_date); - break; - - case FR_TIME_RES_CSEC: - tmp = fr_unix_time_to_csec(src->vb_date); - break; - - case FR_TIME_RES_USEC: - tmp = fr_unix_time_to_usec(src->vb_date); - break; - - case FR_TIME_RES_MSEC: - tmp = fr_unix_time_to_msec(src->vb_date); - break; + { + fr_time_res_t res = FR_TIME_RES_SEC; + if (dst->enumv) res = dst->enumv->flags.flag_time_res; - case FR_TIME_RES_NSEC: - tmp = fr_unix_time_unwrap(src->vb_date); - break; - } - } else goto date_src_seconds; + tmp = fr_unix_time_to_integer(src->vb_date, res); + } break; /* @@ -2842,30 +2789,13 @@ static inline int fr_value_box_cast_integer_to_integer(UNUSED TALLOC_CTX *ctx, f * signed integer for comparisons. */ case FR_TYPE_TIME_DELTA: - if (dst->enumv) { - switch (dst->enumv->flags.flag_time_res) { - delta_src_seconds: - case FR_TIME_RES_SEC: - tmp = (uint64_t)fr_time_delta_to_sec(src->vb_time_delta); - break; - - case FR_TIME_RES_CSEC: - tmp = (uint64_t)fr_time_delta_to_csec(src->vb_time_delta); - break; - - case FR_TIME_RES_USEC: - tmp = (uint64_t)fr_time_delta_to_usec(src->vb_time_delta); - break; + { + fr_time_res_t res = FR_TIME_RES_SEC; - case FR_TIME_RES_MSEC: - tmp = (uint64_t)fr_time_delta_to_msec(src->vb_time_delta); - break; + if (dst->enumv) res = dst->enumv->flags.flag_time_res; - case FR_TIME_RES_NSEC: - tmp = (uint64_t)fr_time_delta_unwrap(src->vb_time_delta); - break; - } - } else goto delta_src_seconds; + tmp = (uint64_t)fr_time_delta_to_integer(src->vb_time_delta, res); + } break; default: @@ -2908,58 +2838,32 @@ static inline int fr_value_box_cast_integer_to_integer(UNUSED TALLOC_CTX *ctx, f fr_value_box_init(dst, dst_type, dst_enumv, src->tainted); switch (dst_type) { case FR_TYPE_DATE: - if (dst->enumv) { - switch (dst->enumv->flags.flag_time_res) { - date_dst_seconds: - case FR_TIME_RES_SEC: - dst->vb_date = fr_unix_time_from_sec(tmp); - break; - - case FR_TIME_RES_CSEC: - dst->vb_date = fr_unix_time_from_csec(tmp); - break; - - case FR_TIME_RES_USEC: - dst->vb_date = fr_unix_time_from_usec(tmp); - break; - - case FR_TIME_RES_MSEC: - dst->vb_date = fr_unix_time_from_msec(tmp); - break; + { + bool overflow; + fr_time_res_t res = FR_TIME_RES_SEC; + if (dst->enumv) res = dst->enumv->flags.flag_time_res; - case FR_TIME_RES_NSEC: - dst->vb_date = fr_unix_time_from_nsec(tmp); - break; - } - } else goto date_dst_seconds; + dst->vb_date = fr_unix_time_from_integer(&overflow, tmp, res); + if (overflow) { + fr_strerror_const("Input to data type would overflow"); + return -1; + } + } break; case FR_TYPE_TIME_DELTA: - if (dst->enumv) { - switch (dst->enumv->flags.flag_time_res) { - delta_dst_seconds: - case FR_TIME_RES_SEC: - dst->vb_time_delta = fr_time_delta_from_sec(tmp); - break; - - case FR_TIME_RES_CSEC: - dst->vb_time_delta = fr_time_delta_from_csec(tmp); - break; - - case FR_TIME_RES_USEC: - dst->vb_time_delta = fr_time_delta_from_usec(tmp); - break; - - case FR_TIME_RES_MSEC: - dst->vb_time_delta = fr_time_delta_from_msec(tmp); - break; + { + bool overflow; + fr_time_res_t res = FR_TIME_RES_SEC; + if (dst->enumv) res = dst->enumv->flags.flag_time_res; - case FR_TIME_RES_NSEC: - dst->vb_time_delta = fr_time_delta_from_nsec(tmp); - break; - } - } else goto delta_dst_seconds; - break; + dst->vb_time_delta = fr_time_delta_from_integer(&overflow, tmp, res); + if (overflow) { + fr_strerror_const("Input to time_delta type would overflow"); + return -1; + } + } + break; default: #ifdef WORDS_BIGENDIAN @@ -5064,6 +4968,9 @@ ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff * formats. The RFC is much stricter. */ switch (data->enumv->flags.flag_time_res) { + case FR_TIME_RES_DAY: + case FR_TIME_RES_HOUR: + case FR_TIME_RES_MIN: case FR_TIME_RES_SEC: break; @@ -5113,7 +5020,6 @@ ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff int64_t lhs = 0; uint64_t rhs = 0; fr_time_res_t res = FR_TIME_RES_SEC; - if (data->enumv) res = data->enumv->flags.flag_time_res; /* @@ -5122,32 +5028,8 @@ ssize_t fr_value_box_print(fr_sbuff_t *out, fr_value_box_t const *data, fr_sbuff */ #define MOD(a,b) (((a<0) ? (-a) : (a))%(b)) - switch (res) { - case FR_TIME_RES_SEC: - lhs = fr_time_delta_to_sec(data->datum.time_delta); - rhs = MOD(fr_time_delta_unwrap(data->datum.time_delta), NSEC); - break; - - case FR_TIME_RES_CSEC: - lhs = fr_time_delta_to_csec(data->datum.time_delta); - rhs = MOD(fr_time_delta_unwrap(data->datum.time_delta), (NSEC / CSEC)); - break; - - case FR_TIME_RES_MSEC: - lhs = fr_time_delta_to_msec(data->datum.time_delta); - rhs = MOD(fr_time_delta_unwrap(data->datum.time_delta), (NSEC / MSEC)); - break; - - case FR_TIME_RES_USEC: - lhs = fr_time_delta_to_usec(data->datum.time_delta); - rhs = MOD(fr_time_delta_unwrap(data->datum.time_delta), (NSEC / USEC)); - break; - - case FR_TIME_RES_NSEC: - lhs = fr_time_delta_unwrap(data->datum.time_delta); - rhs = 0; - break; - } + lhs = fr_time_delta_to_integer(data->datum.time_delta, res); + rhs = MOD(fr_time_delta_unwrap(data->datum.time_delta), fr_time_multiplier_by_res[res]); if (!data->enumv || !data->enumv->flags.is_unsigned) { /* diff --git a/src/modules/rlm_expr/rlm_expr.c b/src/modules/rlm_expr/rlm_expr.c index 576a86814ea..a0a1bf2f7f1 100644 --- a/src/modules/rlm_expr/rlm_expr.c +++ b/src/modules/rlm_expr/rlm_expr.c @@ -274,7 +274,7 @@ static bool get_number(request_t *request, char const **string, int64_t *answer) * integer number of milliseconds */ case FR_TYPE_TIME_DELTA: - y = fr_time_delta_scale(vp->vp_time_delta, vp->data.enumv ? vp->data.enumv->flags.flag_time_res : FR_TIME_RES_SEC); + y = fr_time_delta_to_integer(vp->vp_time_delta, vp->data.enumv ? vp->data.enumv->flags.flag_time_res : FR_TIME_RES_SEC); break; case FR_TYPE_DATE: