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:
Michael Achenbach 2020-12-01 13:46:01 +01:00 committed by Commit Bot
parent 89ffd740cd
commit 886d7cfee4
4 changed files with 108 additions and 2 deletions

View File

@ -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;

View File

@ -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_

View File

@ -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))

View File

@ -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