
Summary: The parser class for stdio currently accepts different argument providers. In-tree this is only used for a fuzzer test, however, the proposed implementation of the GPU handling of printf / scanf will require custom argument handlers. This makes the current approach of using a preprocessor macro messier. This path proposed folding this logic into a template instantiation. The downside to this is that because the implementation of the parser class is placed into an implementation file we need to manually instantiate the needed templates which will slightly bloat binary size. Alternatively we could remove the implementation file, or key off of the `libc` external packaging macro so it is not present in the installed version.
71 lines
2.2 KiB
C++
71 lines
2.2 KiB
C++
//===-- 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 "src/__support/arg_list.h"
|
|
#include "src/stdio/printf_core/parser.h"
|
|
|
|
#include <stdarg.h>
|
|
#include <stdint.h>
|
|
|
|
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<internal::MockArgList>(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;
|
|
}
|