Bitcoin Core  29.1.0
P2P Digital Currency
strprintf.cpp
Go to the documentation of this file.
1 // Copyright (c) 2020-2021 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 
6 #include <test/fuzz/fuzz.h>
7 #include <test/fuzz/util.h>
8 #include <tinyformat.h>
9 #include <util/strencodings.h>
10 #include <util/translation.h>
11 
12 #include <algorithm>
13 #include <cstdint>
14 #include <string>
15 #include <vector>
16 
17 template <typename... Args>
18 void fuzz_fmt(const std::string& fmt, const Args&... args)
19 {
20  (void)tfm::format(tfm::RuntimeFormat{fmt}, args...);
21 }
22 
23 FUZZ_TARGET(str_printf)
24 {
25  FuzzedDataProvider fuzzed_data_provider(buffer.data(), buffer.size());
26  const std::string format_string = fuzzed_data_provider.ConsumeRandomLengthString(64);
27 
28  const int digits_in_format_specifier = std::count_if(format_string.begin(), format_string.end(), IsDigit);
29 
30  // Avoid triggering the following crash bug:
31  // * strprintf("%987654321000000:", 1);
32  //
33  // Avoid triggering the following OOM bug:
34  // * strprintf("%.222222200000000$", 1.1);
35  //
36  // Upstream bug report: https://github.com/c42f/tinyformat/issues/70
37  if (format_string.find('%') != std::string::npos && digits_in_format_specifier >= 7) {
38  return;
39  }
40 
41  // Avoid triggering the following crash bug:
42  // * strprintf("%1$*1$*", -11111111);
43  //
44  // Upstream bug report: https://github.com/c42f/tinyformat/issues/70
45  if (format_string.find('%') != std::string::npos && format_string.find('$') != std::string::npos && format_string.find('*') != std::string::npos && digits_in_format_specifier > 0) {
46  return;
47  }
48 
49  // Avoid triggering the following crash bug:
50  // * strprintf("%.1s", (char*)nullptr);
51  //
52  // (void)strprintf(format_string, (char*)nullptr);
53  //
54  // Upstream bug report: https://github.com/c42f/tinyformat/issues/70
55 
56  try {
57  CallOneOf(
58  fuzzed_data_provider,
59  [&] {
60  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32));
61  },
62  [&] {
63  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeRandomLengthString(32).c_str());
64  },
65  [&] {
66  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeIntegral<signed char>());
67  },
68  [&] {
69  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeIntegral<unsigned char>());
70  },
71  [&] {
72  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeIntegral<char>());
73  },
74  [&] {
75  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeBool());
76  });
77  } catch (const tinyformat::format_error&) {
78  }
79 
80  if (format_string.find('%') != std::string::npos && format_string.find('c') != std::string::npos) {
81  // Avoid triggering the following:
82  // * strprintf("%c", 1.31783e+38);
83  // tinyformat.h:244:36: runtime error: 1.31783e+38 is outside the range of representable values of type 'char'
84  return;
85  }
86 
87  if (format_string.find('%') != std::string::npos && format_string.find('*') != std::string::npos) {
88  // Avoid triggering the following:
89  // * strprintf("%*", -2.33527e+38);
90  // tinyformat.h:283:65: runtime error: -2.33527e+38 is outside the range of representable values of type 'int'
91  // * strprintf("%*", -2147483648);
92  // tinyformat.h:763:25: runtime error: negation of -2147483648 cannot be represented in type 'int'; cast to an unsigned type to negate this value to itself
93  return;
94  }
95 
96  try {
97  CallOneOf(
98  fuzzed_data_provider,
99  [&] {
100  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeFloatingPoint<float>());
101  },
102  [&] {
103  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeFloatingPoint<double>());
104  },
105  [&] {
106  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeIntegral<int16_t>());
107  },
108  [&] {
109  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeIntegral<uint16_t>());
110  },
111  [&] {
112  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeIntegral<int32_t>());
113  },
114  [&] {
115  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeIntegral<uint32_t>());
116  },
117  [&] {
118  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeIntegral<int64_t>());
119  },
120  [&] {
121  fuzz_fmt(format_string, fuzzed_data_provider.ConsumeIntegral<uint64_t>());
122  });
123  } catch (const tinyformat::format_error&) {
124  }
125 }
constexpr bool IsDigit(char c)
Tests if the given character is a decimal digit.
Definition: strencodings.h:150
void format(std::ostream &out, FormatStringCheck< sizeof...(Args)> fmt, const Args &... args)
Format list of arguments to the stream according to given format string.
Definition: tinyformat.h:1079
ArgsManager & args
Definition: bitcoind.cpp:277
std::string ConsumeRandomLengthString(size_t max_length)
FUZZ_TARGET(str_printf)
Definition: strprintf.cpp:23
void fuzz_fmt(const std::string &fmt, const Args &... args)
Definition: strprintf.cpp:18
size_t CallOneOf(FuzzedDataProvider &fuzzed_data_provider, Callables... callables)
Definition: util.h:35