strprintf.cpp raw

   1  // Copyright (c) 2020-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 <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  }
 126