diff --git a/src/lib/utils/int_utils.h b/src/lib/utils/int_utils.h index 2c943f4d402..76e42a88190 100644 --- a/src/lib/utils/int_utils.h +++ b/src/lib/utils/int_utils.h @@ -17,7 +17,7 @@ namespace Botan { template constexpr inline std::optional checked_add(T a, T b) { const T r = a + b; - if(r - a != b) { + if(r < a || r < b) { return {}; } return r; diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp index d3917852fec..e82c384d12a 100644 --- a/src/tests/test_utils.cpp +++ b/src/tests/test_utils.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -38,6 +39,9 @@ class Utility_Function_Tests final : public Test { std::vector run() override { std::vector results; + results.push_back(test_checked_add()); + results.push_back(test_checked_mul()); + results.push_back(test_checked_cast()); results.push_back(test_round_up()); results.push_back(test_loadstore()); results.push_back(test_loadstore_fallback()); @@ -47,6 +51,82 @@ class Utility_Function_Tests final : public Test { } private: + Test::Result test_checked_add() { + Test::Result result("checked_add"); + + const size_t large = static_cast(-5); + const size_t zero = 0; + + for(int si = -15; si != 15; ++si) { + const size_t i = static_cast(si); + auto sum1 = Botan::checked_add(i, zero, zero, zero, large); + auto sum2 = Botan::checked_add(large, zero, zero, zero, i); + + result.confirm("checked_add looks at all args", sum1 == sum2); + + if(i < 5) { + result.test_eq("checked_add worked", sum1.value(), i + large); + } else { + result.confirm("checked_add did not return a result", !sum1.has_value()); + } + } + + auto& rng = Test::rng(); + + for(size_t i = 0; i != 100; ++i) { + const uint16_t x = Botan::make_uint16(rng.next_byte(), rng.next_byte()); + const uint16_t y = Botan::make_uint16(rng.next_byte(), rng.next_byte()); + + const uint32_t ref = static_cast(x) + y; + + if(auto z = Botan::checked_add(x, y)) { + result.test_int_eq("checked_add adds", z.value(), ref); + } else { + result.confirm("checked_add checks", (ref >> 16) > 0); + } + } + + return result; + } + + Test::Result test_checked_mul() { + Test::Result result("checked_mul"); + + auto& rng = Test::rng(); + + for(size_t i = 0; i != 100; ++i) { + const uint16_t x = Botan::make_uint16(rng.next_byte(), rng.next_byte()); + const uint16_t y = Botan::make_uint16(rng.next_byte(), rng.next_byte()); + + const uint32_t ref = static_cast(x) * y; + + if(auto z = Botan::checked_mul(x, y)) { + result.test_int_eq("checked_mul multiplies", z.value(), ref); + } else { + result.confirm("checked_mul checks", (ref >> 16) > 0); + } + } + + return result; + } + + Test::Result test_checked_cast() { + Test::Result result("checked_cast"); + + const uint32_t large = static_cast(-1); + const uint32_t is_16_bits = 0x8123; + const uint32_t is_8_bits = 0x89; + + result.confirm("checked_cast checks", !Botan::checked_cast(large).has_value()); + result.confirm("checked_cast checks", !Botan::checked_cast(large).has_value()); + + result.test_int_eq("checked_cast converts", Botan::checked_cast(large).value(), large); + result.test_int_eq("checked_cast converts", Botan::checked_cast(is_16_bits).value(), 0x8123); + result.test_int_eq("checked_cast converts", Botan::checked_cast(is_8_bits).value(), 0x89); + + return result; + } + Test::Result test_round_up() { Test::Result result("Util round_up");