-
Notifications
You must be signed in to change notification settings - Fork 98
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add unit tests for known-problematic fuzz inputs
- Loading branch information
Showing
6 changed files
with
229 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
#pragma once | ||
#ifndef TEST_FUZZ_COMMON_H | ||
#define TEST_FUZZ_COMMON_H | ||
|
||
#ifdef RYML_SINGLE_HEADER | ||
#include <ryml_all.hpp> | ||
#else | ||
#include <c4/yml/std/std.hpp> | ||
#include <c4/yml/parse.hpp> | ||
#include <c4/yml/emit.hpp> | ||
#include <c4/yml/event_handler_tree.hpp> | ||
#include <c4/yml/parse_engine.def.hpp> | ||
#endif | ||
#include <test_suite/test_suite_events.hpp> | ||
#include <test_suite/test_suite_event_handler.hpp> | ||
#include <cstdio> | ||
|
||
#ifdef C4_EXCEPTIONS | ||
#include <stdexcept> | ||
#else | ||
#include <csetjmp> | ||
std::jmp_buf jmp_env = {}; | ||
c4::csubstr jmp_msg = {}; | ||
#endif | ||
|
||
|
||
#ifdef RYML_DBG | ||
#define _if_dbg(...) __VA_ARGS__ | ||
bool report_errors = true; | ||
#else | ||
#define _if_dbg(...) | ||
bool report_errors = false; | ||
#endif | ||
|
||
inline void report_error(const char* msg, size_t length, c4::yml::Location loc, FILE *f) | ||
{ | ||
if(!report_errors) | ||
return; | ||
if(!loc.name.empty()) | ||
{ | ||
fwrite(loc.name.str, 1, loc.name.len, f); | ||
fputc(':', f); | ||
} | ||
fprintf(f, "%zu:", loc.line); | ||
if(loc.col) | ||
fprintf(f, "%zu:", loc.col); | ||
if(loc.offset) | ||
fprintf(f, " (%zuB):", loc.offset); | ||
fputc(' ', f); | ||
fprintf(f, "%.*s\n", static_cast<int>(length), msg); | ||
fflush(f); | ||
} | ||
|
||
// watchout: VS2022 requires C4_NORETURN to come before inline | ||
C4_NORETURN inline void errcallback(const char *msg, size_t msg_len, c4::yml::Location location, void *) | ||
{ | ||
report_error(msg, msg_len, location, stderr); | ||
C4_IF_EXCEPTIONS( | ||
throw std::runtime_error({msg, msg_len}); | ||
, | ||
jmp_msg.assign(msg, msg_len); | ||
std::longjmp(jmp_env, 1); | ||
); | ||
} | ||
|
||
inline c4::yml::Callbacks create_custom_callbacks() | ||
{ | ||
c4::set_error_flags(c4::ON_ERROR_CALLBACK); | ||
c4::set_error_callback([](const char *msg, size_t msg_len){ | ||
errcallback(msg, msg_len, {}, nullptr); | ||
}); | ||
c4::yml::Callbacks callbacks = {}; | ||
callbacks.m_error = errcallback; | ||
return callbacks; | ||
} | ||
|
||
namespace c4 { | ||
namespace yml { | ||
|
||
inline int fuzztest_parse_emit(uint32_t case_number, csubstr src) | ||
{ | ||
C4_UNUSED(case_number); | ||
set_callbacks(create_custom_callbacks()); | ||
Tree tree(create_custom_callbacks()); | ||
bool parse_success = false; | ||
C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0)) | ||
{ | ||
RYML_ASSERT(tree.empty()); | ||
_if_dbg(_dbg_printf("in[{}]: [{}]~~~\n{}\n~~~\n", case_number, src.len, src); fflush(NULL)); | ||
parse_in_arena(src, &tree); | ||
parse_success = true; | ||
_if_dbg(print_tree("parsed tree", tree)); | ||
_if_dbg(_dbg_printf("in[{}]: [{}]~~~\n{}\n~~~\n", case_number, src.len, src); fflush(NULL)); | ||
std::string dst = emitrs_yaml<std::string>(tree); | ||
_if_dbg(_dbg_printf("emitted[{}]: [{}]~~~\n{}\n~~~\n", case_number, dst.size(), to_csubstr(dst)); fflush(NULL)); | ||
C4_DONT_OPTIMIZE(dst); | ||
C4_DONT_OPTIMIZE(parse_success); | ||
} | ||
C4_IF_EXCEPTIONS_(catch(std::exception const&), else) | ||
{ | ||
// if an exception leaks from here, it is likely because of a greedy noexcept | ||
_if_dbg(if(parse_success) print_tree("parsed tree", tree)); | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
|
||
inline int fuzztest_yaml_events(uint32_t case_number, csubstr src) | ||
{ | ||
C4_UNUSED(case_number); | ||
set_callbacks(create_custom_callbacks()); | ||
EventHandlerYamlStd::EventSink sink = {}; | ||
EventHandlerYamlStd handler(&sink, create_custom_callbacks()); | ||
ParseEngine<EventHandlerYamlStd> parser(&handler); | ||
std::string str(src.begin(), src.end()); | ||
C4_IF_EXCEPTIONS_(try, if(setjmp(jmp_env) == 0)) | ||
{ | ||
_if_dbg(_dbg_printf("in[{}]: [{}]~~~\n{}\n~~~\n", case_number, src.len, src); fflush(NULL)); | ||
parser.parse_in_place_ev("input", c4::to_substr(str)); | ||
_if_dbg(_dbg_printf("evts[{}]: ~~~\n{}\n~~~\n", case_number, sink); fflush(NULL)); | ||
C4_DONT_OPTIMIZE(sink); | ||
} | ||
C4_IF_EXCEPTIONS_(catch(std::exception const&), else) | ||
{ | ||
// if an exception leaks from here, it is likely because of a greedy noexcept | ||
_if_dbg(fprintf(stdout, "err\n"); fflush(NULL)); | ||
return 1; | ||
} | ||
return 0; | ||
} | ||
|
||
} // namespace yml | ||
} // namespace c4 | ||
|
||
#endif /* TEST_FUZZ_COMMON_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#include "./test_fuzz_common.hpp" | ||
#include <atomic> | ||
|
||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *str, size_t len) | ||
{ | ||
static std::atomic<uint32_t> case_number{0}; | ||
c4::csubstr src = {reinterpret_cast<const char*>(str), len}; | ||
return c4::yml::fuzztest_yaml_events(case_number++, src); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
#include <stdio.h> | ||
#include <c4/fs/fs.hpp> | ||
|
||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *str, size_t len); | ||
|
||
int main(int argc, const char *argv[]) | ||
{ | ||
if(argc < 2) | ||
return 1; | ||
const char *filename = argv[1]; | ||
if(!c4::fs::file_exists(filename)) | ||
return 1; | ||
std::string file = c4::fs::file_get_contents<std::string>(filename); | ||
(void)LLVMFuzzerTestOneInput(reinterpret_cast<const uint8_t*>(&file[0]), file.size()); | ||
return 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#include "./test_fuzz_common.hpp" | ||
#include <atomic> | ||
|
||
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *str, size_t len) | ||
{ | ||
static std::atomic<uint32_t> case_number{0}; | ||
c4::csubstr src = {reinterpret_cast<const char*>(str), len}; | ||
return c4::yml::fuzztest_parse_emit(case_number++, src); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters