Reland "Enable simulating errors to test fuzzer reliability"
This is a reland of 4ad08c82f7
The reland organizes the different error types in separate functions
for separate call stacks. Error simulation is also guarded by
a minimum file size to prevent Clusterfuzz from getting stuck with
its bad-build check.
Original change's description:
> Enable simulating errors to test fuzzer reliability
>
> This adds a d8 flag --simulate-errors, which on shutdown will cause
> certain errors. This enables testing the reliability of sanitizers.
>
> This will cause a fatal error, a dcheck (if available) or a
> violation that can be detected with one of the following sanitizers:
> ASAN, UBSAN, MSAN, CFI.
>
> The same flag used in differential fuzzing will cause an error
> subsumed with the error state "fake_difference".
>
> Bug: chromium:1152412
> Change-Id: I4b36c6fe716797004d634263617d22ca67b05600
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2554999
> Commit-Queue: Michael Achenbach <machenbach@chromium.org>
> Reviewed-by: Clemens Backes <clemensb@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#71430}
Bug: chromium:1152412
Change-Id: I604258b4c1ebd215c26b1de6b2822663f857bf64
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2565125
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Reviewed-by: Clemens Backes <clemensb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71538}
This commit is contained in:
parent
89ffd740cd
commit
886d7cfee4
83
src/d8/d8.cc
83
src/d8/d8.cc
@ -465,6 +465,7 @@ base::LazyMutex Shell::workers_mutex_;
|
||||
bool Shell::allow_new_workers_ = true;
|
||||
std::unordered_set<std::shared_ptr<Worker>> Shell::running_workers_;
|
||||
std::atomic<bool> Shell::script_executed_{false};
|
||||
std::atomic<bool> Shell::valid_fuzz_script_{false};
|
||||
base::LazyMutex Shell::isolate_status_lock_;
|
||||
std::map<v8::Isolate*, bool> Shell::isolate_status_;
|
||||
std::map<v8::Isolate*, int> Shell::isolate_running_streaming_tasks_;
|
||||
@ -2642,6 +2643,84 @@ void Shell::OnExit(v8::Isolate* isolate) {
|
||||
|
||||
delete counters_file_;
|
||||
delete counter_map_;
|
||||
|
||||
if (options.simulate_errors && is_valid_fuzz_script()) {
|
||||
// Simulate several errors detectable by fuzzers behind a flag if the
|
||||
// minimum file size for fuzzing was executed.
|
||||
FuzzerMonitor::SimulateErrors();
|
||||
}
|
||||
}
|
||||
|
||||
void Dummy(char* arg) {}
|
||||
|
||||
V8_NOINLINE void FuzzerMonitor::SimulateErrors() {
|
||||
// Initialize a fresh RNG to not interfere with JS execution.
|
||||
std::unique_ptr<base::RandomNumberGenerator> rng;
|
||||
int64_t seed = internal::FLAG_random_seed;
|
||||
if (seed != 0) {
|
||||
rng = std::make_unique<base::RandomNumberGenerator>(seed);
|
||||
} else {
|
||||
rng = std::make_unique<base::RandomNumberGenerator>();
|
||||
}
|
||||
|
||||
double p = rng->NextDouble();
|
||||
if (p < 0.1) {
|
||||
ControlFlowViolation();
|
||||
} else if (p < 0.2) {
|
||||
DCheck();
|
||||
} else if (p < 0.3) {
|
||||
Fatal();
|
||||
} else if (p < 0.4) {
|
||||
ObservableDifference();
|
||||
} else if (p < 0.5) {
|
||||
UndefinedBehavior();
|
||||
} else if (p < 0.6) {
|
||||
UseAfterFree();
|
||||
} else if (p < 0.7) {
|
||||
UseOfUninitializedValue();
|
||||
}
|
||||
}
|
||||
|
||||
V8_NOINLINE void FuzzerMonitor::ControlFlowViolation() {
|
||||
// Control flow violation caught by CFI.
|
||||
void (*func)() = (void (*)()) & Dummy;
|
||||
func();
|
||||
}
|
||||
|
||||
V8_NOINLINE void FuzzerMonitor::DCheck() {
|
||||
// Caught in debug builds.
|
||||
DCHECK(false);
|
||||
}
|
||||
|
||||
V8_NOINLINE void FuzzerMonitor::Fatal() {
|
||||
// Caught in all build types.
|
||||
FATAL("Fake error.");
|
||||
}
|
||||
|
||||
V8_NOINLINE void FuzzerMonitor::ObservableDifference() {
|
||||
// Observable difference caught by differential fuzzing.
|
||||
printf("___fake_difference___\n");
|
||||
}
|
||||
|
||||
V8_NOINLINE void FuzzerMonitor::UndefinedBehavior() {
|
||||
// Caught by UBSAN.
|
||||
int32_t val = -1;
|
||||
USE(val << 8);
|
||||
}
|
||||
|
||||
V8_NOINLINE void FuzzerMonitor::UseAfterFree() {
|
||||
// Use-after-free caught by ASAN.
|
||||
std::vector<bool>* storage = new std::vector<bool>(3);
|
||||
delete storage;
|
||||
USE(storage->at(1));
|
||||
}
|
||||
|
||||
V8_NOINLINE void FuzzerMonitor::UseOfUninitializedValue() {
|
||||
// Use-of-uninitialized-value caught by MSAN.
|
||||
#if defined(__clang__)
|
||||
int uninitialized[1];
|
||||
if (uninitialized[0]) USE(uninitialized);
|
||||
#endif
|
||||
}
|
||||
|
||||
static FILE* FOpen(const char* path, const char* mode) {
|
||||
@ -3013,6 +3092,7 @@ bool SourceGroup::Execute(Isolate* isolate) {
|
||||
base::OS::ExitProcess(1);
|
||||
}
|
||||
Shell::set_script_executed();
|
||||
Shell::update_script_size(source->Length());
|
||||
if (!Shell::ExecuteString(isolate, source, file_name, Shell::kNoPrintResult,
|
||||
Shell::kReportExceptions,
|
||||
Shell::kProcessMessageQueue)) {
|
||||
@ -3387,6 +3467,9 @@ bool Shell::SetOptions(int argc, char* argv[]) {
|
||||
} else if (strcmp(argv[i], "--no-arguments") == 0) {
|
||||
options.include_arguments = false;
|
||||
argv[i] = nullptr;
|
||||
} else if (strcmp(argv[i], "--simulate-errors") == 0) {
|
||||
options.simulate_errors = true;
|
||||
argv[i] = nullptr;
|
||||
} else if (strcmp(argv[i], "--stress-opt") == 0) {
|
||||
options.stress_opt = true;
|
||||
argv[i] = nullptr;
|
||||
|
23
src/d8/d8.h
23
src/d8/d8.h
@ -343,6 +343,7 @@ class ShellOptions {
|
||||
DisallowReassignment<bool> omit_quit = {"omit-quit", false};
|
||||
DisallowReassignment<bool> wait_for_background_tasks = {
|
||||
"wait-for-background-tasks", true};
|
||||
DisallowReassignment<bool> simulate_errors = {"simulate-errors", false};
|
||||
DisallowReassignment<bool> stress_opt = {"stress-opt", false};
|
||||
DisallowReassignment<int> stress_runs = {"stress-runs", 1};
|
||||
DisallowReassignment<bool> stress_snapshot = {"stress-snapshot", false};
|
||||
@ -557,6 +558,11 @@ class Shell : public i::AllStatic {
|
||||
!options.test_shell;
|
||||
}
|
||||
|
||||
static void update_script_size(int size) {
|
||||
if (size > 0) valid_fuzz_script_.store(true);
|
||||
}
|
||||
static bool is_valid_fuzz_script() { return valid_fuzz_script_.load(); }
|
||||
|
||||
static void WaitForRunningWorkers();
|
||||
static void AddRunningWorker(std::shared_ptr<Worker> worker);
|
||||
static void RemoveRunningWorker(const std::shared_ptr<Worker>& worker);
|
||||
@ -584,8 +590,9 @@ class Shell : public i::AllStatic {
|
||||
static bool allow_new_workers_;
|
||||
static std::unordered_set<std::shared_ptr<Worker>> running_workers_;
|
||||
|
||||
// Multiple isolates may update this flag concurrently.
|
||||
// Multiple isolates may update these flags concurrently.
|
||||
static std::atomic<bool> script_executed_;
|
||||
static std::atomic<bool> valid_fuzz_script_;
|
||||
|
||||
static void WriteIgnitionDispatchCountersFile(v8::Isolate* isolate);
|
||||
// Append LCOV coverage data to file.
|
||||
@ -634,6 +641,20 @@ class Shell : public i::AllStatic {
|
||||
static std::atomic<int> unhandled_promise_rejections_;
|
||||
};
|
||||
|
||||
class FuzzerMonitor : public i::AllStatic {
|
||||
public:
|
||||
static void SimulateErrors();
|
||||
|
||||
private:
|
||||
static void ControlFlowViolation();
|
||||
static void DCheck();
|
||||
static void Fatal();
|
||||
static void ObservableDifference();
|
||||
static void UndefinedBehavior();
|
||||
static void UseAfterFree();
|
||||
static void UseOfUninitializedValue();
|
||||
};
|
||||
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_D8_D8_H_
|
||||
|
@ -429,7 +429,7 @@ def run_comparisons(suppress, execution_configs, test_case, timeout,
|
||||
else:
|
||||
# Subsume simulated and unexpected crashes (e.g. during smoke tests)
|
||||
# with one failure state.
|
||||
crash_state = 'simulated crash' if simulated else 'unexpected crash'
|
||||
crash_state = '_simulated_crash_' if simulated else '_unexpected_crash_'
|
||||
raise FailException(FAILURE_HEADER_TEMPLATE % dict(
|
||||
configs='', source_key='', suppression=crash_state))
|
||||
|
||||
|
@ -57,6 +57,8 @@ IGNORE_TEST_CASES = {
|
||||
IGNORE_OUTPUT = {
|
||||
'crbug.com/689877':
|
||||
re.compile(r'^.*SyntaxError: .*Stack overflow$', re.M),
|
||||
'_fake_difference_':
|
||||
re.compile(r'^.*___fake_difference___$', re.M),
|
||||
}
|
||||
|
||||
# Lines matching any of the following regular expressions will be ignored
|
||||
|
Loading…
Reference in New Issue
Block a user