1 | //===- FuzzerUtil.cpp - Misc utils ----------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // Misc utils. |
9 | //===----------------------------------------------------------------------===// |
10 | |
11 | #include "FuzzerUtil.h" |
12 | #include "FuzzerIO.h" |
13 | #include "FuzzerInternal.h" |
14 | #include <cassert> |
15 | #include <chrono> |
16 | #include <cstring> |
17 | #include <errno.h> |
18 | #include <mutex> |
19 | #include <signal.h> |
20 | #include <sstream> |
21 | #include <stdio.h> |
22 | #include <sys/types.h> |
23 | #include <thread> |
24 | |
25 | namespace fuzzer { |
26 | |
27 | void PrintHexArray(const uint8_t *Data, size_t Size, |
28 | const char *PrintAfter) { |
29 | for (size_t i = 0; i < Size; i++) |
30 | Printf("0x%x," , (unsigned)Data[i]); |
31 | Printf("%s" , PrintAfter); |
32 | } |
33 | |
34 | void Print(const Unit &v, const char *PrintAfter) { |
35 | PrintHexArray(v.data(), v.size(), PrintAfter); |
36 | } |
37 | |
38 | void PrintASCIIByte(uint8_t Byte) { |
39 | if (Byte == '\\') |
40 | Printf("\\\\" ); |
41 | else if (Byte == '"') |
42 | Printf("\\\"" ); |
43 | else if (Byte >= 32 && Byte < 127) |
44 | Printf("%c" , Byte); |
45 | else |
46 | Printf("\\%03o" , Byte); |
47 | } |
48 | |
49 | void PrintASCII(const uint8_t *Data, size_t Size, const char *PrintAfter) { |
50 | for (size_t i = 0; i < Size; i++) |
51 | PrintASCIIByte(Data[i]); |
52 | Printf("%s" , PrintAfter); |
53 | } |
54 | |
55 | void PrintASCII(const Unit &U, const char *PrintAfter) { |
56 | PrintASCII(U.data(), U.size(), PrintAfter); |
57 | } |
58 | |
59 | bool ToASCII(uint8_t *Data, size_t Size) { |
60 | bool Changed = false; |
61 | for (size_t i = 0; i < Size; i++) { |
62 | uint8_t &X = Data[i]; |
63 | auto NewX = X; |
64 | NewX &= 127; |
65 | if (!isspace(NewX) && !isprint(NewX)) |
66 | NewX = ' '; |
67 | Changed |= NewX != X; |
68 | X = NewX; |
69 | } |
70 | return Changed; |
71 | } |
72 | |
73 | bool IsASCII(const Unit &U) { return IsASCII(U.data(), U.size()); } |
74 | |
75 | bool IsASCII(const uint8_t *Data, size_t Size) { |
76 | for (size_t i = 0; i < Size; i++) |
77 | if (!(isprint(Data[i]) || isspace(Data[i]))) return false; |
78 | return true; |
79 | } |
80 | |
81 | bool ParseOneDictionaryEntry(const std::string &Str, Unit *U) { |
82 | U->clear(); |
83 | if (Str.empty()) return false; |
84 | size_t L = 0, R = Str.size() - 1; // We are parsing the range [L,R]. |
85 | // Skip spaces from both sides. |
86 | while (L < R && isspace(Str[L])) L++; |
87 | while (R > L && isspace(Str[R])) R--; |
88 | if (R - L < 2) return false; |
89 | // Check the closing " |
90 | if (Str[R] != '"') return false; |
91 | R--; |
92 | // Find the opening " |
93 | while (L < R && Str[L] != '"') L++; |
94 | if (L >= R) return false; |
95 | assert(Str[L] == '\"'); |
96 | L++; |
97 | assert(L <= R); |
98 | for (size_t Pos = L; Pos <= R; Pos++) { |
99 | uint8_t V = (uint8_t)Str[Pos]; |
100 | if (!isprint(V) && !isspace(V)) return false; |
101 | if (V =='\\') { |
102 | // Handle '\\' |
103 | if (Pos + 1 <= R && (Str[Pos + 1] == '\\' || Str[Pos + 1] == '"')) { |
104 | U->push_back(Str[Pos + 1]); |
105 | Pos++; |
106 | continue; |
107 | } |
108 | // Handle '\xAB' |
109 | if (Pos + 3 <= R && Str[Pos + 1] == 'x' |
110 | && isxdigit(Str[Pos + 2]) && isxdigit(Str[Pos + 3])) { |
111 | char Hex[] = "0xAA" ; |
112 | Hex[2] = Str[Pos + 2]; |
113 | Hex[3] = Str[Pos + 3]; |
114 | U->push_back(static_cast<uint8_t>(strtol(Hex, nullptr, 16))); |
115 | Pos += 3; |
116 | continue; |
117 | } |
118 | return false; // Invalid escape. |
119 | } else { |
120 | // Any other character. |
121 | U->push_back(V); |
122 | } |
123 | } |
124 | return true; |
125 | } |
126 | |
127 | bool ParseDictionaryFile(const std::string &Text, std::vector<Unit> *Units) { |
128 | if (Text.empty()) { |
129 | Printf("ParseDictionaryFile: file does not exist or is empty\n" ); |
130 | return false; |
131 | } |
132 | std::istringstream ISS(Text); |
133 | Units->clear(); |
134 | Unit U; |
135 | int LineNo = 0; |
136 | std::string S; |
137 | while (std::getline(ISS, S, '\n')) { |
138 | LineNo++; |
139 | size_t Pos = 0; |
140 | while (Pos < S.size() && isspace(S[Pos])) Pos++; // Skip spaces. |
141 | if (Pos == S.size()) continue; // Empty line. |
142 | if (S[Pos] == '#') continue; // Comment line. |
143 | if (ParseOneDictionaryEntry(S, &U)) { |
144 | Units->push_back(U); |
145 | } else { |
146 | Printf("ParseDictionaryFile: error in line %d\n\t\t%s\n" , LineNo, |
147 | S.c_str()); |
148 | return false; |
149 | } |
150 | } |
151 | return true; |
152 | } |
153 | |
154 | // Code duplicated (and tested) in llvm/include/llvm/Support/Base64.h |
155 | std::string Base64(const Unit &U) { |
156 | static const char Table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" |
157 | "abcdefghijklmnopqrstuvwxyz" |
158 | "0123456789+/" ; |
159 | std::string Buffer; |
160 | Buffer.resize(((U.size() + 2) / 3) * 4); |
161 | |
162 | size_t i = 0, j = 0; |
163 | for (size_t n = U.size() / 3 * 3; i < n; i += 3, j += 4) { |
164 | uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8) | |
165 | (unsigned char)U[i + 2]; |
166 | Buffer[j + 0] = Table[(x >> 18) & 63]; |
167 | Buffer[j + 1] = Table[(x >> 12) & 63]; |
168 | Buffer[j + 2] = Table[(x >> 6) & 63]; |
169 | Buffer[j + 3] = Table[x & 63]; |
170 | } |
171 | if (i + 1 == U.size()) { |
172 | uint32_t x = ((unsigned char)U[i] << 16); |
173 | Buffer[j + 0] = Table[(x >> 18) & 63]; |
174 | Buffer[j + 1] = Table[(x >> 12) & 63]; |
175 | Buffer[j + 2] = '='; |
176 | Buffer[j + 3] = '='; |
177 | } else if (i + 2 == U.size()) { |
178 | uint32_t x = ((unsigned char)U[i] << 16) | ((unsigned char)U[i + 1] << 8); |
179 | Buffer[j + 0] = Table[(x >> 18) & 63]; |
180 | Buffer[j + 1] = Table[(x >> 12) & 63]; |
181 | Buffer[j + 2] = Table[(x >> 6) & 63]; |
182 | Buffer[j + 3] = '='; |
183 | } |
184 | return Buffer; |
185 | } |
186 | |
187 | static std::mutex SymbolizeMutex; |
188 | |
189 | std::string DescribePC(const char *SymbolizedFMT, uintptr_t PC) { |
190 | std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock); |
191 | if (!EF->__sanitizer_symbolize_pc || !l.owns_lock()) |
192 | return "<can not symbolize>" ; |
193 | char PcDescr[1024] = {}; |
194 | EF->__sanitizer_symbolize_pc(reinterpret_cast<void*>(PC), |
195 | SymbolizedFMT, PcDescr, sizeof(PcDescr)); |
196 | PcDescr[sizeof(PcDescr) - 1] = 0; // Just in case. |
197 | return PcDescr; |
198 | } |
199 | |
200 | void PrintPC(const char *SymbolizedFMT, const char *FallbackFMT, uintptr_t PC) { |
201 | if (EF->__sanitizer_symbolize_pc) |
202 | Printf("%s" , DescribePC(SymbolizedFMT, PC).c_str()); |
203 | else |
204 | Printf(FallbackFMT, PC); |
205 | } |
206 | |
207 | void PrintStackTrace() { |
208 | std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock); |
209 | if (EF->__sanitizer_print_stack_trace && l.owns_lock()) |
210 | EF->__sanitizer_print_stack_trace(); |
211 | } |
212 | |
213 | void PrintMemoryProfile() { |
214 | std::unique_lock<std::mutex> l(SymbolizeMutex, std::try_to_lock); |
215 | if (EF->__sanitizer_print_memory_profile && l.owns_lock()) |
216 | EF->__sanitizer_print_memory_profile(95, 8); |
217 | } |
218 | |
219 | unsigned NumberOfCpuCores() { |
220 | unsigned N = std::thread::hardware_concurrency(); |
221 | if (!N) { |
222 | Printf("WARNING: std::thread::hardware_concurrency not well defined for " |
223 | "your platform. Assuming CPU count of 1.\n" ); |
224 | N = 1; |
225 | } |
226 | return N; |
227 | } |
228 | |
229 | uint64_t SimpleFastHash(const void *Data, size_t Size, uint64_t Initial) { |
230 | uint64_t Res = Initial; |
231 | const uint8_t *Bytes = static_cast<const uint8_t *>(Data); |
232 | for (size_t i = 0; i < Size; i++) |
233 | Res = Res * 11 + Bytes[i]; |
234 | return Res; |
235 | } |
236 | |
237 | } // namespace fuzzer |
238 | |