Coverage Report

Created: 2018-09-25 14:53

/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