Skip to content

Commit 6fb4bbf

Browse files
author
MarcoFalke
committed
Merge #18176: tests: Add fuzzing harness for CScript and CScriptNum operations
e37f536 Make lifetime correctness easier to see (avoid reference lifetime extension) (practicalswift) e7ddbd9 tests: Add fuzzing harness for CScriptNum operations (practicalswift) 65a52a0 tests: Add fuzzing harness for CScript operations (practicalswift) eb7c50c tests: Add common Consume* fuzzing functions (practicalswift) Pull request description: Add fuzzing harness for `CScript` and `CScriptNum` operations. Test this PR using: ``` $ make distclean $ ./autogen.sh $ CC=clang CXX=clang++ ./configure --enable-fuzz \ --with-sanitizers=address,fuzzer,undefined $ make $ src/test/fuzz/script_ops … $ src/test/fuzz/scriptnum_ops … ``` ACKs for top commit: MarcoFalke: ACK e37f536 🦂 Tree-SHA512: 5165d918ffe3f1e3e85ab0e61d8b05934f682d324cf63ce188da5890899df2b5727aba9ed10c0437260ecff8055250e60c79d81d764bc740a7652d543a7c5fa3
2 parents ccb2c9e + e37f536 commit 6fb4bbf

File tree

6 files changed

+239
-3
lines changed

6 files changed

+239
-3
lines changed

src/Makefile.test.include

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ FUZZ_TARGETS = \
6161
test/fuzz/script \
6262
test/fuzz/script_deserialize \
6363
test/fuzz/script_flags \
64+
test/fuzz/script_ops \
65+
test/fuzz/scriptnum_ops \
6466
test/fuzz/service_deserialize \
6567
test/fuzz/spanparsing \
6668
test/fuzz/strprintf \
@@ -590,6 +592,18 @@ test_fuzz_script_flags_LDADD = $(FUZZ_SUITE_LD_COMMON)
590592
test_fuzz_script_flags_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
591593
test_fuzz_script_flags_SOURCES = $(FUZZ_SUITE) test/fuzz/script_flags.cpp
592594

595+
test_fuzz_script_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
596+
test_fuzz_script_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
597+
test_fuzz_script_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
598+
test_fuzz_script_ops_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
599+
test_fuzz_script_ops_SOURCES = $(FUZZ_SUITE) test/fuzz/script_ops.cpp
600+
601+
test_fuzz_scriptnum_ops_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES)
602+
test_fuzz_scriptnum_ops_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
603+
test_fuzz_scriptnum_ops_LDADD = $(FUZZ_SUITE_LD_COMMON)
604+
test_fuzz_scriptnum_ops_LDFLAGS = $(RELDFLAGS) $(AM_LDFLAGS) $(LIBTOOL_APP_LDFLAGS)
605+
test_fuzz_scriptnum_ops_SOURCES = $(FUZZ_SUITE) test/fuzz/scriptnum_ops.cpp
606+
593607
test_fuzz_service_deserialize_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) -DSERVICE_DESERIALIZE=1
594608
test_fuzz_service_deserialize_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS)
595609
test_fuzz_service_deserialize_LDADD = $(FUZZ_SUITE_LD_COMMON)

src/test/fuzz/bloom_filter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
2727
while (fuzzed_data_provider.remaining_bytes() > 0) {
2828
switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 6)) {
2929
case 0: {
30-
const std::vector<unsigned char>& b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
30+
const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
3131
(void)bloom_filter.contains(b);
3232
bloom_filter.insert(b);
3333
const bool present = bloom_filter.contains(b);

src/test/fuzz/rolling_bloom_filter.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
2424
while (fuzzed_data_provider.remaining_bytes() > 0) {
2525
switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 2)) {
2626
case 0: {
27-
const std::vector<unsigned char>& b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
27+
const std::vector<unsigned char> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
2828
(void)rolling_bloom_filter.contains(b);
2929
rolling_bloom_filter.insert(b);
3030
const bool present = rolling_bloom_filter.contains(b);

src/test/fuzz/script_ops.cpp

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright (c) 2020 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 <script/script.h>
6+
#include <test/fuzz/FuzzedDataProvider.h>
7+
#include <test/fuzz/fuzz.h>
8+
#include <test/fuzz/util.h>
9+
10+
#include <cstdint>
11+
#include <string>
12+
#include <vector>
13+
14+
void test_one_input(const std::vector<uint8_t>& buffer)
15+
{
16+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
17+
CScript script = ConsumeScript(fuzzed_data_provider);
18+
while (fuzzed_data_provider.remaining_bytes() > 0) {
19+
switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 7)) {
20+
case 0:
21+
script += ConsumeScript(fuzzed_data_provider);
22+
break;
23+
case 1:
24+
script = script + ConsumeScript(fuzzed_data_provider);
25+
break;
26+
case 2:
27+
script << fuzzed_data_provider.ConsumeIntegral<int64_t>();
28+
break;
29+
case 3:
30+
script << ConsumeOpcodeType(fuzzed_data_provider);
31+
break;
32+
case 4:
33+
script << ConsumeScriptNum(fuzzed_data_provider);
34+
break;
35+
case 5:
36+
script << ConsumeRandomLengthByteVector(fuzzed_data_provider);
37+
break;
38+
case 6:
39+
script.clear();
40+
break;
41+
case 7: {
42+
(void)script.GetSigOpCount(false);
43+
(void)script.GetSigOpCount(true);
44+
(void)script.GetSigOpCount(script);
45+
(void)script.HasValidOps();
46+
(void)script.IsPayToScriptHash();
47+
(void)script.IsPayToWitnessScriptHash();
48+
(void)script.IsPushOnly();
49+
(void)script.IsUnspendable();
50+
{
51+
CScript::const_iterator pc = script.begin();
52+
opcodetype opcode;
53+
(void)script.GetOp(pc, opcode);
54+
std::vector<uint8_t> data;
55+
(void)script.GetOp(pc, opcode, data);
56+
(void)script.IsPushOnly(pc);
57+
}
58+
{
59+
int version;
60+
std::vector<uint8_t> program;
61+
(void)script.IsWitnessProgram(version, program);
62+
}
63+
break;
64+
}
65+
}
66+
}
67+
}

src/test/fuzz/scriptnum_ops.cpp

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// Copyright (c) 2020 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 <script/script.h>
6+
#include <test/fuzz/FuzzedDataProvider.h>
7+
#include <test/fuzz/fuzz.h>
8+
#include <test/fuzz/util.h>
9+
10+
#include <cassert>
11+
#include <cstdint>
12+
#include <limits>
13+
#include <vector>
14+
15+
namespace {
16+
bool IsValidAddition(const CScriptNum& lhs, const CScriptNum& rhs)
17+
{
18+
return rhs == 0 || (rhs > 0 && lhs <= CScriptNum{std::numeric_limits<int64_t>::max()} - rhs) || (rhs < 0 && lhs >= CScriptNum{std::numeric_limits<int64_t>::min()} - rhs);
19+
}
20+
21+
bool IsValidSubtraction(const CScriptNum& lhs, const CScriptNum& rhs)
22+
{
23+
return rhs == 0 || (rhs > 0 && lhs >= CScriptNum{std::numeric_limits<int64_t>::min()} + rhs) || (rhs < 0 && lhs <= CScriptNum{std::numeric_limits<int64_t>::max()} + rhs);
24+
}
25+
} // namespace
26+
27+
void test_one_input(const std::vector<uint8_t>& buffer)
28+
{
29+
FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
30+
CScriptNum script_num = ConsumeScriptNum(fuzzed_data_provider);
31+
while (fuzzed_data_provider.remaining_bytes() > 0) {
32+
switch (fuzzed_data_provider.ConsumeIntegralInRange(0, 11)) {
33+
case 0: {
34+
const int64_t i = fuzzed_data_provider.ConsumeIntegral<int64_t>();
35+
assert((script_num == i) != (script_num != i));
36+
assert((script_num <= i) != script_num > i);
37+
assert((script_num >= i) != (script_num < i));
38+
// Avoid signed integer overflow:
39+
// script/script.h:264:93: runtime error: signed integer overflow: -2261405121394637306 + -9223372036854775802 cannot be represented in type 'long'
40+
if (IsValidAddition(script_num, CScriptNum{i})) {
41+
assert((script_num + i) - i == script_num);
42+
}
43+
// Avoid signed integer overflow:
44+
// script/script.h:265:93: runtime error: signed integer overflow: 9223371895120855039 - -9223372036854710486 cannot be represented in type 'long'
45+
if (IsValidSubtraction(script_num, CScriptNum{i})) {
46+
assert((script_num - i) + i == script_num);
47+
}
48+
break;
49+
}
50+
case 1: {
51+
const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
52+
assert((script_num == random_script_num) != (script_num != random_script_num));
53+
assert((script_num <= random_script_num) != (script_num > random_script_num));
54+
assert((script_num >= random_script_num) != (script_num < random_script_num));
55+
// Avoid signed integer overflow:
56+
// script/script.h:264:93: runtime error: signed integer overflow: -9223126527765971126 + -9223372036854756825 cannot be represented in type 'long'
57+
if (IsValidAddition(script_num, random_script_num)) {
58+
assert((script_num + random_script_num) - random_script_num == script_num);
59+
}
60+
// Avoid signed integer overflow:
61+
// script/script.h:265:93: runtime error: signed integer overflow: 6052837899185946624 - -9223372036854775808 cannot be represented in type 'long'
62+
if (IsValidSubtraction(script_num, random_script_num)) {
63+
assert((script_num - random_script_num) + random_script_num == script_num);
64+
}
65+
break;
66+
}
67+
case 2: {
68+
const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
69+
if (!IsValidAddition(script_num, random_script_num)) {
70+
// Avoid assertion failure:
71+
// ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed.
72+
break;
73+
}
74+
script_num += random_script_num;
75+
break;
76+
}
77+
case 3: {
78+
const CScriptNum random_script_num = ConsumeScriptNum(fuzzed_data_provider);
79+
if (!IsValidSubtraction(script_num, random_script_num)) {
80+
// Avoid assertion failure:
81+
// ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed.
82+
break;
83+
}
84+
script_num -= random_script_num;
85+
break;
86+
}
87+
case 4:
88+
script_num = script_num & fuzzed_data_provider.ConsumeIntegral<int64_t>();
89+
break;
90+
case 5:
91+
script_num = script_num & ConsumeScriptNum(fuzzed_data_provider);
92+
break;
93+
case 6:
94+
script_num &= ConsumeScriptNum(fuzzed_data_provider);
95+
break;
96+
case 7:
97+
if (script_num == CScriptNum{std::numeric_limits<int64_t>::min()}) {
98+
// Avoid assertion failure:
99+
// ./script/script.h:279: CScriptNum CScriptNum::operator-() const: Assertion `m_value != std::numeric_limits<int64_t>::min()' failed.
100+
break;
101+
}
102+
script_num = -script_num;
103+
break;
104+
case 8:
105+
script_num = fuzzed_data_provider.ConsumeIntegral<int64_t>();
106+
break;
107+
case 9: {
108+
const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>();
109+
if (!IsValidAddition(script_num, CScriptNum{random_integer})) {
110+
// Avoid assertion failure:
111+
// ./script/script.h:292: CScriptNum &CScriptNum::operator+=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value <= std::numeric_limits<int64_t>::max() - rhs) || (rhs < 0 && m_value >= std::numeric_limits<int64_t>::min() - rhs)' failed.
112+
break;
113+
}
114+
script_num += random_integer;
115+
break;
116+
}
117+
case 10: {
118+
const int64_t random_integer = fuzzed_data_provider.ConsumeIntegral<int64_t>();
119+
if (!IsValidSubtraction(script_num, CScriptNum{random_integer})) {
120+
// Avoid assertion failure:
121+
// ./script/script.h:300: CScriptNum &CScriptNum::operator-=(const int64_t &): Assertion `rhs == 0 || (rhs > 0 && m_value >= std::numeric_limits<int64_t>::min() + rhs) || (rhs < 0 && m_value <= std::numeric_limits<int64_t>::max() + rhs)' failed.
122+
break;
123+
}
124+
script_num -= random_integer;
125+
break;
126+
}
127+
case 11:
128+
script_num &= fuzzed_data_provider.ConsumeIntegral<int64_t>();
129+
break;
130+
}
131+
// Avoid negation failure:
132+
// script/script.h:332:35: runtime error: negation of -9223372036854775808 cannot be represented in type 'int64_t' (aka 'long'); cast to an unsigned type to negate this value to itself
133+
if (script_num != CScriptNum{std::numeric_limits<int64_t>::min()}) {
134+
(void)script_num.getvch();
135+
}
136+
}
137+
}

src/test/fuzz/util.h

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,11 @@
77

88
#include <attributes.h>
99
#include <optional.h>
10+
#include <script/script.h>
1011
#include <serialize.h>
1112
#include <streams.h>
1213
#include <test/fuzz/FuzzedDataProvider.h>
14+
#include <test/fuzz/fuzz.h>
1315
#include <version.h>
1416

1517
#include <cstdint>
@@ -25,7 +27,7 @@ NODISCARD inline std::vector<uint8_t> ConsumeRandomLengthByteVector(FuzzedDataPr
2527
template <typename T>
2628
NODISCARD inline Optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_data_provider, size_t max_length = 4096) noexcept
2729
{
28-
const std::vector<uint8_t>& buffer = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
30+
const std::vector<uint8_t> buffer = ConsumeRandomLengthByteVector(fuzzed_data_provider, max_length);
2931
CDataStream ds{buffer, SER_NETWORK, INIT_PROTO_VERSION};
3032
T obj;
3133
try {
@@ -36,4 +38,20 @@ NODISCARD inline Optional<T> ConsumeDeserializable(FuzzedDataProvider& fuzzed_da
3638
return obj;
3739
}
3840

41+
NODISCARD inline opcodetype ConsumeOpcodeType(FuzzedDataProvider& fuzzed_data_provider) noexcept
42+
{
43+
return static_cast<opcodetype>(fuzzed_data_provider.ConsumeIntegralInRange<uint32_t>(0, MAX_OPCODE));
44+
}
45+
46+
NODISCARD inline CScript ConsumeScript(FuzzedDataProvider& fuzzed_data_provider) noexcept
47+
{
48+
const std::vector<uint8_t> b = ConsumeRandomLengthByteVector(fuzzed_data_provider);
49+
return {b.begin(), b.end()};
50+
}
51+
52+
NODISCARD inline CScriptNum ConsumeScriptNum(FuzzedDataProvider& fuzzed_data_provider) noexcept
53+
{
54+
return CScriptNum{fuzzed_data_provider.ConsumeIntegral<int64_t>()};
55+
}
56+
3957
#endif // BITCOIN_TEST_FUZZ_UTIL_H

0 commit comments

Comments
 (0)