File: in_process_fuzzer.h

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (161 lines) | stat: -rw-r--r-- 6,698 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef CHROME_TEST_FUZZING_IN_PROCESS_FUZZER_H_
#define CHROME_TEST_FUZZING_IN_PROCESS_FUZZER_H_

#include <optional>

#include "chrome/test/base/in_process_browser_test.h"

enum class RunLoopTimeoutBehavior {
  // Default behavior that doesn't alter the current way run loop internal
  // mechanism handles timeouts.
  kDefault,
  // Continues the normal execution after the call to RunLoop::Run. This
  // basically ignores the timeouts.
  kContinue,
  // Calls InProcessFuzzer::DeclareInfiniteLoop. This will still run the fuzz
  // case as `kContinue` would until the Fuzz method returns.
  kDeclareInfiniteLoop,
};

struct InProcessFuzzerOptions {
  // The behavior to be set when a run loop times out.
  RunLoopTimeoutBehavior run_loop_timeout_behavior =
      RunLoopTimeoutBehavior::kDefault;

  // Sets the timeout for the "Fuzz" method to complete.
  std::optional<base::TimeDelta> run_loop_timeout = std::nullopt;
};

// In-process fuzz test.
//
// This is equivalent to a browser test, in that the entire browser
// environment is available for your use, and you can do rich things that
// require the whole browser infrastructure.
//
// The 'Fuzz' method will be called repeatedly, and you just have to
// implement something sensible there to explore parts of Chrome's
// attack surface.
//
// Register your subclass with REGISTER_IN_PROCESS_FUZZER. There can only
// be one per executable.
//
// Different fuzz frameworks might run this in different ways.
// For instance,
// * libfuzzer runs this in a multi-process Chrome browser_test
//   environment.
// * centipede runs it in single-process browser_test mode (currently),
//   with an external fuzz co-ordinator running multiple instances
//   of Chrome.
// * in the future, snapshot fuzzers might pause a VM and resume
//   clones of it (to ensure a cleaner state for each iteration)
// To the extent possible, you should write your fuzzer to be
// implementation-independent and semantically express what
// should happen during such fuzzing of the whole browser.
class InProcessFuzzer : virtual public InProcessBrowserTest {
 public:
  // Called by the main function to create this class.
  // This is called prior to all the normal browser test setup,
  // so don't do anything important in your constructor.
  // Furthermore, this will be re-run even for child Chromium processes.
  // NOLINTNEXTLINE(runtime/explicit)
  InProcessFuzzer(InProcessFuzzerOptions options = {});
  ~InProcessFuzzer() override;

  // Called by the main function to run this fuzzer, after the browser_test
  // equivalent infrastructure has been set up.
  void Run(const std::vector<std::string>& libfuzzer_command_line);

  // If you override this, it's essential you call the superclass method.
  void SetUpOnMainThread() override;
  void RunTestOnMainThread() override;
  void TestBody() override {}
  void SetUp() override;

  friend int fuzz_callback(const uint8_t* data, size_t size);

  // Override if you want to pass particular command line arguments to
  // Chromium for its startup. This is called before any fuzz test case
  // is actually run, so unfortunately you can't generate these through
  // fuzzing. In addition, the browser test framework itself does all
  // sorts of fiddling with the arguments (e.g. a user data dir).
  // It's generally OK to leave this at the default unless you specifically
  // need to enable a feature or similar.
  // Do not include the executable name in your return value - that's
  // prepended automatically.
  virtual base::CommandLine::StringVector GetChromiumCommandLineArguments();

  // Override if (unusually) your fuzzer should use Chromium in multi-
  // process mode. This can make results more realistic, but impedes
  // collection of coverage from the renderer.
  virtual bool UseSingleProcessMode();

 protected:
  // Callback to actually do your fuzzing. This is called from the UI thread,
  // so you should take care not to block the thread too long. If you need
  // to run your fuzz case across multiple threads, consider a nested RunLoop.
  // Return 0 if the input is valid, -1 if it's invalid and should not be
  // evolved further by the fuzzing engine.
  virtual int Fuzz(const uint8_t* data, size_t size) = 0;

  // Should be called by subclasses from within Fuzz if they believe that
  // a fuzz case is going to take infinite time to run. This will arrange
  // to communicate this status to the fuzz engine as far as possible,
  // then for the whole process to exit, thus throwing away that fuzz case.
  // However, after calling this method, Fuzz should return -1 to indicate
  // invalid input.
  // The normal pattern for using this is, within Fuzz, to do this:
  // 1. Create a RunLoop but don't start it yet
  // 2. Start a OneShotTimer which calls this method then stops the RunLoop
  // 3. Start an async task which will run the test case, cancel the timer,
  //    and then stop the run loop
  // 4. Start the RunLoop.
  // If the test case turns out not actually to be infinite, step 3 could
  // cause a UaF, so this pattern can probably be improved in future.
  void DeclareInfiniteLoop() { exit_after_fuzz_case_ = true; }

  // Whether we're in corpus merging mode. Some fuzzers behave specially
  // in this mode for efficiency reasons.
  bool InMergeMode() const;

 private:
  int DoFuzz(const uint8_t* data, size_t size);

  // Changes run loop timeout behavior to silently continue running the
  // test/fuzzer instead of failing. Timed out run loops will stop running,
  // but the rest of the test will continue executing.
  void KeepRunningOnTimeout();

  // Changes run loop timeouts behaviour to call `DeclareInfiniteLoop()`.
  void DeclareInfiniteLoopOnTimeout();

 private:
  std::vector<std::string> libfuzzer_command_line_;
  bool exit_after_fuzz_case_ = false;
  InProcessFuzzerOptions options_;
};

class InProcessFuzzerFactoryBase {
 public:
  virtual std::unique_ptr<InProcessFuzzer> CreateInProcessFuzzer() = 0;
};

extern InProcessFuzzerFactoryBase* g_in_process_fuzzer_factory;

// Class used to register a single in-process fuzzer in each executable.
template <typename T>
class InProcessFuzzerFactory : public InProcessFuzzerFactoryBase {
 public:
  InProcessFuzzerFactory() { g_in_process_fuzzer_factory = this; }
  std::unique_ptr<InProcessFuzzer> CreateInProcessFuzzer() override {
    return std::make_unique<T>();
  }
};

#define REGISTER_IN_PROCESS_FUZZER(fuzzer_class) \
  InProcessFuzzerFactory<fuzzer_class> fuzzer_instance;

#endif  // CHROME_TEST_FUZZING_IN_PROCESS_FUZZER_H_