blob: abdf401bfcd20bbcf1fc1a5ab1a7638c935a6d01 [file] [log] [blame] [edit]
// Copyright 2023 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/fuzzilli/fuzzilli.h"
#include "include/v8-extension.h"
#include "include/v8-primitive.h"
#include "include/v8-template.h"
#include "src/api/api.h"
#include "src/execution/isolate-inl.h"
#include "src/execution/isolate.h"
#include "src/fuzzilli/cov.h"
#include "src/sandbox/sandbox.h"
#include "src/sandbox/testing.h"
#ifdef V8_OS_LINUX
#include <signal.h>
#include <unistd.h>
#endif // V8_OS_LINUX
namespace v8 {
namespace internal {
v8::Local<v8::FunctionTemplate> FuzzilliExtension::GetNativeFunctionTemplate(
v8::Isolate* isolate, v8::Local<v8::String> str) {
return v8::FunctionTemplate::New(isolate, FuzzilliExtension::Fuzzilli);
}
// We have to assume that the fuzzer will be able to call this function e.g. by
// enumerating the properties of the global object and eval'ing them. As such
// this function is implemented in a way that requires passing some magic value
// as first argument (with the idea being that the fuzzer won't be able to
// generate this value) which then also acts as a selector for the operation
// to perform.
void FuzzilliExtension::Fuzzilli(const FunctionCallbackInfo<Value>& info) {
v8::Isolate* isolate = info.GetIsolate();
v8::String::Utf8Value operation(isolate, info[0]);
if (*operation == nullptr) {
return;
}
if (strcmp(*operation, "FUZZILLI_CRASH") == 0) {
auto arg = info[1]
->Int32Value(info.GetIsolate()->GetCurrentContext())
.FromMaybe(0);
switch (arg) {
case 0:
IMMEDIATE_CRASH();
break;
case 1:
CHECK(false);
break;
case 2:
DCHECK(false);
break;
case 3: {
// Access an invalid address.
// We want to use an "interesting" address for the access (instead of
// e.g. nullptr). In the (unlikely) case that the address is actually
// mapped, simply increment the pointer until it crashes.
// The cast ensures that this works correctly on both 32-bit and 64-bit.
Address addr = static_cast<Address>(0x414141414141ull);
char* ptr = reinterpret_cast<char*>(addr);
for (int i = 0; i < 1024; i++) {
*ptr = 'A';
ptr += 1 * i::MB;
}
break;
}
case 4: {
// Use-after-free, likely only crashes in ASan builds.
auto* vec = new std::vector<int>(4);
delete vec;
USE(vec->at(0));
break;
}
case 5: {
// Out-of-bounds access (1), likely only crashes in ASan or
// "hardened"/"safe" libc++ builds.
std::vector<int> vec(5);
USE(vec[5]);
break;
}
case 6: {
// Out-of-bounds access (2), likely only crashes in ASan builds.
std::vector<int> vec(6);
memset(vec.data(), 42, 0x100);
break;
}
case 7: {
if (i::v8_flags.hole_fuzzing) {
// This should crash with a segmentation fault only
// when --hole-fuzzing is used.
char* ptr = reinterpret_cast<char*>(0x414141414141ull);
for (int i = 0; i < 1024; i++) {
*ptr = 'A';
ptr += 1 * i::GB;
}
}
break;
}
case 8: {
// This allows Fuzzilli to check that DEBUG is defined, which should be
// the case if dcheck_always_on is set. This is useful for fuzzing as
// there are some integrity checks behind DEBUG.
#ifdef DEBUG
IMMEDIATE_CRASH();
#endif
break;
}
default:
break;
}
} else if (strcmp(*operation, "FUZZILLI_PRINT") == 0) {
static FILE* fzliout = fdopen(REPRL_DWFD, "w");
if (!fzliout) {
fprintf(
stderr,
"Fuzzer output channel not available, printing to stdout instead\n");
fzliout = stdout;
}
v8::String::Utf8Value string(isolate, info[1]);
if (*string == nullptr) {
return;
}
fprintf(fzliout, "%s\n", *string);
fflush(fzliout);
}
}
} // namespace internal
} // namespace v8