Skip to content

Commit d5e2c4a

Browse files
fuzz: Add fuzz test for checked and saturating add and left shift
Co-authored-by: Ryan Ofsky <[email protected]>
1 parent c03a279 commit d5e2c4a

File tree

2 files changed

+51
-0
lines changed

2 files changed

+51
-0
lines changed

src/test/fuzz/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ add_executable(fuzz
7171
netaddress.cpp
7272
netbase_dns_lookup.cpp
7373
node_eviction.cpp
74+
overflow.cpp
7475
p2p_handshake.cpp
7576
p2p_headers_presync.cpp
7677
p2p_transport_serialization.cpp

src/test/fuzz/overflow.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright (c) 2025-present The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <test/fuzz/FuzzedDataProvider.h>
6+
#include <test/fuzz/fuzz.h>
7+
#include <util/check.h>
8+
#include <util/overflow.h>
9+
10+
#include <algorithm>
11+
#include <limits>
12+
#include <optional>
13+
14+
namespace {
15+
//! Test overflow operations for type T using a wider type, W, to verify results.
16+
template <typename T, typename W>
17+
void TestOverflow(FuzzedDataProvider& fuzzed_data_provider)
18+
{
19+
constexpr auto min{std::numeric_limits<T>::min()};
20+
constexpr auto max{std::numeric_limits<T>::max()};
21+
// Range needs to be at least twice as big to allow two numbers to be added without overflowing.
22+
static_assert(min >= std::numeric_limits<W>::min() / 2);
23+
static_assert(max <= std::numeric_limits<W>::max() / 2);
24+
25+
auto widen = [](T value) -> W { return value; };
26+
auto clamp = [](W value) -> W { return std::clamp<W>(value, min, max); };
27+
auto check = [](W value) -> std::optional<W> { if (value >= min && value <= max) return value; else return std::nullopt; };
28+
29+
const T i = fuzzed_data_provider.ConsumeIntegral<T>();
30+
const T j = fuzzed_data_provider.ConsumeIntegral<T>();
31+
const unsigned shift = fuzzed_data_provider.ConsumeIntegralInRange<unsigned>(0, std::numeric_limits<W>::digits - std::numeric_limits<T>::digits);
32+
33+
Assert(clamp(widen(i) + widen(j)) == SaturatingAdd(i, j));
34+
Assert(check(widen(i) + widen(j)) == CheckedAdd(i, j));
35+
36+
Assert(clamp(widen(i) << shift) == SaturatingLeftShift(i, shift));
37+
Assert(check(widen(i) << shift) == CheckedLeftShift(i, shift));
38+
}
39+
} // namespace
40+
41+
FUZZ_TARGET(overflow)
42+
{
43+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
44+
TestOverflow<int8_t, int64_t>(fuzzed_data_provider);
45+
TestOverflow<int16_t, int64_t>(fuzzed_data_provider);
46+
TestOverflow<int32_t, int64_t>(fuzzed_data_provider);
47+
TestOverflow<uint8_t, uint64_t>(fuzzed_data_provider);
48+
TestOverflow<uint16_t, uint64_t>(fuzzed_data_provider);
49+
TestOverflow<uint32_t, uint64_t>(fuzzed_data_provider);
50+
}

0 commit comments

Comments
 (0)