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,17 @@ +add_header_library( + mock_arg_list + HDRS + mock_arg_list.h + DEPENDS + libc.src.__support.arg_list + libc.src.__support.common +) + +add_libc_fuzzer( + printf_parser_fuzz + SRCS + printf_parser_fuzz.cpp + DEPENDS + libc.src.stdio.printf_core.parser + .mock_arg_list +) diff --git a/libc/fuzzing/stdio/mock_arg_list.h b/libc/fuzzing/stdio/mock_arg_list.h new file mode 100644 --- /dev/null +++ b/libc/fuzzing/stdio/mock_arg_list.h @@ -0,0 +1,42 @@ +//===-- Mock arg list -------------------------------------------*- C++ -*-===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_FUZZING_STDIO_MOCK_ARG_LIST_H +#define LLVM_LIBC_FUZZING_STDIO_MOCK_ARG_LIST_H + +#include "src/__support/common.h" + +#include "src/__support/arg_list.h" + +namespace __llvm_libc { +namespace internal { +class MockArgList : public ArgList { + int arg_counter = 0; + +public: + LIBC_INLINE MockArgList(ArgList any_arg_list) : ArgList(any_arg_list){}; + LIBC_INLINE MockArgList(MockArgList &other) : ArgList(other) { + this->arg_counter = other.arg_counter; + } + LIBC_INLINE ~MockArgList() = default; + + LIBC_INLINE MockArgList &operator=(MockArgList &rhs) { + arg_counter = rhs.arg_counter; + return *this; + } + + template LIBC_INLINE T next_var() { + ++arg_counter; + return static_cast(arg_counter); + } +}; + +} // namespace internal +} // namespace __llvm_libc + +#endif // LLVM_LIBC_FUZZING_STDIO_MOCK_ARG_LIST_H 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,75 @@ +//===-- 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. +/// +//===----------------------------------------------------------------------===// + +#include "mock_arg_list.h" +#include "src/stdio/printf_core/parser.h" + +#include +#include + +using namespace __llvm_libc; + +int arglist_func(const char *in_str, size_t size, ...) { + + va_list vlist; + va_start(vlist, size); + auto arg_list = internal::ArgList(vlist); + va_end(vlist); + + auto mock_arg_list = internal::MockArgList(arg_list); + + 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(); + } + return 0; +} + +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'; + + int ret_val = arglist_func(in_str, size); + + delete[] in_str; + return ret_val; +}