diff --git a/libc/fuzzing/CMakeLists.txt b/libc/fuzzing/CMakeLists.txt --- a/libc/fuzzing/CMakeLists.txt +++ b/libc/fuzzing/CMakeLists.txt @@ -3,4 +3,5 @@ add_subdirectory(math) add_subdirectory(stdlib) +add_subdirectory(stdio) add_subdirectory(string) diff --git a/libc/fuzzing/stdio/CMakeLists.txt b/libc/fuzzing/stdio/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/fuzzing/stdio/CMakeLists.txt @@ -0,0 +1,9 @@ +add_libc_fuzzer( + printf_parser_fuzz + SRCS + printf_parser_fuzz.cpp + DEPENDS + libc.src.stdio.printf_core.mock_parser + COMPILE_OPTIONS + -DLIBC_COPT_MOCK_ARG_LIST +) diff --git a/libc/fuzzing/stdio/printf_parser_fuzz.cpp b/libc/fuzzing/stdio/printf_parser_fuzz.cpp new file mode 100644 --- /dev/null +++ b/libc/fuzzing/stdio/printf_parser_fuzz.cpp @@ -0,0 +1,73 @@ +//===-- printf_parser_fuzz.cpp --------------------------------------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// +/// Fuzzing test for llvm-libc qsort implementation. +/// +//===----------------------------------------------------------------------===// + +#ifndef LIBC_COPT_MOCK_ARG_LIST +#error The printf Parser Fuzzer must be compiled with LIBC_COPT_MOCK_ARG_LIST, and the parser itself must also be compiled with that option when it's linked against the fuzzer. +#endif + +#include "src/__support/arg_list.h" +#include "src/stdio/printf_core/parser.h" + +#include +#include + +using namespace __llvm_libc; + +// The design for the printf parser fuzzer is fairly simple. The parser uses a +// mock arg list that will never fail, and is passed a randomized string. The +// format sections it outputs are checked against a count of the number of '%' +// signs are in the original string. This is a fairly basic test, and the main +// intent is to run this under sanitizers, which will check for buffer overruns. +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + char *in_str = new char[size + 1]; + + for (size_t i = 0; i < size; ++i) + in_str[i] = data[i]; + + in_str[size] = '\0'; + + auto mock_arg_list = internal::MockArgList(); + + auto parser = printf_core::Parser(in_str, mock_arg_list); + + int str_percent_count = 0; + + for (size_t i = 0; i < size && in_str[i] != '\0'; ++i) { + if (in_str[i] == '%') { + ++str_percent_count; + } + } + + int section_percent_count = 0; + + for (printf_core::FormatSection cur_section = parser.get_next_section(); + !cur_section.raw_string.empty(); + cur_section = parser.get_next_section()) { + if (cur_section.has_conv) { + ++section_percent_count; + if (cur_section.conv_name == '%') { + ++section_percent_count; + } + } else if (cur_section.raw_string[0] == '%') { + // If the conversion would be undefined, it's instead raw, but it still + // starts with a %. + ++section_percent_count; + } + } + + if (str_percent_count != section_percent_count) { + __builtin_trap(); + } + + delete[] in_str; + return 0; +} diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt --- a/libc/src/stdio/printf_core/CMakeLists.txt +++ b/libc/src/stdio/printf_core/CMakeLists.txt @@ -25,6 +25,25 @@ libc.src.__support.common ) +add_object_library( + mock_parser + SRCS + parser.cpp + HDRS + parser.h + DEPENDS + .core_structs + libc.src.__support.arg_list + libc.src.__support.ctype_utils + libc.src.__support.str_to_integer + libc.src.__support.CPP.bit + libc.src.__support.CPP.string_view + libc.src.__support.CPP.type_traits + libc.src.__support.common + COMPILE_OPTIONS + -DLIBC_COPT_MOCK_ARG_LIST +) + add_object_library( string_writer SRCS