test: add unit tests for integer overflow helpers
authorPauli <pauli@openssl.org>
Wed, 27 Oct 2021 23:21:40 +0000 (09:21 +1000)
committerPauli <pauli@openssl.org>
Fri, 12 Nov 2021 09:49:46 +0000 (19:49 +1000)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/16930)

test/build.info
test/safe_math_test.c [new file with mode: 0644]

index 0f379e11e222fb6ffb975db4367a42f83555f430..bc8d4002322beabd7d880bf435a546e4c04aa425 100644 (file)
@@ -45,7 +45,7 @@ IF[{- !$disabled{tests} -}]
           evp_fetch_prov_test v3nametest v3ext \
           crltest danetest bad_dtls_test lhash_test sparse_array_test \
           conf_include_test params_api_test params_conversion_test \
-          constant_time_test verify_extra_test clienthellotest \
+          constant_time_test safe_math_test verify_extra_test clienthellotest \
           packettest asynctest secmemtest srptest memleaktest stack_test \
           dtlsv1listentest ct_test threadstest afalgtest d2i_test \
           ssl_test_ctx_test ssl_test x509aux cipherlist_test asynciotest \
@@ -245,6 +245,10 @@ IF[{- !$disabled{tests} -}]
   INCLUDE[constant_time_test]=../include ../apps/include
   DEPEND[constant_time_test]=../libcrypto libtestutil.a
 
+  SOURCE[safe_math_test]=safe_math_test.c
+  INCLUDE[safe_math_test]=../include ../apps/include
+  DEPEND[safe_math_test]=../libcrypto libtestutil.a
+
   SOURCE[verify_extra_test]=verify_extra_test.c
   INCLUDE[verify_extra_test]=../include ../apps/include
   DEPEND[verify_extra_test]=../libcrypto libtestutil.a
diff --git a/test/safe_math_test.c b/test/safe_math_test.c
new file mode 100644 (file)
index 0000000..da50ec8
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
+ *
+ * Licensed under the Apache License 2.0 (the "License").  You may not use
+ * this file except in compliance with the License.  You can obtain a copy
+ * in the file LICENSE in the source distribution or at
+ * https://www.openssl.org/source/license.html
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+/*
+ * Uncomment this if the fallback non-builtin overflow checking is to
+ * be tested.
+ */
+/*#define OPENSSL_NO_BUILTIN_OVERFLOW_CHECKING*/
+
+#include "internal/nelem.h"
+#include "internal/safe_math.h"
+#include "testutil.h"
+
+/* Create the safe math instances we're interested in */
+OSSL_SAFE_MATH_SIGNED(int, int)
+OSSL_SAFE_MATH_UNSIGNED(uint, unsigned int)
+OSSL_SAFE_MATH_UNSIGNED(size_t, size_t)
+
+static const struct {
+    int a, b;
+    int sum_err, sub_err, mul_err, div_err, mod_err, neg_a_err, neg_b_err;
+    int abs_a_err, abs_b_err;
+} test_ints[] = {
+    { 1, 3,                 0, 0, 0, 0, 0, 0, 0, 0, 0 },
+    { -1, 3,                0, 0, 0, 0, 0, 0, 0, 0, 0 },
+    { 1, -3,                0, 0, 0, 0, 0, 0, 0, 0, 0 },
+    { -1, -3,               0, 0, 0, 0, 0, 0, 0, 0, 0 },
+    { INT_MAX, 1,           1, 0, 0, 0, 0, 0, 0, 0, 0 },
+    { INT_MAX, 2,           1, 0, 1, 0, 0, 0, 0, 0, 0 },
+    { INT_MIN, 1,           0, 1, 0, 0, 0, 1, 0, 1, 0 },
+    { 1, INT_MIN,           0, 1, 0, 0, 0, 0, 1, 0, 1 },
+    { INT_MIN, 2,           0, 1, 1, 0, 0, 1, 0, 1, 0 },
+    { 2, INT_MIN,           0, 1, 1, 0, 0, 0, 1, 0, 1 },
+    { INT_MIN, -1,          1, 0, 1, 1, 1, 1, 0, 1, 0 },
+    { INT_MAX, INT_MIN,     0, 1, 1, 0, 0, 0, 1, 0, 1 },
+    { INT_MIN, INT_MAX,     0, 1, 1, 0, 0, 1, 0, 1, 0 },
+    { 3, 0,                 0, 0, 0, 1, 1, 0, 0, 0, 0 },
+};
+
+static int test_int_ops(int n)
+{
+    int err, r;
+    const int a = test_ints[n].a, b = test_ints[n].b;
+
+    err = 0;
+    r = safe_add_int(a, b, &err);
+    if (!TEST_int_eq(err, test_ints[n].sum_err)
+            || (!err && !TEST_int_eq(r, a + b)))
+        goto err;
+
+    err = 0;
+    r = safe_sub_int(a, b, &err);
+    if (!TEST_int_eq(err, test_ints[n].sub_err)
+            || (!err && !TEST_int_eq(r, a - b)))
+        goto err;
+
+    err = 0;
+    r = safe_mul_int(a, b, &err);
+    if (!TEST_int_eq(err, test_ints[n].mul_err)
+            || (!err && !TEST_int_eq(r, a * b)))
+        goto err;
+
+    err = 0;
+    r = safe_div_int(a, b, &err);
+    if (!TEST_int_eq(err, test_ints[n].div_err)
+            || (!err && !TEST_int_eq(r, a / b)))
+        goto err;
+
+    err = 0;
+    r = safe_mod_int(a, b, &err);
+    if (!TEST_int_eq(err, test_ints[n].mod_err)
+            || (!err && !TEST_int_eq(r, a % b)))
+        goto err;
+
+    err = 0;
+    r = safe_neg_int(a, &err);
+    if (!TEST_int_eq(err, test_ints[n].neg_a_err)
+            || (!err && !TEST_int_eq(r, -a)))
+        goto err;
+
+    err = 0;
+    r = safe_neg_int(b, &err);
+    if (!TEST_int_eq(err, test_ints[n].neg_b_err)
+            || (!err && !TEST_int_eq(r, -b)))
+        goto err;
+
+    err = 0;
+    r = safe_abs_int(a, &err);
+    if (!TEST_int_eq(err, test_ints[n].abs_a_err)
+            || (!err && !TEST_int_eq(r, a < 0 ? -a : a)))
+        goto err;
+
+    err = 0;
+    r = safe_abs_int(b, &err);
+    if (!TEST_int_eq(err, test_ints[n].abs_b_err)
+            || (!err && !TEST_int_eq(r, b < 0 ? -b : b)))
+        goto err;
+    return 1;
+ err:
+    TEST_info("a = %d  b = %d  r = %d  err = %d", a, b, r, err);
+    return 0;
+}
+
+static const struct {
+    unsigned int a, b;
+    int sum_err, sub_err, mul_err, div_err, mod_err;
+} test_uints[] = {
+    { 3, 1,                 0, 0, 0, 0, 0 },
+    { 1, 3,                 0, 1, 0, 0, 0 },
+    { UINT_MAX, 1,          1, 0, 0, 0, 0 },
+    { UINT_MAX, 2,          1, 0, 1, 0, 0 },
+    { 1, UINT_MAX,          1, 1, 0, 0, 0 },
+    { 2, UINT_MAX,          1, 1, 1, 0, 0 },
+    { UINT_MAX, 0,          0, 0, 0, 1, 1 },
+};
+
+static int test_uint_ops(int n)
+{
+    int err;
+    unsigned int r;
+    const unsigned int a = test_uints[n].a, b = test_uints[n].b;
+
+    err = 0;
+    r = safe_add_uint(a, b, &err);
+    if (!TEST_int_eq(err, test_uints[n].sum_err)
+            || (!err && !TEST_uint_eq(r, a + b)))
+        goto err;
+
+    err = 0;
+    r = safe_sub_uint(a, b, &err);
+    if (!TEST_int_eq(err, test_uints[n].sub_err)
+            || (!err && !TEST_uint_eq(r, a - b)))
+        goto err;
+
+    err = 0;
+    r = safe_mul_uint(a, b, &err);
+    if (!TEST_int_eq(err, test_uints[n].mul_err)
+            || (!err && !TEST_uint_eq(r, a * b)))
+        goto err;
+
+    err = 0;
+    r = safe_div_uint(a, b, &err);
+    if (!TEST_int_eq(err, test_uints[n].div_err)
+            || (!err && !TEST_uint_eq(r, a / b)))
+        goto err;
+
+    err = 0;
+    r = safe_mod_uint(a, b, &err);
+    if (!TEST_int_eq(err, test_uints[n].mod_err)
+            || (!err && !TEST_uint_eq(r, a % b)))
+        goto err;
+
+    err = 0;
+    r = safe_neg_uint(a, &err);
+    if (!TEST_int_eq(err, a != 0) || (!err && !TEST_uint_eq(r, 0)))
+        goto err;
+
+    err = 0;
+    r = safe_neg_uint(b, &err);
+    if (!TEST_int_eq(err, b != 0) || (!err && !TEST_uint_eq(r, 0)))
+        goto err;
+    err = 0;
+    r = safe_abs_uint(a, &err);
+    if (!TEST_int_eq(err, 0) || !TEST_uint_eq(r, a))
+        goto err;
+
+    err = 0;
+    r = safe_abs_uint(b, &err);
+    if (!TEST_int_eq(err, 0) || !TEST_uint_eq(r, b))
+        goto err;
+   return 1;
+ err:
+    TEST_info("a = %u  b = %u  r = %u  err = %d", a, b, r, err);
+    return 0;
+}
+
+static const struct {
+    size_t a, b;
+    int sum_err, sub_err, mul_err, div_err, mod_err;
+} test_size_ts[] = {
+    { 3, 1,                 0, 0, 0, 0, 0 },
+    { 1, 3,                 0, 1, 0, 0, 0 },
+    { SIZE_MAX, 1,          1, 0, 0, 0, 0 },
+    { SIZE_MAX, 2,          1, 0, 1, 0, 0 },
+    { 1, SIZE_MAX,          1, 1, 0, 0, 0 },
+    { 2, SIZE_MAX,          1, 1, 1, 0, 0 },
+    { 11, 0,                0, 0, 0, 1, 1 },
+};
+
+static int test_size_t_ops(int n)
+{
+    int err;
+    size_t r;
+    const size_t a = test_size_ts[n].a, b = test_size_ts[n].b;
+
+    err = 0;
+    r = safe_add_size_t(a, b, &err);
+    if (!TEST_int_eq(err, test_size_ts[n].sum_err)
+            || (!err && !TEST_size_t_eq(r, a + b)))
+        goto err;
+
+    err = 0;
+    r = safe_sub_size_t(a, b, &err);
+    if (!TEST_int_eq(err, test_size_ts[n].sub_err)
+            || (!err && !TEST_size_t_eq(r, a - b)))
+        goto err;
+
+    err = 0;
+    r = safe_mul_size_t(a, b, &err);
+    if (!TEST_int_eq(err, test_size_ts[n].mul_err)
+            || (!err && !TEST_size_t_eq(r, a * b)))
+        goto err;
+
+    err = 0;
+    r = safe_div_size_t(a, b, &err);
+    if (!TEST_int_eq(err, test_uints[n].div_err)
+            || (!err && !TEST_size_t_eq(r, a / b)))
+        goto err;
+
+    err = 0;
+    r = safe_mod_size_t(a, b, &err);
+    if (!TEST_int_eq(err, test_size_ts[n].mod_err)
+            || (!err && !TEST_size_t_eq(r, a % b)))
+        goto err;
+
+    err = 0;
+    r = safe_neg_size_t(a, &err);
+    if (!TEST_int_eq(err, a != 0) || (!err && !TEST_size_t_eq(r, 0)))
+        goto err;
+
+    err = 0;
+    r = safe_neg_size_t(b, &err);
+    if (!TEST_int_eq(err, b != 0) || (!err && !TEST_size_t_eq(r, 0)))
+        goto err;
+    err = 0;
+    r = safe_abs_size_t(a, &err);
+    if (!TEST_int_eq(err, 0) || !TEST_size_t_eq(r, a))
+        goto err;
+
+    err = 0;
+    r = safe_abs_size_t(b, &err);
+    if (!TEST_int_eq(err, 0) || !TEST_size_t_eq(r, b))
+        goto err;
+    return 1;
+ err:
+    TEST_info("a = %zu  b = %zu  r = %zu  err = %d", a, b, r, err);
+    return 0;
+}
+
+static const struct {
+    int a, b, c;
+    int err;
+} test_muldiv_ints[] = {
+    { 3, 1, 2,                          0 },
+    { 1, 3, 2,                          0 },
+    { -3, 1, 2,                         0 },
+    { 1, 3, -2,                         0 },
+    { INT_MAX, INT_MAX, INT_MAX,        0 },
+    { INT_MIN, INT_MIN, INT_MAX,        1 },
+    { INT_MIN, INT_MIN, INT_MIN,        0 },
+    { INT_MAX, 2, 4,                    0 },
+    { 8, INT_MAX, 4,                    1 },
+    { INT_MAX, 8, 4,                    1 },
+    { INT_MIN, 2, 4,                    1 },
+    { 8, INT_MIN, 4,                    1 },
+    { INT_MIN, 8, 4,                    1 },
+    { 3, 4, 0,                          1 },
+};
+
+static int test_int_muldiv(int n)
+{
+    int err = 0;
+    int r, real = 0;
+    const int a = test_muldiv_ints[n].a;
+    const int b = test_muldiv_ints[n].b;
+    const int c = test_muldiv_ints[n].c;
+
+    r = safe_muldiv_int(a, b, c, &err);
+    if (c != 0)
+        real = (int)((int64_t)a * (int64_t)b / (int64_t)c);
+    if (!TEST_int_eq(err, test_muldiv_ints[n].err)
+            || (!err && !TEST_int_eq(r, real))) {
+        TEST_info("%d * %d / %d  r = %d  err = %d", a, b, c, r, err);
+        return 0;
+    }
+    return 1;
+}
+
+static const struct {
+    unsigned int a, b, c;
+    int err;
+} test_muldiv_uints[] = {
+    { 3, 1, 2,                          0 },
+    { 1, 3, 2,                          0 },
+    { UINT_MAX, UINT_MAX, UINT_MAX,     0 },
+    { UINT_MAX, 2, 4,                   0 },
+    { 8, UINT_MAX, 4,                   1 },
+    { UINT_MAX, 8, 4,                   1 },
+    { 3, 4, 0,                          1 },
+};
+
+static int test_uint_muldiv(int n)
+{
+    int err = 0;
+    unsigned int r, real = 0;
+    const unsigned int a = test_muldiv_uints[n].a;
+    const unsigned int b = test_muldiv_uints[n].b;
+    const unsigned int c = test_muldiv_uints[n].c;
+
+    r = safe_muldiv_uint(a, b, c, &err);
+    if (c != 0)
+        real = (unsigned int)((uint64_t)a * (uint64_t)b / (uint64_t)c);
+    if (!TEST_int_eq(err, test_muldiv_uints[n].err)
+            || (!err && !TEST_uint_eq(r, real))) {
+        TEST_info("%u * %u / %u  r = %u  err = %d", a, b, c, r, err);
+        return 0;
+    }
+    return 1;
+}
+
+int setup_tests(void)
+{
+    ADD_ALL_TESTS(test_int_ops, OSSL_NELEM(test_ints));
+    ADD_ALL_TESTS(test_uint_ops, OSSL_NELEM(test_uints));
+    ADD_ALL_TESTS(test_size_t_ops, OSSL_NELEM(test_size_ts));
+    ADD_ALL_TESTS(test_int_muldiv, OSSL_NELEM(test_muldiv_ints));
+    ADD_ALL_TESTS(test_uint_muldiv, OSSL_NELEM(test_muldiv_uints));
+    return 1;
+}