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

Reply via email to