On Tue, Nov 18, 2025 at 11:39 AM Gaetan Rivet via dev <
[email protected]> wrote:

> Add helpers to do simple arithmetic operations safely on u64 integers.
> Additional, substraction and multiplication overflow are detected and
> avoided.
>
> Signed-off-by: Gaetan Rivet <[email protected]>
> ---
>  lib/util.h        | 25 ++++++++++++++++++
>  tests/library.at  |  5 ++++
>  tests/test-util.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 95 insertions(+)
>
> diff --git a/lib/util.h b/lib/util.h
> index ef993626a1..18dfd6975a 100644
> --- a/lib/util.h
> +++ b/lib/util.h
> @@ -590,6 +590,31 @@ ovs_u128_is_superset(ovs_u128 super, ovs_u128 sub)
>              uint_is_superset(super.u64.lo, sub.u64.lo));
>  }
>
> +static inline uint64_t
> +ovs_u64_safeadd(uint64_t a, uint64_t b)
> +{
> +    return (UINT64_MAX - a < b) ? UINT64_MAX : a + b;
> +}
> +
> +static inline uint64_t
> +ovs_u64_safesub(uint64_t a, uint64_t b)
> +{
> +    return (a < b) ? 0 : a - b;
> +}
> +
> +static inline uint64_t
> +ovs_u64_safemul(uint64_t a, uint64_t b)
> +{
> +    static const uint64_t sqrt_u64_max = UINT64_C(1) << (64 / 2);
> +
> +    if ((a >= sqrt_u64_max || b >= sqrt_u64_max) &&
> +        a > 0 && UINT64_MAX / a < b) {
> +        return UINT64_MAX;
> +    } else {
> +        return a * b;
> +    }
> +}
>

Minor point but why not just use:

bool __builtin_add_overflow   (type1 x, type2 y, type3 *sum);
bool __builtin_sub_overflow   (type1 x, type2 y, type3 *diff);
bool __builtin_mul_overflow   (type1 x, type2 y, type3 *prod);

-M


> +
>  void xsleep(unsigned int seconds);
>  void xnanosleep(uint64_t nanoseconds);
>  void xnanosleep_no_quiesce(uint64_t nanoseconds);
> diff --git a/tests/library.at b/tests/library.at
> index 20899bde00..507d343d11 100644
> --- a/tests/library.at
> +++ b/tests/library.at
> @@ -254,6 +254,11 @@ AT_KEYWORDS([sat math sat_math])
>  AT_CHECK([ovstest test-util sat_math])
>  AT_CLEANUP
>
> +AT_SETUP([safe U64 operations])
> +AT_KEYWORDS([math])
> +AT_CHECK([ovstest test-util safe-u64])
> +AT_CLEANUP
> +
>  AT_SETUP([snprintf])
>  AT_CHECK([ovstest test-util snprintf])
>  AT_CLEANUP
> diff --git a/tests/test-util.c b/tests/test-util.c
> index 5d88d38f26..2650c9507e 100644
> --- a/tests/test-util.c
> +++ b/tests/test-util.c
> @@ -1183,6 +1183,70 @@ test_sat_math(struct ovs_cmdl_context *ctx
> OVS_UNUSED)
>      }
>  }
>
> +static void
> +test_safe_u64(struct ovs_cmdl_context *ctx OVS_UNUSED)
> +{
> +    struct {
> +        uint64_t       a,              b,           expected;
> +    } add_cases[] = {
> +        {              0,              0,                  0, },
> +        {              1,              2,                  3, },
> +        {              0,     UINT64_MAX,         UINT64_MAX, },
> +        {     UINT64_MAX,              0,         UINT64_MAX, },
> +        {             -1,              0,         UINT64_MAX, },
> +        {             -1,             -1,         UINT64_MAX, },
> +        { UINT64_MAX / 2, UINT64_MAX / 2,     UINT64_MAX - 1, },
> +        {              2, UINT64_MAX / 2, UINT64_MAX / 2 + 2, },
> +    },
> +    sub_cases[] = {
> +        {              0,              0,                  0, },
> +        {              1,              2,                  0, },
> +        {              0,     UINT64_MAX,                  0, },
> +        {     UINT64_MAX,              0,         UINT64_MAX, },
> +        {             -1,              0,         UINT64_MAX, },
> +        {             -1,             -1,                  0, },
> +        { UINT64_MAX / 2, UINT64_MAX / 2,                  0, },
> +        {              2, UINT64_MAX / 2,                  0, },
> +    },
> +    mul_cases[] = {
> +        {              0,              0,                  0, },
> +        {              0,              2,                  0, },
> +        {              1,              2,                  2, },
> +        {              0,     UINT64_MAX,                  0, },
> +        {     UINT64_MAX,              0,                  0, },
> +        {             -1,              0,                  0, },
> +        {              0,             -1,                  0, },
> +        {             -1,             -1,         UINT64_MAX, },
> +        {     4294967296,     4294967296,         UINT64_MAX, },
> +        { UINT64_MAX / 2, UINT64_MAX / 2,         UINT64_MAX, },
> +        {              2, UINT64_MAX / 2,     UINT64_MAX - 1, },
> +    };
> +
> +    for (size_t i = 0; i < ARRAY_SIZE(add_cases); i++) {
> +        uint64_t a = add_cases[i].a;
> +        uint64_t b = add_cases[i].b;
> +        uint64_t expected = add_cases[i].expected;
> +
> +        ovs_assert(ovs_u64_safeadd(a, b) == expected);
> +    }
> +
> +    for (size_t i = 0; i < ARRAY_SIZE(sub_cases); i++) {
> +        uint64_t a = sub_cases[i].a;
> +        uint64_t b = sub_cases[i].b;
> +        uint64_t expected = sub_cases[i].expected;
> +
> +        ovs_assert(ovs_u64_safesub(a, b) == expected);
> +    }
> +
> +    for (size_t i = 0; i < ARRAY_SIZE(mul_cases); i++) {
> +        uint64_t a = mul_cases[i].a;
> +        uint64_t b = mul_cases[i].b;
> +        uint64_t expected = mul_cases[i].expected;
> +
> +        ovs_assert(ovs_u64_safemul(a, b) == expected);
> +    }
> +}
> +
>  #ifndef _WIN32
>  static void
>  test_file_name(struct ovs_cmdl_context *ctx)
> @@ -1220,6 +1284,7 @@ static const struct ovs_cmdl_command commands[] = {
>      {"ovs_scan", NULL, 0, 0, test_ovs_scan, OVS_RO},
>      {"snprintf", NULL, 0, 0, test_snprintf, OVS_RO},
>      {"sat_math", NULL, 0, 0, test_sat_math, OVS_RO},
> +    {"safe-u64", NULL, 0, 0, test_safe_u64, OVS_RO},
>  #ifndef _WIN32
>      {"file_name", NULL, 1, INT_MAX, test_file_name, OVS_RO},
>  #endif
> --
> 2.34.1
>
> _______________________________________________
> dev mailing list
> [email protected]
> https://mail.openvswitch.org/mailman/listinfo/ovs-dev
>
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to