Files
carbon-lang/toolchain/driver/driver_fuzzer.cpp
T
Jon Ross-Perkins 4f024410f7 Add stdin to driver's streams, and refactor stream passing (#4812)
The language server needs stdin, and for tests we should be passing it
around. My intent is to pass in a faux stdin to Driver for language
server tests.

As long as I'm adding a new parameter, I was looking at also changing
the way streams are passed in to Driver for style (pointers since
they're held past construction lifetime). Since these are all stored in
DriverEnv, I thought it might be a net improvement to use the struct
directly, getting more explicit parameter names and also removing the
need for `SetFuzzing`.

I'm trying here to avoid functional changes, but there are a couple
additional fixes like removing an obsolete `find_insensitive` and
refactoring how `ValidateOptions` handles errors (because it reduces the
number of spots that operate on error_stream).
2025-01-21 16:52:05 +00:00

101 lines
3.2 KiB
C++

// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include <cstring>
#include <string>
#include "common/exe_path.h"
#include "common/raw_string_ostream.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/raw_ostream.h"
#include "testing/fuzzing/libfuzzer.h"
#include "toolchain/driver/driver.h"
#include "toolchain/install/install_paths.h"
namespace Carbon::Testing {
static const InstallPaths* install_paths = nullptr;
// NOLINTNEXTLINE(readability-non-const-parameter): External API required types.
extern "C" auto LLVMFuzzerInitialize(int* argc, char*** argv) -> int {
CARBON_CHECK(*argc >= 1, "Need the `argv[0]` value to initialize!");
install_paths = new InstallPaths(
InstallPaths::MakeForBazelRunfiles(FindExecutablePath((*argv)[0])));
return 0;
}
static auto Read(const unsigned char*& data, size_t& size, int& output)
-> bool {
if (size < sizeof(output)) {
return false;
}
std::memcpy(&output, data, sizeof(output));
size -= sizeof(output);
data += sizeof(output);
return true;
}
extern "C" auto LLVMFuzzerTestOneInput(const unsigned char* data, size_t size)
-> int {
// First use the data to compute the number of arguments. Note that for
// scaling reasons we don't allow 2^31 arguments, even empty ones. Simply
// creating the vector of those won't work. We limit this to 2^20 arguments
// total.
int num_args;
if (!Read(data, size, num_args) || num_args < 0 || num_args > (1 << 20)) {
return 0;
}
// Now use the data to compute the length of each argument. We don't want to
// exhaust all memory, so bound the search space to using 2^17 bytes of
// memory for the argument text itself.
size_t arg_length_sum = 0;
llvm::SmallVector<int> arg_lengths(num_args);
for (int& arg_length : arg_lengths) {
if (!Read(data, size, arg_length) || arg_length < 0) {
return 0;
}
arg_length_sum += arg_length;
if (arg_length_sum > (1 << 17)) {
return 0;
}
}
// Ensure we have enough data for all the arguments.
if (size < arg_length_sum) {
return 0;
}
// Lastly, read the contents of each argument out of the data.
llvm::SmallVector<llvm::StringRef> args;
args.reserve(num_args);
for (int arg_length : arg_lengths) {
args.push_back(
llvm::StringRef(reinterpret_cast<const char*>(data), arg_length));
data += arg_length;
size -= arg_length;
}
llvm::IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> fs =
new llvm::vfs::InMemoryFileSystem;
RawStringOstream error_stream;
llvm::raw_null_ostream null_ostream;
Driver driver({.fs = fs,
.installation = install_paths,
.input_stream = nullptr,
.output_stream = &null_ostream,
.error_stream = &error_stream,
.fuzzing = true});
if (!driver.RunCommand(args).success) {
auto str = error_stream.TakeStr();
if (llvm::StringRef(str).find("error:") == llvm::StringRef::npos) {
llvm::errs() << "No error message on a failure!\n";
return 1;
}
}
return 0;
}
} // namespace Carbon::Testing