]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
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> | |
13 | void invoke_fmt(const uint8_t* Data, 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 + 1); | |
33 | std::memcpy(argnamebuffer.data(), Data, argsize); | |
34 | auto argname = argnamebuffer.data(); | |
35 | #else | |
36 | auto argname = fmt_fuzzer::as_chars(Data); | |
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 | |
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 | ||
108 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* Data, 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 | } |