From: Arran Cudbard-Bell Date: Mon, 7 Sep 2020 16:17:20 +0000 (-0400) Subject: value: Fix conversion of numeric types to dates X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c87b58a612fc6fba0f90aa2a6dc2bf5387fef2a2;p=thirdparty%2Ffreeradius-server.git value: Fix conversion of numeric types to dates --- diff --git a/src/lib/util/time.c b/src/lib/util/time.c index ad0daa01b30..a860a37b13c 100644 --- a/src/lib/util/time.c +++ b/src/lib/util/time.c @@ -255,6 +255,45 @@ fr_time_t fr_time_from_sec(time_t when) return (((fr_time_t) when) * NSEC) - atomic_load_explicit(&our_realtime, memory_order_consume); } +/** Convert msec (wallclock time) to a fr_time_t (internal time) + * + * @param[in] when The timestamp to convert. + * @return + * - >0 number of nanoseconds since the server started. + * - 0 when the server started. + * - <0 number of nanoseconds before the server started. + */ +fr_time_t fr_time_from_msec(int64_t when) +{ + return (((fr_time_t) when) * MSEC) - atomic_load_explicit(&our_realtime, memory_order_consume); +} + +/** Convert usec (wallclock time) to a fr_time_t (internal time) + * + * @param[in] when The timestamp to convert. + * @return + * - >0 number of nanoseconds since the server started. + * - 0 when the server started. + * - <0 number of nanoseconds before the server started. + */ +fr_time_t fr_time_from_usec(int64_t when) +{ + return (((fr_time_t) when) * USEC) - atomic_load_explicit(&our_realtime, memory_order_consume); +} + +/** Convert a nsec (wallclock time) to a fr_time_t (internal time) + * + * @param[in] when The timestamp to convert. + * @return + * - >0 number of nanoseconds since the server started. + * - 0 when the server started. + * - <0 number of nanoseconds before the server started. + */ +fr_time_t fr_time_from_nsec(int64_t when) +{ + return (((fr_time_t) when) * NSEC) - atomic_load_explicit(&our_realtime, memory_order_consume); +} + /** Convert a timespec (wallclock time) to a fr_time_t (internal time) * * @param[in] when_ts The timestamp to convert. diff --git a/src/lib/util/time.h b/src/lib/util/time.h index c0580ed031f..5346f00cefb 100644 --- a/src/lib/util/time.h +++ b/src/lib/util/time.h @@ -85,6 +85,7 @@ typedef struct { #define NSEC (1000000000) #define USEC (1000000) +#define MSEC (1000) int fr_time_start(void); int fr_time_sync(void); @@ -197,6 +198,10 @@ fr_unix_time_t fr_time_to_unix_time(fr_time_t when); int64_t fr_time_wallclock_at_last_sync(void); fr_time_t fr_time_from_sec(time_t when) CC_HINT(nonnull); +fr_time_t fr_time_from_msec(int64_t when) CC_HINT(nonnull); +fr_time_t fr_time_from_usec(int64_t when) CC_HINT(nonnull); +fr_time_t fr_time_from_nsec(int64_t when) CC_HINT(nonnull); + fr_time_t fr_time_from_timeval(struct timeval const *when_tv) CC_HINT(nonnull); fr_time_t fr_time_from_timespec(struct timespec const *when_tv) CC_HINT(nonnull); int fr_time_delta_from_time_zone(char const *tz, fr_time_delta_t *delta) CC_HINT(nonnull); diff --git a/src/lib/util/value.c b/src/lib/util/value.c index 413f7026220..4ae0e6aa8ab 100644 --- a/src/lib/util/value.c +++ b/src/lib/util/value.c @@ -1550,13 +1550,13 @@ ssize_t fr_value_box_from_network(TALLOC_CTX *ctx, fr_value_box_hton(dst, dst); /* Operate in-place */ break; - /* - * Dates and deltas are stored internally as - * 64-bit nanoseconds. We have to convert from - * the network format. First by size - * (16/32/64-bit), and then by resolution (ns, - * us, ms, s). - */ + /* + * Dates and deltas are stored internally as + * 64-bit nanoseconds. We have to convert from + * the network format. First by size + * (16/32/64-bit), and then by resolution (ns, + * us, ms, s). + */ case FR_TYPE_DATE: { size_t i, length = 4; @@ -2599,6 +2599,7 @@ static inline int fr_value_box_cast_to_uint16(TALLOC_CTX *ctx, fr_value_box_t *d * Pre-initialise box for non-variable types */ fr_value_box_init(dst, dst_type, dst_enumv, src->tainted); + switch (src->type) { case FR_TYPE_BOOL: dst->vb_uint16 = (src->vb_bool == true) ? 1 : 0; @@ -2648,7 +2649,6 @@ static inline int fr_value_box_cast_to_uint32(TALLOC_CTX *ctx, fr_value_box_t *d { fr_assert(dst_type == FR_TYPE_UINT32); - switch (src->type) { case FR_TYPE_STRING: return fr_value_box_from_str(ctx, dst, &dst_type, dst_enumv, @@ -2711,12 +2711,11 @@ static inline int fr_value_box_cast_to_uint32(TALLOC_CTX *ctx, fr_value_box_t *d fr_unix_time_t cast; switch (dst->enumv->flags.type_size) { - date_seconds: default: case FR_TIME_RES_SEC: cast = fr_unix_time_to_sec(src->vb_date); - if (cast >= ((uint64_t) 1) << 32) { + if (cast >= UINT32_MAX) { invalid_cast: fr_strerror_printf("Invalid cast: result cannot fit into uint32"); return -1; @@ -2726,25 +2725,19 @@ static inline int fr_value_box_cast_to_uint32(TALLOC_CTX *ctx, fr_value_box_t *d case FR_TIME_RES_USEC: cast = fr_unix_time_to_usec(src->vb_date); - if (cast >= ((uint64_t) 1) << 32) goto invalid_cast; - - dst->vb_uint32 = cast; break; case FR_TIME_RES_MSEC: cast = fr_unix_time_to_msec(src->vb_date); - if (cast >= ((uint64_t) 1) << 32) goto invalid_cast; - - dst->vb_uint32 = cast; break; case FR_TIME_RES_NSEC: cast = src->vb_date; - if (cast >= ((uint64_t) 1) << 32) goto invalid_cast; - - dst->vb_uint32 = cast; break; } + if ((cast >= UINT32_MAX) || (cast < 0)) goto invalid_cast; + + dst->vb_uint32 = cast; } break; @@ -2758,37 +2751,27 @@ static inline int fr_value_box_cast_to_uint32(TALLOC_CTX *ctx, fr_value_box_t *d int64_t cast; switch (dst->enumv->flags.type_size) { - delta_seconds: default: case FR_TIME_RES_SEC: cast = fr_time_delta_to_sec(src->vb_time_delta); - if (cast >= ((int64_t) 1) << 32) goto invalid_cast; - - dst->vb_uint32 = cast; break; case FR_TIME_RES_USEC: cast = fr_time_delta_to_usec(src->vb_time_delta); - if (cast >= ((int64_t) 1) << 32) goto invalid_cast; - - dst->vb_uint32 = cast; break; case FR_TIME_RES_MSEC: cast = fr_time_delta_to_msec(src->vb_time_delta); - if (cast >= ((int64_t) 1) << 32) goto invalid_cast; - - dst->vb_uint32 = cast; break; case FR_TIME_RES_NSEC: cast = src->vb_time_delta; - if (cast >= ((int64_t) 1) << 32) goto invalid_cast; - - dst->vb_uint32 = cast; break; } + if ((cast >= UINT32_MAX) || (cast < 0)) goto invalid_cast; + + dst->vb_uint32 = cast; } break; @@ -2953,6 +2936,146 @@ static inline int fr_value_box_cast_to_uint64(TALLOC_CTX *ctx, fr_value_box_t *d return 0; } +static inline int fr_value_box_cast_to_date(TALLOC_CTX *ctx, fr_value_box_t *dst, + fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, + fr_value_box_t const *src) +{ + int64_t tmp = 0; + + fr_assert(dst_type == FR_TYPE_DATE); + + switch (src->type) { + case FR_TYPE_STRING: + return fr_value_box_from_str(ctx, dst, &dst_type, dst_enumv, + src->vb_strvalue, src->datum.length, '\0', src->tainted); + + default: + break; + } + + /* + * Pre-initialise box for non-variable types + */ + fr_value_box_init(dst, dst_type, dst_enumv, src->tainted); + + switch (src->type) { + /* + * First convert to a numeric type + */ + case FR_TYPE_OCTETS: + { + fr_type_t width; + fr_value_box_t number; + + switch (src->vb_length) { + case 8: + width = FR_TYPE_UINT64; + break; + + case 4: + width = FR_TYPE_UINT32; + break; + + case 2: + width = FR_TYPE_UINT16; + break; + + case 1: + width = FR_TYPE_UINT8; + break; + + default: + fr_strerror_printf("Invalid cast from %s to %s. Source is length %zd does not match " + "the length of a numeric type", + fr_table_str_by_value(fr_value_box_type_table, src->type, ""), + fr_table_str_by_value(fr_value_box_type_table, dst_type, ""), + src->datum.length); + return -1; + } + + if (fr_value_box_fixed_size_from_octets(&number, width, dst_enumv, src) < 0) return -1; + + return fr_value_box_cast_to_date(ctx, dst, dst_type, dst_enumv, &number); + } + break; + + case FR_TYPE_UINT8: + tmp = (int64_t)src->vb_uint8; + + assign: + if (!dst->enumv) { + goto delta_seconds; + } else switch (dst->enumv->flags.type_size) { + delta_seconds: + default: + case FR_TIME_RES_SEC: + dst->vb_uint64 = fr_unix_time_from_sec(tmp); + break; + + case FR_TIME_RES_USEC: + dst->vb_uint64 = fr_unix_time_from_usec(tmp); + break; + + case FR_TIME_RES_MSEC: + dst->vb_uint64 = fr_unix_time_from_msec(tmp); + break; + + case FR_TIME_RES_NSEC: + dst->vb_uint64 = fr_unix_time_from_nsec(tmp); + break; + } + break; + + case FR_TYPE_UINT16: + tmp = (int64_t)src->vb_uint16; + goto assign; + + case FR_TYPE_UINT32: + tmp = (int64_t)src->vb_uint32; + goto assign; + + case FR_TYPE_UINT64: + if (src->vb_uint64 > INT64_MAX) { + fr_strerror_printf("Invalid cast from %s to %s. %" PRIu64 " outside of value range " + "%" PRId64 "-%" PRId64, + fr_table_str_by_value(fr_value_box_type_table, src->type, ""), + fr_table_str_by_value(fr_value_box_type_table, dst_type, ""), + src->vb_uint64, INT64_MIN, INT64_MAX); + return -1; + } + tmp = (int64_t)src->vb_uint64; + goto assign; + + case FR_TYPE_INT8: + tmp = (int64_t)src->vb_int8; + goto assign; + + case FR_TYPE_INT16: + tmp = (int64_t)src->vb_int16; + goto assign; + + case FR_TYPE_INT32: + tmp = (int64_t)src->vb_int32; + goto assign; + + case FR_TYPE_INT64: + tmp = (int64_t)src->vb_int64; + goto assign; + + case FR_TYPE_TIME_DELTA: + dst->vb_date = fr_time_from_nsec(src->vb_time_delta); + break; + + default: + fr_strerror_printf("Invalid cast from %s to %s. Unsupported", + fr_table_str_by_value(fr_value_box_type_table, src->type, ""), + fr_table_str_by_value(fr_value_box_type_table, dst_type, "")); + return -1; + } + + return 0; +} + /** Convert one type of fr_value_box_t to another * * This should be the canonical function used to convert between INTERNAL data formats. @@ -3072,7 +3195,11 @@ int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, case FR_TYPE_INT64: case FR_TYPE_FLOAT32: case FR_TYPE_FLOAT64: + break; + case FR_TYPE_DATE: + return fr_value_box_cast_to_date(ctx, dst, dst_type, dst_enumv, src); + case FR_TYPE_SIZE: case FR_TYPE_TIME_DELTA: case FR_TYPE_ABINARY: