diff --git a/compiler-rt/lib/fuzzer/afl/afl_driver.cpp b/compiler-rt/lib/fuzzer/afl/afl_driver.cpp --- a/compiler-rt/lib/fuzzer/afl/afl_driver.cpp +++ b/compiler-rt/lib/fuzzer/afl/afl_driver.cpp @@ -31,12 +31,19 @@ rm -rf IN OUT; mkdir IN OUT; echo z > IN/z; $AFL_HOME/afl-fuzz -i IN -o OUT ./a.out ################################################################################ -AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this environment variable -*appends* stderr to the file specified. If the file does not exist, it is -created. This is useful for getting stack traces (when using ASAN for example) -or original error messages on hard to reproduce bugs. +Here are some environment variables that control afl_driver's behavior: + +AFL_DRIVER_STDERR_DUPLICATE_FILENAME: Setting this *appends* stderr to the file +specified. If the file does not exist, it is created. This is useful for getting +stack traces (when using ASAN for example) or original error messages on hard +to reproduce bugs. Note that any content written to stderr will be written to +this file instead of stderr's usual location. + +AFL_DRIVER_CLOSE_FD_MASK: Similar to libFuzzer's -close_fd_mask behavior option. +If 1, close stdout at startup. If 2 close stderr; if 3 close both. */ + #include #include #include @@ -97,7 +104,7 @@ // Notify AFL about deferred forkserver. static volatile char AFL_DEFER_FORKSVR[] = "##SIG_AFL_DEFER_FORKSRV##"; -extern "C" void __afl_manual_init(); +extern "C" void __afl_manual_init(); static volatile char suppress_warning1 = AFL_DEFER_FORKSVR[0]; // Input buffer. @@ -115,6 +122,67 @@ } } +// Keep track of where stderr content is being written to, so that +// dup_and_close_stderr can use the correct one. +FILE* output_file = stderr; + +void discard_output(int fd) { + FILE *temp = fopen("/dev/null", "w"); + if (!temp) + abort(); + dup2(fileno(temp), fd); + fclose(temp); +} + +void close_stdout() { discard_output(STDOUT_FILENO); } + +// Use this optionally defined function to output sanitizer messages even if +// user asks to close stderr. +__attribute__((weak, visibility("default"))) extern "C" void +__sanitizer_set_report_fd(void*); + +// Prevent the targeted code from writing to stderr's desired location but allow +// sanitizers to do so. +void dup_and_close_stderr() { + int output_fileno = fileno(output_file); + int output_fd = dup(output_fileno); + if (output_fd <= 0) + abort(); + + FILE *new_output_file = fdopen(output_fd, "w"); + if (!new_output_file) + abort(); + + if (!__sanitizer_set_report_fd) + return; + + __sanitizer_set_report_fd(reinterpret_cast(output_fd)); + discard_output(output_fileno); +} + +// TODO(metzman): Make the rest of this file obey LLVM's style. +void Printf(const char *Fmt, ...) { + va_list ap; + va_start(ap, Fmt); + vfprintf(output_file, Fmt, ap); + va_end(ap); + fflush(output_file); +} + +// Close stdout and/or stderr if user asks for it. +static void maybe_close_fd_mask() { + char *fd_mask_str = getenv("AFL_DRIVER_CLOSE_FD_MASK"); + if (!fd_mask_str) + return; + + int fd_mask = atoi(fd_mask_str); + if (fd_mask & 2) + dup_and_close_stderr(); + + if (fd_mask & 1) + close_stdout(); +} + // If the user asks us to duplicate stderr, then do it. static void maybe_duplicate_stderr() { char* stderr_duplicate_filename = @@ -123,7 +191,7 @@ if (!stderr_duplicate_filename) return; - FILE* stderr_duplicate_stream = + FILE *stderr_duplicate_stream = freopen(stderr_duplicate_filename, "a+", stderr); if (!stderr_duplicate_stream) { @@ -132,6 +200,7 @@ "Failed to duplicate stderr to AFL_DRIVER_STDERR_DUPLICATE_FILENAME"); abort(); } + output_file = stderr_duplicate_stream; } // Define LLVMFuzzerMutate to avoid link failures for targets that use it @@ -161,7 +230,7 @@ } int main(int argc, char **argv) { - fprintf(stderr, + Printf( "======================= INFO =========================\n" "This binary is built for AFL-fuzz.\n" "To run the target function on individual input(s) execute this:\n" @@ -174,12 +243,13 @@ "re-spawning the process (default: 1000)\n" "======================================================\n", argv[0], argv[0], argv[0]); + + maybe_duplicate_stderr(); + maybe_close_fd_mask(); if (LLVMFuzzerInitialize) LLVMFuzzerInitialize(&argc, &argv); // Do any other expensive one-time initialization here. - maybe_duplicate_stderr(); - if (!getenv("AFL_DRIVER_DONT_DEFER")) __afl_manual_init(); @@ -187,8 +257,7 @@ if (argc == 2 && argv[1][0] == '-') N = atoi(argv[1] + 1); else if(argc == 2 && (N = atoi(argv[1])) > 0) - fprintf(stderr, "WARNING: using the deprecated call style `%s %d`\n", - argv[0], N); + Printf("WARNING: using the deprecated call style `%s %d`\n", argv[0], N); else if (argc > 1) return ExecuteFilesOnyByOne(argc, argv); @@ -212,5 +281,5 @@ delete[] copy; } } - fprintf(stderr, "%s: successfully executed %d input(s)\n", argv[0], num_runs); + Printf("%s: successfully executed %d input(s)\n", argv[0], num_runs); } diff --git a/compiler-rt/test/fuzzer/AFLDriverCloseFdMaskTest.cpp b/compiler-rt/test/fuzzer/AFLDriverCloseFdMaskTest.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/fuzzer/AFLDriverCloseFdMaskTest.cpp @@ -0,0 +1,37 @@ +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +// Contains dummy functions used to avoid dependency on AFL. +#include +#include +#include + +extern "C" void __afl_manual_init() {} + +extern "C" int __afl_persistent_loop(unsigned int N) { + static int Count = N; + fprintf(stderr, "__afl_persistent_loop calle, Count = %d\n", Count); + if (Count--) + return 1; + return 0; +} + +// This declaration exists to prevent the Darwin linker +// from complaining about this being a missing weak symbol. +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { + fprintf(stderr, "LLVMFuzzerInitialize called\n"); + return 0; +} + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + puts("STDOUT MESSAGE"); + fflush(0); + fprintf(stderr, "STDERR MESSAGE\n"); + if (Size < 4) + return 0; + + int *Array = new int[100]; + delete[] Array; + return Array[Size]; +} diff --git a/compiler-rt/test/fuzzer/afl-driver-close-fd-mask.test b/compiler-rt/test/fuzzer/afl-driver-close-fd-mask.test new file mode 100644 --- /dev/null +++ b/compiler-rt/test/fuzzer/afl-driver-close-fd-mask.test @@ -0,0 +1,44 @@ +REQUIRES: linux +RUN: %no_fuzzer_cpp_compiler %S/AFLDriverCloseFdMaskTest.cpp %libfuzzer_src/afl/afl_driver.cpp -o %t-AFLDriverCloseFdMaskTest + +RUN: echo "%no_fuzzer_cpp_compiler %S/AFLDriverCloseFdMaskTest.cpp %libfuzzer_src/afl/afl_driver.cpp -o %t-AFLDriverCloseFdMaskTest" > /tmp/cmd +RUN: echo -n "abc" > %t.file1 +RUN: echo -n "abcd" > %t.file2 + +RUN: echo "%libfuzzer_src/afl/afl_driver.cpp" > /tmp/src + +; Test that not AFL_DRIVER_CLOSE_FD_MASK file isn't broken. +RUN: unset AFL_DRIVER_CLOSE_FD_MASK +RUN: %run %t-AFLDriverCloseFdMaskTest < %t.file1 2>&1 | FileCheck %s --check-prefix=PRINTS_ALL + +PRINTS_ALL: STDOUT MESSAGE +PRINTS_ALL: STDERR MESSAGE + +; Test that stdout is closed properly. +CHECK1-NOT: STDOUT MESSAGE +CHECK1: STDERR MESSAGE +RUN: AFL_DRIVER_CLOSE_FD_MASK=1 %run %t-AFLDriverCloseFdMaskTest < %t.file1 2>&1 | FileCheck %s --check-prefix=CHECK1 + +; Test that stderr is closed properly. +CHECK2: STDOUT MESSAGE +CHECK2-NOT: STDERR MESSAGE +RUN: AFL_DRIVER_CLOSE_FD_MASK=2 %run %t-AFLDriverCloseFdMaskTest < %t.file1 2>&1 | FileCheck %s --check-prefix=CHECK2 + +; Test that both are closed properly. +CHECK3-NOT: STDOUT MESSAGE +CHECK3-NOT: STDERR MESSAGE +RUN: AFL_DRIVER_CLOSE_FD_MASK=3 %run %t-AFLDriverCloseFdMaskTest < %t.file1 2>&1 | FileCheck %s --check-prefix=CHECK3 + +; Test that a stack is printed when we close stderr +CHECK4: STDOUT MESSAGE +CHECK4-NOT: STDERR MESSAGE +CHECK4: ERROR: AddressSanitizer +RUN: AFL_DRIVER_CLOSE_FD_MASK=2 not %run %t-AFLDriverCloseFdMaskTest < %t.file2 2>&1 | FileCheck %s --check-prefix=CHECK4 + +; Test that a stack is written to the stderr duplicate file when we close stderr +; and specify a duplicate. +CHECK5-NOT: STDERR MESSAGE +CHECK5: ERROR: AddressSanitizer +RUN: rm %t.stderr +RUN: AFL_DRIVER_STDERR_DUPLICATE_FILENAME=%t.stderr AFL_DRIVER_CLOSE_FD_MASK=2 not %run %t-AFLDriverCloseFdMaskTest < %t.file2 +RUN: cat %t.stderr | FileCheck %s --check-prefix=CHECK5 \ No newline at end of file