/src/mozilla-central/tools/fuzzing/faulty/Faulty.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | #include <cerrno> |
8 | | #include <climits> |
9 | | #include <cmath> |
10 | | #include <fstream> |
11 | | #include <prinrval.h> |
12 | | #include <unistd.h> |
13 | | #include "base/string_util.h" |
14 | | #include "FuzzingMutate.h" |
15 | | #include "FuzzingTraits.h" |
16 | | #include "chrome/common/ipc_channel.h" |
17 | | #include "chrome/common/ipc_message.h" |
18 | | #include "chrome/common/file_descriptor_set_posix.h" |
19 | | #include "mozilla/ipc/Faulty.h" |
20 | | #include "mozilla/TypeTraits.h" |
21 | | #include "nsNetCID.h" |
22 | | #include "nsIEventTarget.h" |
23 | | #include "nsIFile.h" |
24 | | #include "nsIFileStreams.h" |
25 | | #include "nsILineInputStream.h" |
26 | | #include "nsIRunnable.h" |
27 | | #include "nsThreadUtils.h" |
28 | | #include "nsLocalFile.h" |
29 | | #include "nsNetCID.h" |
30 | | #include "nsPrintfCString.h" |
31 | | #include "nsTArray.h" |
32 | | #include "nsXULAppAPI.h" |
33 | | #include "prenv.h" |
34 | | |
35 | | |
36 | | namespace mozilla { |
37 | | namespace ipc { |
38 | | |
39 | | using namespace mozilla::fuzzing; |
40 | | |
41 | | const unsigned int Faulty::sDefaultProbability = Faulty::DefaultProbability(); |
42 | | const bool Faulty::sIsLoggingEnabled = Faulty::Logging(); |
43 | | |
44 | | /** |
45 | | * RandomNumericValue generates negative and positive integrals. |
46 | | */ |
47 | | template <typename T> |
48 | | T RandomIntegral() |
49 | 0 | { |
50 | 0 | static_assert(mozilla::IsIntegral<T>::value == true, |
51 | 0 | "T must be an integral type"); |
52 | 0 | double r = static_cast<double>(random() % ((sizeof(T) * CHAR_BIT) + 1)); |
53 | 0 | T x = static_cast<T>(pow(2.0, r)) - 1; |
54 | 0 | if (std::numeric_limits<T>::is_signed && random() % 2 == 0) { |
55 | 0 | return (x * -1) - 1; |
56 | 0 | } |
57 | 0 | return x; |
58 | 0 | } Unexecuted instantiation: char mozilla::ipc::RandomIntegral<char>() Unexecuted instantiation: unsigned char mozilla::ipc::RandomIntegral<unsigned char>() Unexecuted instantiation: short mozilla::ipc::RandomIntegral<short>() Unexecuted instantiation: unsigned short mozilla::ipc::RandomIntegral<unsigned short>() Unexecuted instantiation: int mozilla::ipc::RandomIntegral<int>() Unexecuted instantiation: unsigned int mozilla::ipc::RandomIntegral<unsigned int>() Unexecuted instantiation: long mozilla::ipc::RandomIntegral<long>() Unexecuted instantiation: unsigned long mozilla::ipc::RandomIntegral<unsigned long>() |
59 | | |
60 | | /** |
61 | | * RandomNumericLimit returns either the min or max limit of an arithmetic |
62 | | * data type. |
63 | | */ |
64 | | template <typename T> |
65 | 0 | T RandomNumericLimit() { |
66 | 0 | static_assert(mozilla::IsArithmetic<T>::value == true, |
67 | 0 | "T must be an arithmetic type"); |
68 | 0 | return random() % 2 == 0 ? std::numeric_limits<T>::min() |
69 | 0 | : std::numeric_limits<T>::max(); |
70 | 0 | } Unexecuted instantiation: char mozilla::ipc::RandomNumericLimit<char>() Unexecuted instantiation: unsigned char mozilla::ipc::RandomNumericLimit<unsigned char>() Unexecuted instantiation: short mozilla::ipc::RandomNumericLimit<short>() Unexecuted instantiation: unsigned short mozilla::ipc::RandomNumericLimit<unsigned short>() Unexecuted instantiation: int mozilla::ipc::RandomNumericLimit<int>() Unexecuted instantiation: unsigned int mozilla::ipc::RandomNumericLimit<unsigned int>() Unexecuted instantiation: long mozilla::ipc::RandomNumericLimit<long>() Unexecuted instantiation: unsigned long mozilla::ipc::RandomNumericLimit<unsigned long>() Unexecuted instantiation: double mozilla::ipc::RandomNumericLimit<double>() Unexecuted instantiation: float mozilla::ipc::RandomNumericLimit<float>() |
71 | | |
72 | | /** |
73 | | * RandomIntegerRange returns a random integral within a user defined range. |
74 | | */ |
75 | | template <typename T> |
76 | | T RandomIntegerRange(T min, T max) |
77 | 0 | { |
78 | 0 | static_assert(mozilla::IsIntegral<T>::value == true, |
79 | 0 | "T must be an integral type"); |
80 | 0 | MOZ_ASSERT(min < max); |
81 | 0 | return static_cast<T>((random() % (max - min + 1)) + min); |
82 | 0 | } Unexecuted instantiation: char mozilla::ipc::RandomIntegerRange<char>(char, char) Unexecuted instantiation: short mozilla::ipc::RandomIntegerRange<short>(short, short) Unexecuted instantiation: unsigned short mozilla::ipc::RandomIntegerRange<unsigned short>(unsigned short, unsigned short) Unexecuted instantiation: int mozilla::ipc::RandomIntegerRange<int>(int, int) Unexecuted instantiation: long mozilla::ipc::RandomIntegerRange<long>(long, long) Unexecuted instantiation: unsigned long mozilla::ipc::RandomIntegerRange<unsigned long>(unsigned long, unsigned long) Unexecuted instantiation: unsigned int mozilla::ipc::RandomIntegerRange<unsigned int>(unsigned int, unsigned int) Unexecuted instantiation: unsigned char mozilla::ipc::RandomIntegerRange<unsigned char>(unsigned char, unsigned char) |
83 | | |
84 | | /** |
85 | | * RandomFloatingPointRange returns a random floating-point number within a |
86 | | * user defined range. |
87 | | */ |
88 | | template <typename T> |
89 | | T RandomFloatingPointRange(T min, T max) |
90 | 0 | { |
91 | 0 | static_assert(mozilla::IsFloatingPoint<T>::value == true, |
92 | 0 | "T must be a floating point type"); |
93 | 0 | MOZ_ASSERT(min < max); |
94 | 0 | T x = static_cast<T>(random()) / static_cast<T>(RAND_MAX); |
95 | 0 | return min + x * (max - min); |
96 | 0 | } Unexecuted instantiation: double mozilla::ipc::RandomFloatingPointRange<double>(double, double) Unexecuted instantiation: float mozilla::ipc::RandomFloatingPointRange<float>(float, float) |
97 | | |
98 | | /** |
99 | | * RandomFloatingPoint returns a random floating-point number. |
100 | | */ |
101 | | template <typename T> |
102 | | T RandomFloatingPoint() |
103 | 0 | { |
104 | 0 | static_assert(mozilla::IsFloatingPoint<T>::value == true, |
105 | 0 | "T must be a floating point type"); |
106 | 0 | int radix = RandomIntegerRange<int>(std::numeric_limits<T>::min_exponent, |
107 | 0 | std::numeric_limits<T>::max_exponent); |
108 | 0 | T x = static_cast<T>(pow(2.0, static_cast<double>(radix))); |
109 | 0 | return x * RandomFloatingPointRange<T>(0.0, 10.0); |
110 | 0 | } Unexecuted instantiation: double mozilla::ipc::RandomFloatingPoint<double>() Unexecuted instantiation: float mozilla::ipc::RandomFloatingPoint<float>() |
111 | | |
112 | | /** |
113 | | * FuzzIntegralType mutates an incercepted integral type of a pickled message. |
114 | | */ |
115 | | template <typename T> |
116 | | void FuzzIntegralType(T* v, bool largeValues) |
117 | 0 | { |
118 | 0 | static_assert(mozilla::IsIntegral<T>::value == true, |
119 | 0 | "T must be an integral type"); |
120 | 0 | switch (random() % 6) { |
121 | 0 | case 0: |
122 | 0 | if (largeValues) { |
123 | 0 | (*v) = RandomIntegral<T>(); |
124 | 0 | break; |
125 | 0 | } |
126 | 0 | MOZ_FALLTHROUGH; |
127 | 0 | case 1: |
128 | 0 | if (largeValues) { |
129 | 0 | (*v) = RandomNumericLimit<T>(); |
130 | 0 | break; |
131 | 0 | } |
132 | 0 | MOZ_FALLTHROUGH; |
133 | 0 | case 2: |
134 | 0 | if (largeValues) { |
135 | 0 | (*v) = RandomIntegerRange<T>(std::numeric_limits<T>::min(), |
136 | 0 | std::numeric_limits<T>::max()); |
137 | 0 | break; |
138 | 0 | } |
139 | 0 | MOZ_FALLTHROUGH; |
140 | 0 | default: |
141 | 0 | switch(random() % 2) { |
142 | 0 | case 0: |
143 | 0 | // Prevent underflow |
144 | 0 | if (*v != std::numeric_limits<T>::min()) { |
145 | 0 | (*v)--; |
146 | 0 | break; |
147 | 0 | } |
148 | 0 | MOZ_FALLTHROUGH; |
149 | 0 | case 1: |
150 | 0 | // Prevent overflow |
151 | 0 | if (*v != std::numeric_limits<T>::max()) { |
152 | 0 | (*v)++; |
153 | 0 | break; |
154 | 0 | } |
155 | 0 | } |
156 | 0 | } |
157 | 0 | } Unexecuted instantiation: void mozilla::ipc::FuzzIntegralType<char>(char*, bool) Unexecuted instantiation: void mozilla::ipc::FuzzIntegralType<unsigned char>(unsigned char*, bool) Unexecuted instantiation: void mozilla::ipc::FuzzIntegralType<short>(short*, bool) Unexecuted instantiation: void mozilla::ipc::FuzzIntegralType<unsigned short>(unsigned short*, bool) Unexecuted instantiation: void mozilla::ipc::FuzzIntegralType<int>(int*, bool) Unexecuted instantiation: void mozilla::ipc::FuzzIntegralType<unsigned int>(unsigned int*, bool) Unexecuted instantiation: void mozilla::ipc::FuzzIntegralType<long>(long*, bool) Unexecuted instantiation: void mozilla::ipc::FuzzIntegralType<unsigned long>(unsigned long*, bool) |
158 | | |
159 | | /** |
160 | | * FuzzFloatingPointType mutates an incercepted floating-point type of a |
161 | | * pickled message. |
162 | | */ |
163 | | template <typename T> |
164 | | void FuzzFloatingPointType(T* v, bool largeValues) |
165 | 0 | { |
166 | 0 | static_assert(mozilla::IsFloatingPoint<T>::value == true, |
167 | 0 | "T must be a floating point type"); |
168 | 0 | switch (random() % 6) { |
169 | 0 | case 0: |
170 | 0 | if (largeValues) { |
171 | 0 | (*v) = RandomNumericLimit<T>(); |
172 | 0 | break; |
173 | 0 | } |
174 | 0 | MOZ_FALLTHROUGH; |
175 | 0 | case 1: |
176 | 0 | if (largeValues) { |
177 | 0 | (*v) = RandomFloatingPointRange<T>(std::numeric_limits<T>::min(), |
178 | 0 | std::numeric_limits<T>::max()); |
179 | 0 | break; |
180 | 0 | } |
181 | 0 | MOZ_FALLTHROUGH; |
182 | 0 | default: |
183 | 0 | (*v) = RandomFloatingPoint<T>(); |
184 | 0 | } |
185 | 0 | } Unexecuted instantiation: void mozilla::ipc::FuzzFloatingPointType<double>(double*, bool) Unexecuted instantiation: void mozilla::ipc::FuzzFloatingPointType<float>(float*, bool) |
186 | | |
187 | | /** |
188 | | * FuzzStringType mutates an incercepted string type of a pickled message. |
189 | | */ |
190 | | template <typename T> |
191 | | void FuzzStringType(T& v, const T& literal1, const T& literal2) |
192 | | { |
193 | | switch (random() % 5) { |
194 | | case 4: |
195 | | v = v + v; |
196 | | MOZ_FALLTHROUGH; |
197 | | case 3: |
198 | | v = v + v; |
199 | | MOZ_FALLTHROUGH; |
200 | | case 2: |
201 | | v = v + v; |
202 | | break; |
203 | | case 1: |
204 | | v += literal1; |
205 | | break; |
206 | | case 0: |
207 | | v = literal2; |
208 | | break; |
209 | | } |
210 | | } |
211 | | |
212 | | |
213 | | Faulty::Faulty() |
214 | | // Mutate messages as a blob. |
215 | | : mFuzzMessages(!!PR_GetEnv("FAULTY_MESSAGES")) |
216 | | // Enables the strategy for fuzzing pipes. |
217 | | , mFuzzPipes(!!PR_GetEnv("FAULTY_PIPE")) |
218 | | // Enables the strategy for fuzzing pickled messages. |
219 | | , mFuzzPickle(!!PR_GetEnv("FAULTY_PICKLE")) |
220 | | // Uses very large values while fuzzing pickled messages. |
221 | | // This may cause a high amount of malloc_abort() / NS_ABORT_OOM crashes. |
222 | | , mUseLargeValues(!!PR_GetEnv("FAULTY_LARGE_VALUES")) |
223 | | // Use the provided blacklist as whitelist. |
224 | | , mUseAsWhitelist(!!PR_GetEnv("FAULTY_AS_WHITELIST")) |
225 | | // Sets up our target process. |
226 | | , mIsValidProcessType(IsValidProcessType()) |
227 | 0 | { |
228 | 0 | if (mIsValidProcessType) { |
229 | 0 | FAULTY_LOG("Initializing for new process of type '%s' with pid %u.", |
230 | 0 | XRE_ChildProcessTypeToString(XRE_GetProcessType()), |
231 | 0 | getpid()); |
232 | 0 |
|
233 | 0 | /* Setup random seed. */ |
234 | 0 | const char* userSeed = PR_GetEnv("FAULTY_SEED"); |
235 | 0 | unsigned long randomSeed = static_cast<unsigned long>(PR_IntervalNow()); |
236 | 0 | if (userSeed) { |
237 | 0 | long n = std::strtol(userSeed, nullptr, 10); |
238 | 0 | if (n != 0) { |
239 | 0 | randomSeed = static_cast<unsigned long>(n); |
240 | 0 | } |
241 | 0 | } |
242 | 0 | srandom(randomSeed); |
243 | 0 |
|
244 | 0 | /* Setup directory for dumping messages. */ |
245 | 0 | mMessagePath = PR_GetEnv("FAULTY_MESSAGE_PATH"); |
246 | 0 | if (mMessagePath && *mMessagePath) { |
247 | 0 | if (CreateOutputDirectory(mMessagePath) != NS_OK) { |
248 | 0 | mMessagePath = nullptr; |
249 | 0 | } |
250 | 0 | } |
251 | 0 |
|
252 | 0 | /* Set IPC messages blacklist. */ |
253 | 0 | mBlacklistPath = PR_GetEnv("FAULTY_BLACKLIST"); |
254 | 0 | if (mBlacklistPath && *mBlacklistPath) { |
255 | 0 | FAULTY_LOG("* Using message blacklist = %s", mBlacklistPath); |
256 | 0 | } |
257 | 0 |
|
258 | 0 | FAULTY_LOG("* Fuzzing strategy: messages = %s", mFuzzMessages ? "enabled" : "disabled"); |
259 | 0 | FAULTY_LOG("* Fuzzing strategy: pickle = %s", mFuzzPickle ? "enabled" : "disabled"); |
260 | 0 | FAULTY_LOG("* Fuzzing strategy: pipe = %s", mFuzzPipes ? "enabled" : "disabled"); |
261 | 0 | FAULTY_LOG("* Fuzzing probability = %u", sDefaultProbability); |
262 | 0 | FAULTY_LOG("* Fuzzing mutation factor = %u", MutationFactor()); |
263 | 0 | FAULTY_LOG("* RNG seed = %lu", randomSeed); |
264 | 0 |
|
265 | 0 | sMsgCounter = 0; |
266 | 0 | } |
267 | 0 | } |
268 | | |
269 | | // static |
270 | | bool |
271 | | Faulty::IsValidProcessType(void) |
272 | 0 | { |
273 | 0 | bool isValidProcessType; |
274 | 0 | const bool targetChildren = !!PR_GetEnv("FAULTY_CHILDREN"); |
275 | 0 | const bool targetParent = !!PR_GetEnv("FAULTY_PARENT"); |
276 | 0 | unsigned short int currentProcessType = XRE_GetProcessType(); |
277 | 0 |
|
278 | 0 | if (targetChildren && !targetParent) { |
279 | 0 | // Fuzz every child process type but not the parent process. |
280 | 0 | isValidProcessType = currentProcessType == GeckoProcessType_Default; |
281 | 0 | } else if (!targetChildren && targetParent |
282 | 0 | && (currentProcessType == GeckoProcessType_Plugin |
283 | 0 | || currentProcessType == GeckoProcessType_Content |
284 | 0 | || currentProcessType == GeckoProcessType_GMPlugin |
285 | 0 | || currentProcessType == GeckoProcessType_GPU |
286 | 0 | || currentProcessType == GeckoProcessType_PDFium |
287 | 0 | || currentProcessType == GeckoProcessType_VR)) { |
288 | 0 | // Fuzz inside any of the above child process only. |
289 | 0 | isValidProcessType = true; |
290 | 0 | } else if (targetChildren && targetParent) { |
291 | 0 | // Fuzz every process type. |
292 | 0 | isValidProcessType = true; |
293 | 0 | } else { |
294 | 0 | // Fuzz no process type at all. |
295 | 0 | isValidProcessType = false; |
296 | 0 | } |
297 | 0 |
|
298 | 0 | if (!isValidProcessType) { |
299 | 0 | FAULTY_LOG("Disabled for this process of type '%s' with pid %d.", |
300 | 0 | XRE_ChildProcessTypeToString(XRE_GetProcessType()), |
301 | 0 | getpid()); |
302 | 0 | } |
303 | 0 |
|
304 | 0 | return isValidProcessType; |
305 | 0 | } |
306 | | |
307 | | // static |
308 | | unsigned int |
309 | | Faulty::DefaultProbability(void) |
310 | 3 | { |
311 | 3 | // Defines the likelihood of fuzzing a message. |
312 | 3 | const char* probability = PR_GetEnv("FAULTY_PROBABILITY"); |
313 | 3 | if (probability) { |
314 | 0 | long n = std::strtol(probability, nullptr, 10); |
315 | 0 | if (n != 0) { |
316 | 0 | return n; |
317 | 0 | } |
318 | 3 | } |
319 | 3 | return FAULTY_DEFAULT_PROBABILITY; |
320 | 3 | } |
321 | | |
322 | | // static |
323 | | bool |
324 | | Faulty::Logging(void) |
325 | 3 | { |
326 | 3 | // Enables logging of sendmsg() calls even in optimized builds. |
327 | 3 | return !!PR_GetEnv("FAULTY_ENABLE_LOGGING"); |
328 | 3 | } |
329 | | |
330 | | |
331 | | // static |
332 | | uint32_t |
333 | | Faulty::MutationFactor() |
334 | 0 | { |
335 | 0 | static uint64_t sPropValue = FAULTY_DEFAULT_MUTATION_FACTOR; |
336 | 0 | static bool sInitialized = false; |
337 | 0 |
|
338 | 0 | if (sInitialized) { |
339 | 0 | return sPropValue; |
340 | 0 | } |
341 | 0 | sInitialized = true; |
342 | 0 |
|
343 | 0 | const char* factor = PR_GetEnv("FAULTY_MUTATION_FACTOR"); |
344 | 0 | if (factor) { |
345 | 0 | long n = strtol(factor, nullptr, 10); |
346 | 0 | if (n != 0) { |
347 | 0 | sPropValue = n; |
348 | 0 | return sPropValue; |
349 | 0 | } |
350 | 0 | } |
351 | 0 | return sPropValue; |
352 | 0 | } |
353 | | |
354 | | // |
355 | | // Strategy: Pipes |
356 | | // |
357 | | |
358 | | void |
359 | | Faulty::MaybeCollectAndClosePipe(int aPipe, unsigned int aProbability) |
360 | 0 | { |
361 | 0 | if (!mFuzzPipes) { |
362 | 0 | return; |
363 | 0 | } |
364 | 0 | |
365 | 0 | if (aPipe > -1) { |
366 | 0 | FAULTY_LOG("Collecting pipe %d to bucket of pipes (count: %zu)", |
367 | 0 | aPipe, mFds.size()); |
368 | 0 | mFds.insert(aPipe); |
369 | 0 | } |
370 | 0 |
|
371 | 0 | if (mFds.size() > 0 && FuzzingTraits::Sometimes(aProbability)) { |
372 | 0 | std::set<int>::iterator it(mFds.begin()); |
373 | 0 | std::advance(it, FuzzingTraits::Random(mFds.size())); |
374 | 0 | FAULTY_LOG("Trying to close collected pipe: %d", *it); |
375 | 0 | errno = 0; |
376 | 0 | while ((close(*it) == -1 && (errno == EINTR))) { |
377 | 0 | ; |
378 | 0 | } |
379 | 0 | FAULTY_LOG("Pipe status after attempt to close: %d", errno); |
380 | 0 | mFds.erase(it); |
381 | 0 | } |
382 | 0 | } |
383 | | |
384 | | // |
385 | | // Strategy: Pickle |
386 | | // |
387 | | |
388 | | void |
389 | | Faulty::MutateBool(bool* aValue) |
390 | 0 | { |
391 | 0 | *aValue = !(*aValue); |
392 | 0 | } |
393 | | |
394 | | void |
395 | | Faulty::FuzzBool(bool* aValue, unsigned int aProbability) |
396 | 0 | { |
397 | 0 | if (mIsValidProcessType) { |
398 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
399 | 0 | bool oldValue = *aValue; |
400 | 0 | MutateBool(aValue); |
401 | 0 | FAULTY_LOG("Message field |bool| of value: %d mutated to: %d", |
402 | 0 | (int)oldValue, (int)*aValue); |
403 | 0 | } |
404 | 0 | } |
405 | 0 | } |
406 | | |
407 | | void |
408 | | Faulty::MutateChar(char* aValue) |
409 | 0 | { |
410 | 0 | FuzzIntegralType<char>(aValue, true); |
411 | 0 | } |
412 | | |
413 | | void |
414 | | Faulty::FuzzChar(char* aValue, unsigned int aProbability) |
415 | 0 | { |
416 | 0 | if (mIsValidProcessType) { |
417 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
418 | 0 | char oldValue = *aValue; |
419 | 0 | MutateChar(aValue); |
420 | 0 | FAULTY_LOG("Message field |char| of value: %c mutated to: %c", |
421 | 0 | oldValue, *aValue); |
422 | 0 | } |
423 | 0 | } |
424 | 0 | } |
425 | | |
426 | | void |
427 | | Faulty::MutateUChar(unsigned char* aValue) |
428 | 0 | { |
429 | 0 | FuzzIntegralType<unsigned char>(aValue, true); |
430 | 0 | } |
431 | | |
432 | | void |
433 | | Faulty::FuzzUChar(unsigned char* aValue, unsigned int aProbability) |
434 | 0 | { |
435 | 0 | if (mIsValidProcessType) { |
436 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
437 | 0 | unsigned char oldValue = *aValue; |
438 | 0 | MutateUChar(aValue); |
439 | 0 | FAULTY_LOG("Message field |unsigned char| of value: %u mutated to: %u", |
440 | 0 | oldValue, *aValue); |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
444 | | |
445 | | void |
446 | | Faulty::MutateInt16(int16_t* aValue) |
447 | 0 | { |
448 | 0 | FuzzIntegralType<int16_t>(aValue, true); |
449 | 0 | } |
450 | | |
451 | | void |
452 | | Faulty::FuzzInt16(int16_t* aValue, unsigned int aProbability) |
453 | 0 | { |
454 | 0 | if (mIsValidProcessType) { |
455 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
456 | 0 | int16_t oldValue = *aValue; |
457 | 0 | MutateInt16(aValue); |
458 | 0 | FAULTY_LOG("Message field |int16| of value: %d mutated to: %d", |
459 | 0 | oldValue, *aValue); |
460 | 0 | } |
461 | 0 | } |
462 | 0 | } |
463 | | |
464 | | void |
465 | | Faulty::MutateUInt16(uint16_t* aValue) |
466 | 0 | { |
467 | 0 | FuzzIntegralType<uint16_t>(aValue, true); |
468 | 0 | } |
469 | | |
470 | | void |
471 | | Faulty::FuzzUInt16(uint16_t* aValue, unsigned int aProbability) |
472 | 0 | { |
473 | 0 | if (mIsValidProcessType) { |
474 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
475 | 0 | uint16_t oldValue = *aValue; |
476 | 0 | MutateUInt16(aValue); |
477 | 0 | FAULTY_LOG("Message field |uint16| of value: %d mutated to: %d", |
478 | 0 | oldValue, *aValue); |
479 | 0 | } |
480 | 0 | } |
481 | 0 | } |
482 | | |
483 | | void |
484 | | Faulty::MutateInt(int* aValue) |
485 | 0 | { |
486 | 0 | FuzzIntegralType<int>(aValue, mUseLargeValues); |
487 | 0 | } |
488 | | |
489 | | void |
490 | | Faulty::FuzzInt(int* aValue, unsigned int aProbability) |
491 | 0 | { |
492 | 0 | if (mIsValidProcessType) { |
493 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
494 | 0 | int oldValue = *aValue; |
495 | 0 | MutateInt(aValue); |
496 | 0 | FAULTY_LOG("Message field |int| of value: %d mutated to: %d", |
497 | 0 | oldValue, *aValue); |
498 | 0 | } |
499 | 0 | } |
500 | 0 | } |
501 | | |
502 | | void |
503 | | Faulty::MutateUInt32(uint32_t* aValue) |
504 | 0 | { |
505 | 0 | FuzzIntegralType<uint32_t>(aValue, mUseLargeValues); |
506 | 0 | } |
507 | | |
508 | | void |
509 | | Faulty::FuzzUInt32(uint32_t* aValue, unsigned int aProbability) |
510 | 0 | { |
511 | 0 | if (mIsValidProcessType) { |
512 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
513 | 0 | uint32_t oldValue = *aValue; |
514 | 0 | MutateUInt32(aValue); |
515 | 0 | FAULTY_LOG("Message field |uint32| of value: %u mutated to: %u", |
516 | 0 | oldValue, *aValue); |
517 | 0 | } |
518 | 0 | } |
519 | 0 | } |
520 | | |
521 | | void |
522 | | Faulty::MutateLong(long* aValue) |
523 | 0 | { |
524 | 0 | FuzzIntegralType<long>(aValue, mUseLargeValues); |
525 | 0 | } |
526 | | |
527 | | void |
528 | | Faulty::FuzzLong(long* aValue, unsigned int aProbability) |
529 | 0 | { |
530 | 0 | if (mIsValidProcessType) { |
531 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
532 | 0 | long oldValue = *aValue; |
533 | 0 | MutateLong(aValue); |
534 | 0 | FAULTY_LOG("Message field |long| of value: %ld mutated to: %ld", |
535 | 0 | oldValue, *aValue); |
536 | 0 | } |
537 | 0 | } |
538 | 0 | } |
539 | | |
540 | | void |
541 | | Faulty::MutateULong(unsigned long* aValue) |
542 | 0 | { |
543 | 0 | FuzzIntegralType<unsigned long>(aValue, mUseLargeValues); |
544 | 0 | } |
545 | | |
546 | | void |
547 | | Faulty::FuzzULong(unsigned long* aValue, unsigned int aProbability) |
548 | 0 | { |
549 | 0 | if (mIsValidProcessType) { |
550 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
551 | 0 | unsigned long oldValue = *aValue; |
552 | 0 | MutateULong(aValue); |
553 | 0 | FAULTY_LOG("Message field |unsigned long| of value: %lu mutated to: %lu", |
554 | 0 | oldValue, *aValue); |
555 | 0 | } |
556 | 0 | } |
557 | 0 | } |
558 | | |
559 | | void |
560 | | Faulty::MutateSize(size_t* aValue) |
561 | 0 | { |
562 | 0 | FuzzIntegralType<size_t>(aValue, mUseLargeValues); |
563 | 0 | } |
564 | | |
565 | | void |
566 | | Faulty::FuzzSize(size_t* aValue, unsigned int aProbability) |
567 | 0 | { |
568 | 0 | if (mIsValidProcessType) { |
569 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
570 | 0 | size_t oldValue = *aValue; |
571 | 0 | MutateSize(aValue); |
572 | 0 | FAULTY_LOG("Message field |size_t| of value: %zu mutated to: %zu", |
573 | 0 | oldValue, *aValue); |
574 | 0 | } |
575 | 0 | } |
576 | 0 | } |
577 | | |
578 | | void |
579 | | Faulty::MutateUInt64(uint64_t* aValue) |
580 | 0 | { |
581 | 0 | FuzzIntegralType<uint64_t>(aValue, mUseLargeValues); |
582 | 0 | } |
583 | | |
584 | | void |
585 | | Faulty::FuzzUInt64(uint64_t* aValue, unsigned int aProbability) |
586 | 0 | { |
587 | 0 | if (mIsValidProcessType) { |
588 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
589 | 0 | uint64_t oldValue = *aValue; |
590 | 0 | MutateUInt64(aValue); |
591 | 0 | FAULTY_LOG("Message field |uint64| of value: %" PRIu64 " mutated to: %" PRIu64, |
592 | 0 | oldValue, *aValue); |
593 | 0 | } |
594 | 0 | } |
595 | 0 | } |
596 | | |
597 | | void |
598 | | Faulty::MutateInt64(int64_t* aValue) |
599 | 0 | { |
600 | 0 | FuzzIntegralType<int64_t>(aValue, mUseLargeValues); |
601 | 0 | } |
602 | | |
603 | | void |
604 | | Faulty::FuzzInt64(int64_t* aValue, unsigned int aProbability) |
605 | 0 | { |
606 | 0 | if (mIsValidProcessType) { |
607 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
608 | 0 | int64_t oldValue = *aValue; |
609 | 0 | MutateInt64(aValue); |
610 | 0 | FAULTY_LOG("Message field |int64| of value: %" PRIu64 " mutated to: %" PRIu64, |
611 | 0 | oldValue, *aValue); |
612 | 0 | } |
613 | 0 | } |
614 | 0 | } |
615 | | |
616 | | void |
617 | | Faulty::MutateDouble(double* aValue) |
618 | 0 | { |
619 | 0 | FuzzFloatingPointType<double>(aValue, mUseLargeValues); |
620 | 0 | } |
621 | | |
622 | | void |
623 | | Faulty::FuzzDouble(double* aValue, unsigned int aProbability) |
624 | 0 | { |
625 | 0 | if (mIsValidProcessType) { |
626 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
627 | 0 | double oldValue = *aValue; |
628 | 0 | MutateDouble(aValue); |
629 | 0 | FAULTY_LOG("Message field |double| of value: %f mutated to: %f", |
630 | 0 | oldValue, *aValue); |
631 | 0 | } |
632 | 0 | } |
633 | 0 | } |
634 | | |
635 | | void |
636 | | Faulty::MutateFloat(float* aValue) |
637 | 0 | { |
638 | 0 | FuzzFloatingPointType<float>(aValue, mUseLargeValues); |
639 | 0 | } |
640 | | |
641 | | void |
642 | | Faulty::FuzzFloat(float* aValue, unsigned int aProbability) |
643 | 0 | { |
644 | 0 | if (mIsValidProcessType) { |
645 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
646 | 0 | float oldValue = *aValue; |
647 | 0 | MutateFloat(aValue); |
648 | 0 | FAULTY_LOG("Message field |float| of value: %f mutated to: %f", |
649 | 0 | oldValue, *aValue); |
650 | 0 | } |
651 | 0 | } |
652 | 0 | } |
653 | | |
654 | | void |
655 | | Faulty::FuzzString(std::string& aValue, unsigned int aProbability) |
656 | 0 | { |
657 | 0 | if (mIsValidProcessType) { |
658 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
659 | 0 | std::string oldValue = aValue; |
660 | 0 | FuzzStringType<std::string>(aValue, "xoferiF", std::string()); |
661 | 0 | FAULTY_LOG("Message field |string| of value: %s mutated to: %s", |
662 | 0 | oldValue.c_str(), aValue.c_str()); |
663 | 0 | } |
664 | 0 | } |
665 | 0 | } |
666 | | |
667 | | void |
668 | | Faulty::FuzzWString(std::wstring& aValue, unsigned int aProbability) |
669 | 0 | { |
670 | 0 | if (mIsValidProcessType) { |
671 | 0 | if (mFuzzPickle && FuzzingTraits::Sometimes(aProbability)) { |
672 | 0 | std::wstring oldValue = aValue; |
673 | 0 | FAULTY_LOG("Message field |wstring|"); |
674 | 0 | FuzzStringType<std::wstring>(aValue, L"xoferiF", std::wstring()); |
675 | 0 | } |
676 | 0 | } |
677 | 0 | } |
678 | | |
679 | | // static |
680 | | nsresult |
681 | 0 | Faulty::CreateOutputDirectory(const char *aPathname) { |
682 | 0 | nsCOMPtr<nsIFile> path; |
683 | 0 | bool exists; |
684 | 0 | nsresult rv; |
685 | 0 |
|
686 | 0 | rv = NS_NewNativeLocalFile(nsDependentCString(aPathname), |
687 | 0 | true, |
688 | 0 | getter_AddRefs(path)); |
689 | 0 |
|
690 | 0 | rv = path->Exists(&exists); |
691 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
692 | 0 | return rv; |
693 | 0 | } |
694 | 0 | |
695 | 0 | if (!exists) { |
696 | 0 | rv = path->Create(nsIFile::DIRECTORY_TYPE, 0755); |
697 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
698 | 0 | return rv; |
699 | 0 | } |
700 | 0 | } |
701 | 0 | |
702 | 0 | return NS_OK; |
703 | 0 | } |
704 | | |
705 | | /* static */ |
706 | | nsresult |
707 | | Faulty::ReadFile(const char* aPathname, nsTArray<nsCString> &aArray) |
708 | 0 | { |
709 | 0 | nsresult rv; |
710 | 0 | nsCOMPtr<nsIFile> file; |
711 | 0 |
|
712 | 0 | rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(aPathname), |
713 | 0 | true, |
714 | 0 | getter_AddRefs(file)); |
715 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
716 | 0 | return rv; |
717 | 0 | } |
718 | 0 | |
719 | 0 | bool exists = false; |
720 | 0 | rv = file->Exists(&exists); |
721 | 0 | if (NS_WARN_IF(NS_FAILED(rv)) || !exists) { |
722 | 0 | return rv; |
723 | 0 | } |
724 | 0 | |
725 | 0 | nsCOMPtr<nsIFileInputStream> fileStream( |
726 | 0 | do_CreateInstance(NS_LOCALFILEINPUTSTREAM_CONTRACTID, &rv)); |
727 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
728 | 0 | return rv; |
729 | 0 | } |
730 | 0 | |
731 | 0 | rv = fileStream->Init(file, -1, -1, 0); |
732 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
733 | 0 | return rv; |
734 | 0 | } |
735 | 0 | |
736 | 0 | nsCOMPtr<nsILineInputStream> lineStream(do_QueryInterface(fileStream, &rv)); |
737 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
738 | 0 | return rv; |
739 | 0 | } |
740 | 0 | |
741 | 0 | nsAutoCString line; |
742 | 0 | bool more = true; |
743 | 0 | do { |
744 | 0 | rv = lineStream->ReadLine(line, &more); |
745 | 0 | if (line.IsEmpty()) { |
746 | 0 | continue; |
747 | 0 | } |
748 | 0 | if (line.CharAt(0) == '#') { |
749 | 0 | /* Ignore comments. */ |
750 | 0 | continue; |
751 | 0 | } |
752 | 0 | aArray.AppendElement(line); |
753 | 0 | } while (more); |
754 | 0 |
|
755 | 0 | return NS_OK; |
756 | 0 | } |
757 | | |
758 | | bool |
759 | 0 | Faulty::IsMessageNameBlacklisted(const char *aMessageName) { |
760 | 0 | static bool sFileLoaded = false; |
761 | 0 | static nsTArray<nsCString> sMessageBlacklist; |
762 | 0 |
|
763 | 0 | if (!sFileLoaded && mBlacklistPath) { |
764 | 0 | /* Run ReadFile() on the main thread to prevent |
765 | 0 | MOZ_ASSERT(NS_IsMainThread()) in nsStandardURL via nsNetStartup(). */ |
766 | 0 | nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction( |
767 | 0 | "Fuzzer::ReadBlacklistOnMainThread", |
768 | 0 | [&]() { |
769 | 0 | if (Faulty::ReadFile(mBlacklistPath, sMessageBlacklist) != NS_OK) { |
770 | 0 | sFileLoaded = false; |
771 | 0 | } else { |
772 | 0 | sFileLoaded = true; |
773 | 0 | } |
774 | 0 | } |
775 | 0 | ); |
776 | 0 | NS_DispatchToMainThread(r.forget(), NS_DISPATCH_SYNC); |
777 | 0 | } |
778 | 0 |
|
779 | 0 | if (!sFileLoaded) { |
780 | 0 | return false; |
781 | 0 | } |
782 | 0 | |
783 | 0 | if (sMessageBlacklist.Length() == 0) { |
784 | 0 | return false; |
785 | 0 | } |
786 | 0 | |
787 | 0 | return sMessageBlacklist.Contains(aMessageName); |
788 | 0 | } |
789 | | |
790 | | // static |
791 | | std::vector<uint8_t> |
792 | | Faulty::GetDataFromIPCMessage(IPC::Message* aMsg) |
793 | 0 | { |
794 | 0 | const Pickle::BufferList& buffers = aMsg->Buffers(); |
795 | 0 | std::vector<uint8_t> data; |
796 | 0 | data.reserve(buffers.Size()); |
797 | 0 |
|
798 | 0 | Pickle::BufferList::IterImpl i = buffers.Iter(); |
799 | 0 | while (!i.Done()) { |
800 | 0 | size_t s = i.RemainingInSegment(); |
801 | 0 | data.insert(data.end(), i.Data(), i.Data() + s); |
802 | 0 |
|
803 | 0 | i.Advance(buffers, s); |
804 | 0 | } |
805 | 0 |
|
806 | 0 | return data; |
807 | 0 | } |
808 | | |
809 | | // static |
810 | | void |
811 | 0 | Faulty::CopyFDs(IPC::Message* aDstMsg, IPC::Message* aSrcMsg) { |
812 | 0 | FileDescriptorSet* dstFdSet = aDstMsg->file_descriptor_set(); |
813 | 0 | FileDescriptorSet* srcFdSet = aSrcMsg->file_descriptor_set(); |
814 | 0 | for (size_t i = 0; i < srcFdSet->size(); i++) { |
815 | 0 | int fd = srcFdSet->GetDescriptorAt(i); |
816 | 0 | dstFdSet->Add(fd); |
817 | 0 | } |
818 | 0 | } |
819 | | |
820 | | IPC::Message * |
821 | 0 | Faulty::MutateIPCMessage(const char *aChannel, IPC::Message* aMsg, unsigned int aProbability) { |
822 | 0 | if (!mIsValidProcessType || !mFuzzMessages) { |
823 | 0 | return aMsg; |
824 | 0 | } |
825 | 0 | |
826 | 0 | sMsgCounter += 1; |
827 | 0 | LogMessage(aChannel, aMsg); |
828 | 0 |
|
829 | 0 | /* Skip immediately if we shall not try to fuzz this message. */ |
830 | 0 | if (!FuzzingTraits::Sometimes(aProbability)) { |
831 | 0 | return aMsg; |
832 | 0 | } |
833 | 0 | |
834 | 0 | const bool isMessageListed = IsMessageNameBlacklisted(aMsg->name()); |
835 | 0 |
|
836 | 0 | /* Check if this message is blacklisted and shall not get fuzzed. */ |
837 | 0 | if (isMessageListed && !mUseAsWhitelist) { |
838 | 0 | FAULTY_LOG("BLACKLISTED: %s", aMsg->name()); |
839 | 0 | return aMsg; |
840 | 0 | } |
841 | 0 |
|
842 | 0 | /* Check if the message is whitelisted. */ |
843 | 0 | if (!isMessageListed && mUseAsWhitelist) { |
844 | 0 | /* Silently skip this message. */ |
845 | 0 | return aMsg; |
846 | 0 | } |
847 | 0 | |
848 | 0 | /* Retrieve BufferLists as data from original message. */ |
849 | 0 | std::vector<uint8_t> data(GetDataFromIPCMessage(aMsg)); |
850 | 0 |
|
851 | 0 | /* Check if there is enough data in the message to fuzz. */ |
852 | 0 | uint32_t headerSize = aMsg->HeaderSizeFromData(nullptr, nullptr); |
853 | 0 | if (headerSize == data.size()) { |
854 | 0 | FAULTY_LOG("IGNORING: %s", aMsg->name()); |
855 | 0 | return aMsg; |
856 | 0 | } |
857 | 0 |
|
858 | 0 | /* Mutate the message data. */ |
859 | 0 | size_t maxMutations = FuzzingTraits::Frequency(data.size(), MutationFactor()); |
860 | 0 | FAULTY_LOG("FUZZING (%zu bytes): %s", maxMutations, aMsg->name()); |
861 | 0 | while (maxMutations--) { |
862 | 0 | /* Ignore the header data of the message. */ |
863 | 0 | uint32_t pos = RandomIntegerRange<uint32_t>(headerSize, data.size() - 1); |
864 | 0 | switch (FuzzingTraits::Random(6)) { |
865 | 0 | case 0: |
866 | 0 | break; |
867 | 0 | case 1: |
868 | 0 | data.at(pos) = RandomIntegerRange<uint8_t>(0, 1); |
869 | 0 | break; |
870 | 0 | case 2: |
871 | 0 | data.at(pos) ^= (1 << RandomIntegerRange<uint8_t>(0, 8)); |
872 | 0 | break; |
873 | 0 | default: |
874 | 0 | data.at(pos) = RandomIntegerRange<uint8_t>(255, 255); |
875 | 0 | } |
876 | 0 | } |
877 | 0 |
|
878 | 0 | /* Build new message. */ |
879 | 0 | auto *mutatedMsg = new IPC::Message(reinterpret_cast<const char*>(data.data()), data.size()); |
880 | 0 | CopyFDs(mutatedMsg, aMsg); |
881 | 0 |
|
882 | 0 | /* Dump original message for diff purposes. */ |
883 | 0 | DumpMessage(aChannel, aMsg, nsPrintfCString(".%zu.o", sMsgCounter).get()); |
884 | 0 | /* Dump mutated message for diff purposes. */ |
885 | 0 | DumpMessage(aChannel, mutatedMsg, nsPrintfCString(".%zu.m", sMsgCounter).get()); |
886 | 0 |
|
887 | 0 | delete aMsg; |
888 | 0 |
|
889 | 0 | return mutatedMsg; |
890 | 0 | } |
891 | | |
892 | | void |
893 | 0 | Faulty::LogMessage(const char* aChannel, IPC::Message* aMsg) { |
894 | 0 | if (!mIsValidProcessType) { |
895 | 0 | return; |
896 | 0 | } |
897 | 0 | |
898 | 0 | std::string fileName = nsPrintfCString("message.%u.%zu", |
899 | 0 | getpid(), sMsgCounter).get(); |
900 | 0 |
|
901 | 0 | FAULTY_LOG("Process: %u | Size: %10zu | %-20s | %s => %s", |
902 | 0 | XRE_GetProcessType(), |
903 | 0 | aMsg->Buffers().Size(), |
904 | 0 | fileName.c_str(), |
905 | 0 | aChannel, |
906 | 0 | aMsg->name()); |
907 | 0 | } |
908 | | |
909 | | void |
910 | | Faulty::DumpMessage(const char *aChannel, IPC::Message* aMsg, std::string aAppendix) |
911 | 0 | { |
912 | 0 | if (!mIsValidProcessType || !mMessagePath) { |
913 | 0 | return; |
914 | 0 | } |
915 | 0 | |
916 | 0 | std::vector<uint8_t> data(GetDataFromIPCMessage(aMsg)); |
917 | 0 | std::string fileName; |
918 | 0 |
|
919 | 0 | if (!aAppendix.empty()) { |
920 | 0 | fileName = nsPrintfCString("%s/message.%u%s", |
921 | 0 | mMessagePath, getpid(), aAppendix.c_str()).get(); |
922 | 0 | } else { |
923 | 0 | fileName = nsPrintfCString("%s/%s", |
924 | 0 | mMessagePath, fileName.c_str()).get(); |
925 | 0 | } |
926 | 0 |
|
927 | 0 | std::fstream fp; |
928 | 0 | fp.open(fileName, std::fstream::out | std::fstream::binary); |
929 | 0 | fp.write(reinterpret_cast<const char*>(data.data()), data.size()); |
930 | 0 | fp.close(); |
931 | 0 | } |
932 | | |
933 | | } // namespace ipc |
934 | | } // namespace mozilla |
935 | | |