-
Notifications
You must be signed in to change notification settings - Fork 124
Expand file tree
/
Copy pathcentipede_callbacks.h
More file actions
241 lines (210 loc) · 10 KB
/
centipede_callbacks.h
File metadata and controls
241 lines (210 loc) · 10 KB
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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
// Copyright 2022 The Centipede Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef THIRD_PARTY_CENTIPEDE_CENTIPEDE_CALLBACKS_H_
#define THIRD_PARTY_CENTIPEDE_CENTIPEDE_CALLBACKS_H_
#include <cstddef>
#include <filesystem> // NOLINT
#include <string>
#include <string_view>
#include <vector>
#include "absl/base/nullability.h"
#include "absl/status/statusor.h"
#include "./centipede/binary_info.h"
#include "./centipede/byte_array_mutator.h"
#include "./centipede/command.h"
#include "./centipede/environment.h"
#include "./centipede/fuzztest_mutator.h"
#include "./centipede/mutation_input.h"
#include "./centipede/runner_result.h"
#include "./centipede/shared_memory_blob_sequence.h"
#include "./centipede/util.h"
#include "./common/defs.h"
namespace fuzztest::internal {
// User must inherit from this class and override at least the
// pure virtual functions.
//
// The classes inherited from this one must be thread-compatible.
// Note: the interface is not yet stable and may change w/o a notice.
class CentipedeCallbacks {
public:
// `env` is used to pass flags to `this`, it must outlive `this`.
CentipedeCallbacks(const Environment &env)
: env_(env),
byte_array_mutator_(env.knobs, GetRandomSeed(env.seed)),
fuzztest_mutator_(env.knobs, GetRandomSeed(env.seed)),
inputs_blobseq_(shmem_name1_.c_str(), env.shmem_size_mb << 20,
env.use_posix_shmem),
outputs_blobseq_(shmem_name2_.c_str(), env.shmem_size_mb << 20,
env.use_posix_shmem) {
if (env.use_legacy_default_mutator)
FUZZTEST_CHECK(byte_array_mutator_.set_max_len(env.max_len));
else
FUZZTEST_CHECK(fuzztest_mutator_.set_max_len(env.max_len));
}
virtual ~CentipedeCallbacks() {}
// Feeds `inputs` into the `binary`, for every input populates `batch_result`.
// Old contents of `batch_result` are cleared.
// Returns true on success, false on failure.
// Post-condition:
// `batch_result` has results for every `input`, even on failure.
virtual bool Execute(std::string_view binary,
const std::vector<ByteArray>& inputs,
BatchResult& batch_result, absl::Time deadline) = 0;
// Takes non-empty `inputs` and returns at most `num_mutants` mutated inputs.
virtual std::vector<ByteArray> Mutate(
const std::vector<MutationInputRef> &inputs, size_t num_mutants) {
return env_.use_legacy_default_mutator
? byte_array_mutator_.MutateMany(inputs, num_mutants)
: fuzztest_mutator_.MutateMany(inputs, num_mutants);
}
// Populates the BinaryInfo using the `symbolizer_path` and `coverage_binary`
// in `env_`. The tables may not be populated if the PC table cannot be
// determined from the `coverage_binary` or if symbolization fails. Exits if
// PC table was not populated and `env_.require_pc_table` is set.
virtual void PopulateBinaryInfo(BinaryInfo &binary_info);
// Retrieves at most `num_seeds` seed inputs. Returns the number of seeds
// available if `num_seeds` had been large enough.
virtual size_t GetSeeds(size_t num_seeds, std::vector<ByteArray> &seeds) {
if (num_seeds > 0) seeds = {{0}};
return 1;
}
// Returns the configuration from the test target in the serialized form.
// Returns an empty string if the test target doesn't provide configuration.
virtual absl::StatusOr<std::string> GetSerializedTargetConfig() { return ""; }
// Requests any commands that are executing in persistent mode to
// end the execution and waits for them.
void CleanUpPersistentMode();
protected:
// Helpers that the user-defined class may use if needed.
// Same as ExecuteCentipedeSancovBinary, but uses shared memory.
// Much faster for fast targets since it uses fewer system calls.
int ExecuteCentipedeSancovBinaryWithShmem(
std::string_view binary, const std::vector<ByteArray>& inputs,
BatchResult& batch_result, absl::Time deadline);
// Constructs a string CENTIPEDE_RUNNER_FLAGS=":flag1:flag2:...",
// where the flags are determined by `env` and also include `extra_flags`.
// If `disable_coverage`, coverage options are not added.
std::string ConstructRunnerFlags(std::string_view extra_flags = "",
bool disable_coverage = false);
// Uses an external binary `binary` to generate seed inputs. The binary should
// be linked against :centipede_runner and implement the RunnerCallbacks
// interface as described in runner_interface.h.
//
// Retrieves the first `seeds.size()` inputs (if exist) from `binary`,
// replacing the existing elements of `seeds`, and shrinking `seeds` if
// needed. Sets `num_avail_seeds` to the number of available seeds, which may
// be more than `seeds.size()`.
//
// Returns true on success.
bool GetSeedsViaExternalBinary(std::string_view binary,
size_t &num_avail_seeds,
std::vector<ByteArray> &seeds);
// Uses an external binary `binary` to get the serialized test target
// configuration. The binary should be linked against :centipede_runner and
// implement the RunnerCallbacks interface as described in runner_interface.h.
//
// If the binary returns with success and doesn't provide the configuration,
// sets `serialized_config` to empty string.
//
// Returns true on success.
bool GetSerializedTargetConfigViaExternalBinary(
std::string_view binary, std::string &serialized_config);
// Uses an external binary `binary` to mutate `inputs`. The binary
// should be linked against :centipede_runner and implement the
// RunnerCallbacks interface as described in runner_interface.h,
// or implement the legacy Structure-Aware Fuzzing interface described here:
// github.com/google/fuzzing/blob/master/docs/structure-aware-fuzzing.md
//
// Returns a `MutationResult` instance where `exit_code` indicates whether
// the binary was executed successfully, `has_custom_mutator` indicates
// whether the binary has a custom mutator, and if it does, `mutants` contains
// at most `num_mutants` non-empty mutants.
MutationResult MutateViaExternalBinary(
std::string_view binary, const std::vector<MutationInputRef> &inputs,
size_t num_mutants);
// Loads the dictionary from `dictionary_path`,
// returns the number of dictionary entries loaded.
size_t LoadDictionary(std::string_view dictionary_path);
protected:
const Environment &env_;
ByteArrayMutator byte_array_mutator_;
FuzzTestMutator fuzztest_mutator_;
private:
class PersistentModeServer;
struct CommandContext {
Command cmd;
// Opaque server for persistent mode. Nullptr if persistent mode is
// disabled.
std::unique_ptr<PersistentModeServer> persistent_mode_server;
~CommandContext();
};
// Returns a CommandContext with matching `binary`. Creates one if needed.
CommandContext& GetOrCreateCommandContextForBinary(std::string_view binary);
// Runs a batch with the command `binary` and returns the exit code.
int RunBatchForBinary(std::string_view binary, absl::Time deadline);
// Prints the execution log from the last executed binary.
void PrintExecutionLog() const;
// Variables required for ExecuteCentipedeSancovBinaryWithShmem.
// They are computed in CTOR, to avoid extra computation in the hot loop.
std::string temp_dir_ = TemporaryLocalDirPath();
std::string temp_input_file_path_ =
std::filesystem::path(temp_dir_).append("temp_input_file");
const std::string execute_log_path_ =
std::filesystem::path(temp_dir_).append("log");
std::string failure_description_path_ =
std::filesystem::path(temp_dir_).append("failure_description");
std::string failure_signature_path_ =
std::filesystem::path(temp_dir_).append("failure_signature");
const std::string shmem_name1_ = ProcessAndThreadUniqueID("/ctpd-shm1-");
const std::string shmem_name2_ = ProcessAndThreadUniqueID("/ctpd-shm2-");
SharedMemoryBlobSequence inputs_blobseq_;
SharedMemoryBlobSequence outputs_blobseq_;
// Need unique_ptr indirection because CommandContext is not movable/copyable
// due to Command.
std::vector<std::unique_ptr<CommandContext>> command_contexts_;
};
// Abstract class for creating/destroying CentipedeCallbacks objects.
// A typical implementation would simply new/delete objects of appropriate type,
// see DefaultCallbacksFactory below.
// Other implementations (e.g. for tests) may take the object from elsewhere
// and not actually delete it.
class CentipedeCallbacksFactory {
public:
virtual CentipedeCallbacks *create(const Environment &env) = 0;
virtual void destroy(CentipedeCallbacks *callbacks) = 0;
virtual ~CentipedeCallbacksFactory() {}
};
// This is the typical way to implement a CentipedeCallbacksFactory for a Type.
template <typename Type>
class DefaultCallbacksFactory : public CentipedeCallbacksFactory {
public:
CentipedeCallbacks *create(const Environment &env) override {
return new Type(env);
}
void destroy(CentipedeCallbacks *callbacks) override { delete callbacks; }
};
// Creates a CentipedeCallbacks object in CTOR and destroys it in DTOR.
class ScopedCentipedeCallbacks {
public:
ScopedCentipedeCallbacks(CentipedeCallbacksFactory &factory,
const Environment &env)
: factory_(factory), callbacks_(factory_.create(env)) {}
~ScopedCentipedeCallbacks() { factory_.destroy(callbacks_); }
CentipedeCallbacks *absl_nonnull callbacks() { return callbacks_; }
private:
CentipedeCallbacksFactory &factory_;
CentipedeCallbacks *callbacks_;
};
} // namespace fuzztest::internal
#endif // THIRD_PARTY_CENTIPEDE_CENTIPEDE_CALLBACKS_H_