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; + } +} + 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
