Index: lib/Fuzzer/afl/afl_driver.h =================================================================== --- /dev/null +++ lib/Fuzzer/afl/afl_driver.h @@ -0,0 +1,17 @@ +//===- afl_driver.h - Header for the driver ---------------------*- C++ -* ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +//Header for (test) source files that use functions from afl_driver.cpp +//===----------------------------------------------------------------------===// + +#ifndef LLVM_FUZZER_AFL_AFL_DRIVER_H_ +#define LLVM_FUZZER_AFL_AFL_DRIVER_H_ + +void duplicate_stderr(); + +#endif // LLVM_FUZZER_AFL_AFL_DRIVER_H_ Index: lib/Fuzzer/afl/afl_driver.cpp =================================================================== --- lib/Fuzzer/afl/afl_driver.cpp +++ lib/Fuzzer/afl/afl_driver.cpp @@ -33,9 +33,10 @@ $AFL_HOME/afl-fuzz -i IN -o OUT ./a.out ################################################################################ */ + #include -#include #include +#include #include #include #include @@ -56,9 +57,28 @@ extern "C" void __afl_manual_init(); static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0]; +#ifndef AFL_DRIVER_TEST // Silence unused variable warnings when testing. // Input buffer. static const size_t kMaxAflInputSize = 1 << 20; static uint8_t AflInputBuf[kMaxAflInputSize]; +#endif + +// If the user asks us to duplicate stderr, then do it. +void duplicate_stderr() { + char* stderr_duplicate_filename = + getenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); + + if (!stderr_duplicate_filename) + return; + + FILE* stderr_duplicate_stream = + freopen(stderr_duplicate_filename, "a+", stderr); + + assert(stderr_duplicate_stream && + "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); +} + +#ifndef AFL_DRIVER_TEST int main(int argc, char **argv) { fprintf(stderr, "Running in AFl-fuzz mode\nUsage:\n" @@ -70,6 +90,8 @@ LLVMFuzzerInitialize(&argc, &argv); // Do any other expensive one-time initialization here. + duplicate_stderr(); + __afl_manual_init(); int N = 1000; @@ -88,3 +110,5 @@ } } } + +#endif Index: lib/Fuzzer/test/CMakeLists.txt =================================================================== --- lib/Fuzzer/test/CMakeLists.txt +++ lib/Fuzzer/test/CMakeLists.txt @@ -116,8 +116,11 @@ add_executable(LLVMFuzzer-Unittest FuzzerUnittest.cpp FuzzerFnAdapterUnittest.cpp + ../afl/afl_driver.cpp ) +target_compile_definitions(LLVMFuzzer-Unittest PRIVATE AFL_DRIVER_TEST=1) + target_link_libraries(LLVMFuzzer-Unittest gtest gtest_main Index: lib/Fuzzer/test/FuzzerUnittest.cpp =================================================================== --- lib/Fuzzer/test/FuzzerUnittest.cpp +++ lib/Fuzzer/test/FuzzerUnittest.cpp @@ -2,10 +2,15 @@ // License. See LICENSE.TXT for details. #include "FuzzerInternal.h" +#include "afl/afl_driver.h" #include "gtest/gtest.h" +#include +#include #include #include +#define TEST_DATA "testdata\n" + using namespace fuzzer; // For now, have LLVMFuzzerTestOneInput just to make it link. @@ -444,3 +449,81 @@ EXPECT_GT(Hist[i], TriesPerUnit / N / 3); } } + +// Test that duplicate_stderr() duplicates stderr to a file when +// AFL_DRIVER_STDERR_DUPLICATE_FILENAME is set to the name of a valid file. +TEST(DuplicateStderrTest, DuplicatesToValidFile) { + const char* DUPLICATE_FILENAME = "duplicated-stderr"; + // Setup: make sure the duplicate file is fresh and save a copy of stderr + // to restore. + unlink(DUPLICATE_FILENAME); + int stderr_backup_fd = dup(STDERR_FILENO); + + // Duplicate it. + setenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME", DUPLICATE_FILENAME, 1); + duplicate_stderr(); + + // Write TEST_DATA to duplicated stderr. + fprintf(stderr, TEST_DATA); + fflush(stderr); + + // Verify the test data was written. + FILE* duplicate_file = fopen(DUPLICATE_FILENAME, "r"); + ASSERT_TRUE(duplicate_file); + char file_data[sizeof(TEST_DATA) + 1] = {0}; + fread(file_data, 1, sizeof(TEST_DATA) + 1, duplicate_file); + ASSERT_EQ(strcmp(file_data, TEST_DATA), 0); + + // Tear down: Unset the duplicate setderr's environmental variable, + // close the files handles, restore stderr and delete the file. + unsetenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); + fclose(duplicate_file); + close(STDERR_FILENO); + dup2(stderr_backup_fd, STDERR_FILENO); + unlink(DUPLICATE_FILENAME); +} + +// Test that calling duplicate_stderr() causes an assertion to fail +// when AFL_DRIVER_STDERR_DUPLICATE_FILENAME is set to a path that cannot be +// written to. +TEST(DuplicateStderrTest, DiesWhenInvalidFilenameSet) { + // The root directory is not a valid file path for stderr to be duplicated + // to. + const char* INVALID_DUPLICATE_FILE = "/"; + + setenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME", INVALID_DUPLICATE_FILE, 1); + ASSERT_DEATH(duplicate_stderr(), ".*"); + + // Tear down: Unset the duplicate setderr's environmental variable. + unsetenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); +} + +// Return the number of open file descriptors. +int get_num_open_fds() { + int num_fds = 0; + int dtablesize = getdtablesize(); + + // Try to open every file descriptor. + for (int idx = 0; idx < dtablesize; idx++) { + int pos_fd = dup(idx); + // If a descriptor isn't associated with an open file, dup() will return -1. + if (pos_fd != -1) { + // If -1 isn't returned then we've opened a duplicate descriptor: close + // it. + close(pos_fd); + num_fds += 1; + } + } + return num_fds; +} + +// Test that duplicate_stderr() does not duplicate stderr when +// AFL_DRIVER_STDERR_DUPLICATE_FILENAME is not set. +TEST(DuplicateStderrTest, DoesNothingWhenNoFilenameSet) { + unsetenv("AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); + // The number of open file descriptors should be the same if + // calling duplicate_stderr() does nothing. + int pre_call_fd_count = get_num_open_fds(); + duplicate_stderr(); + ASSERT_EQ(get_num_open_fds(), pre_call_fd_count); +}