• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2019, Paul Dreik
2 // License: see LICENSE.rst in the fmt root directory
3 
4 #include <fmt/chrono.h>
5 #include <fmt/core.h>
6 #include <cstdint>
7 #include <stdexcept>
8 #include <type_traits>
9 #include <vector>
10 #include "fuzzer_common.h"
11 
12 template <typename Item1>
invoke_fmt(const uint8_t * Data,std::size_t Size,unsigned int argsize)13 void invoke_fmt(const uint8_t* Data, std::size_t Size, unsigned int argsize) {
14   constexpr auto N1 = sizeof(Item1);
15   static_assert(N1 <= fmt_fuzzer::Nfixed, "Nfixed too small");
16   if (Size <= fmt_fuzzer::Nfixed) {
17     return;
18   }
19   const Item1 item1 = fmt_fuzzer::assignFromBuf<Item1>(Data);
20 
21   Data += fmt_fuzzer::Nfixed;
22   Size -= fmt_fuzzer::Nfixed;
23 
24   // how many chars should be used for the argument name?
25   if (argsize <= 0 || argsize >= Size) {
26     return;
27   }
28 
29   // allocating buffers separately is slower, but increases chances
30   // of detecting memory errors
31 #if FMT_FUZZ_SEPARATE_ALLOCATION
32   std::vector<char> argnamebuffer(argsize);
33   std::memcpy(argnamebuffer.data(), Data, argsize);
34   auto argname = fmt::string_view(argnamebuffer.data(), argsize);
35 #else
36   auto argname = fmt::string_view(fmt_fuzzer::as_chars(Data), argsize);
37 #endif
38   Data += argsize;
39   Size -= argsize;
40 
41 #if FMT_FUZZ_SEPARATE_ALLOCATION
42   // allocates as tight as possible, making it easier to catch buffer overruns.
43   std::vector<char> fmtstringbuffer(Size);
44   std::memcpy(fmtstringbuffer.data(), Data, Size);
45   auto fmtstring = fmt::string_view(fmtstringbuffer.data(), Size);
46 #else
47   auto fmtstring = fmt::string_view(fmt_fuzzer::as_chars(Data), Size);
48 #endif
49 
50 #if FMT_FUZZ_FORMAT_TO_STRING
51   std::string message = fmt::format(fmtstring, fmt::arg(argname, item1));
52 #else
53   fmt::memory_buffer outbuf;
54   fmt::format_to(outbuf, fmtstring, fmt::arg(argname, item1));
55 #endif
56 }
57 
58 // for dynamic dispatching to an explicit instantiation
invoke(int index,Callback callback)59 template <typename Callback> void invoke(int index, Callback callback) {
60   switch (index) {
61   case 0:
62     callback(bool{});
63     break;
64   case 1:
65     callback(char{});
66     break;
67   case 2:
68     using sc = signed char;
69     callback(sc{});
70     break;
71   case 3:
72     using uc = unsigned char;
73     callback(uc{});
74     break;
75   case 4:
76     callback(short{});
77     break;
78   case 5:
79     using us = unsigned short;
80     callback(us{});
81     break;
82   case 6:
83     callback(int{});
84     break;
85   case 7:
86     callback(unsigned{});
87     break;
88   case 8:
89     callback(long{});
90     break;
91   case 9:
92     using ul = unsigned long;
93     callback(ul{});
94     break;
95   case 10:
96     callback(float{});
97     break;
98   case 11:
99     callback(double{});
100     break;
101   case 12:
102     using LD = long double;
103     callback(LD{});
104     break;
105   }
106 }
107 
LLVMFuzzerTestOneInput(const uint8_t * Data,std::size_t Size)108 extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, std::size_t Size) {
109   if (Size <= 3) {
110     return 0;
111   }
112 
113   // switch types depending on the first byte of the input
114   const auto first = Data[0] & 0x0F;
115   const unsigned int second = (Data[0] & 0xF0) >> 4;
116   Data++;
117   Size--;
118 
119   auto outerfcn = [=](auto param1) {
120     invoke_fmt<decltype(param1)>(Data, Size, second);
121   };
122 
123   try {
124     invoke(first, outerfcn);
125   } catch (std::exception& /*e*/) {
126   }
127   return 0;
128 }
129