diff --git a/.github/workflows/fuzzing.yml b/.github/workflows/fuzzing.yml index 86a5a661..0c6adf18 100644 --- a/.github/workflows/fuzzing.yml +++ b/.github/workflows/fuzzing.yml @@ -42,7 +42,7 @@ permissions: jobs: run_fuzzers: name: Run fuzzing regression tests - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 steps: - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 @@ -59,6 +59,15 @@ jobs: llvm-19 echo /usr/lib/llvm-19/bin >>"${GITHUB_PATH}" + - name: Install build dependencies + run: |- + set -x + sudo apt-get install --yes --no-install-recommends -V \ + libabsl-dev \ + liblzma-dev \ + libprotobuf-dev \ + protobuf-compiler + - name: Build Expat fuzzers run: | set -x -o pipefail @@ -79,13 +88,16 @@ jobs: # Tune compilation of fuzzers to use Clang with ASan and UBSan -DCMAKE_C_COMPILER=clang - -DCMAKE_C_FLAGS='-Wall -Wextra -pedantic -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common' + -DCMAKE_CXX_COMPILER=clang++ + -DCMAKE_{C,CXX}_FLAGS='-Wall -Wextra -pedantic -O1 -g -fsanitize=address,undefined -fno-sanitize-recover=all -fno-omit-frame-pointer -fno-common' -DCMAKE_{EXE,MODULE,SHARED}_LINKER_FLAGS='-g -fsanitize=address,undefined' -DEXPAT_WARNINGS_AS_ERRORS=ON ) cmake "${args[@]}" -S . -B build make -C build VERBOSE=1 -j$(nproc) + ./build/fuzz/xml_lpm_fuzzer -help=1 + - name: Download and extract Expat fuzzing corpora run: |- set -x diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 58989fad..e92fc5af 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -5,7 +5,7 @@ # \___/_/\_\ .__/ \__,_|\__| # |_| XML parser # -# Copyright (c) 2021-2024 Sebastian Pipping +# Copyright (c) 2021-2025 Sebastian Pipping # Copyright (c) 2023 Joyce Brum # Copyright (c) 2023 Hanno Böck # Copyright (c) 2024 Dag-Erling Smørgrav @@ -52,8 +52,6 @@ jobs: - MODE: distcheck - MODE: qa-sh FLAT_ENV: CC=clang CXX=clang++ LD=clang++ QA_SANITIZER=address - - MODE: cmake-oos - FLAT_ENV: CMAKE_ARGS="-DEXPAT_ATTR_INFO=ON -DEXPAT_BUILD_FUZZERS=ON -DCMAKE_C_FLAGS=-fsanitize=address -DCMAKE_CXX_FLAGS=-fsanitize=address -DCMAKE_EXE_LINKER_FLAGS=-fsanitize=address -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++" - MODE: qa-sh FLAT_ENV: CC=clang CXX=clang++ LD=clang++ QA_SANITIZER=address CMAKE_ARGS=-DEXPAT_ATTR_INFO=ON - MODE: qa-sh diff --git a/expat/CMakeLists.txt b/expat/CMakeLists.txt index 1afc9a22..1e6dcdb7 100644 --- a/expat/CMakeLists.txt +++ b/expat/CMakeLists.txt @@ -30,6 +30,7 @@ # Copyright (c) 2020 Thomas Beutlich # Copyright (c) 2021 Alex Richardson # Copyright (c) 2022 Johnny Jazeix +# Copyright (c) 2022 Mark Brand # Copyright (c) 2022 David Faure # Unlike most of Expat, # this file is copyrighted under the BSD-license for buildsystem files of KDE. @@ -169,11 +170,15 @@ if(NOT _EXPAT_HELP) mark_as_advanced(_EXPAT_M32) endif() -if(EXPAT_BUILD_TESTS) +if(EXPAT_BUILD_TESTS OR EXPAT_BUILD_FUZZERS) # We have to call enable_language() before modifying any CMAKE_CXX_* variables enable_language(CXX) - set(CMAKE_CXX_STANDARD 11) + if (EXPAT_BUILD_FUZZERS) + set(CMAKE_CXX_STANDARD 17) # for std::string_view for Abseil for libprotobuf-mutator + else() + set(CMAKE_CXX_STANDARD 11) + endif() set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) # i.e. -std=c++11 rather than default -std=gnu++11 endif() @@ -755,6 +760,55 @@ if(EXPAT_BUILD_FUZZERS) TARGET ${target_name} PROPERTY RUNTIME_OUTPUT_DIRECTORY fuzz) endforeach() endforeach() + + find_package(absl REQUIRED) + find_package(Protobuf REQUIRED) + + # Only include libprotobuf-mutator here so we don't build it in non-fuzz + # configurations. + include(ExternalProject) + + set(ProtobufMutator_PREFIX libprotobuf-mutator) + set(ProtobufMutator_PATH ${CMAKE_CURRENT_BINARY_DIR}/${ProtobufMutator_PREFIX}/src/${ProtobufMutator_PREFIX}) + set(ProtobufMutator_BUILD_PATH ${ProtobufMutator_PATH}-build) + set(ProtobufMutator_INCLUDE_DIR ${ProtobufMutator_PATH}) + set(ProtobufMutator_LIBRARIES ${ProtobufMutator_BUILD_PATH}/src/libfuzzer/libprotobuf-mutator-libfuzzer.a ${ProtobufMutator_BUILD_PATH}/src/libprotobuf-mutator.a) + + ExternalProject_Add( + ${ProtobufMutator_PREFIX} + PREFIX ${ProtobufMutator_PREFIX} + GIT_REPOSITORY https://github.com/google/libprotobuf-mutator.git + GIT_TAG 50ef9159bb838931d83e5bbd77eeec05fc50f57d # v1.1-7-g50ef915, last before commit "Switch to requires_utf8_validation" + CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + CMAKE_CACHE_ARGS -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} -DLIB_PROTO_MUTATOR_TESTING:BOOL=OFF + BUILD_BYPRODUCTS ${ProtobufMutator_LIBRARIES} + PATCH_COMMAND sed -e "s,-Werror, ," -e "s,CMAKE_CXX_STANDARD [0-9]\\+,CMAKE_CXX_STANDARD 17," -i.bak ${ProtobufMutator_PATH}/CMakeLists.txt + UPDATE_COMMAND true + INSTALL_COMMAND true) + + protobuf_generate_cpp(XML_LPM_FUZZER_PROTO_SRCS + XML_LPM_FUZZER_PROTO_HDRS + fuzz/xml_lpm_fuzzer.proto) + + add_executable(xml_lpm_fuzzer + fuzz/xml_lpm_fuzzer.cpp + ${XML_LPM_FUZZER_PROTO_SRCS}) + target_include_directories(xml_lpm_fuzzer PUBLIC ${ProtobufMutator_INCLUDE_DIR}) + target_link_libraries(xml_lpm_fuzzer + fuzzpat + ${absl_LIBRARIES} + ${Protobuf_LIBRARIES} + ${ProtobufMutator_LIBRARIES}) + add_dependencies(xml_lpm_fuzzer ${ProtobufMutator_PREFIX}) + + # NOTE: Avoiding target_link_options here only because it needs CMake >=3.13 + if(EXPAT_OSSFUZZ_BUILD) + set_target_properties(xml_lpm_fuzzer PROPERTIES LINK_FLAGS $ENV{LIB_FUZZING_ENGINE}) + else() + target_compile_options(xml_lpm_fuzzer PRIVATE -fsanitize=fuzzer) + set_target_properties(xml_lpm_fuzzer PROPERTIES LINK_FLAGS -fsanitize=fuzzer) + endif() + set_property(TARGET xml_lpm_fuzzer PROPERTY RUNTIME_OUTPUT_DIRECTORY fuzz) else() if(EXPAT_OSSFUZZ_BUILD) message(SEND_ERROR diff --git a/expat/fuzz/xml_lpm_fuzzer.cpp b/expat/fuzz/xml_lpm_fuzzer.cpp new file mode 100644 index 00000000..f52ea7b2 --- /dev/null +++ b/expat/fuzz/xml_lpm_fuzzer.cpp @@ -0,0 +1,464 @@ +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2022 Mark Brand + Copyright (c) 2025 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#if defined(NDEBUG) +# undef NDEBUG // because checks below rely on assert(...) +#endif + +#include +#include +#include + +#include "expat.h" +#include "xml_lpm_fuzzer.pb.h" +#include "src/libfuzzer/libfuzzer_macro.h" + +static const char *g_encoding = nullptr; +static const char *g_external_entity = nullptr; +static size_t g_external_entity_size = 0; + +void +SetEncoding(const xml_lpm_fuzzer::Encoding &e) { + switch (e) { + case xml_lpm_fuzzer::Encoding::UTF8: + g_encoding = "UTF-8"; + break; + + case xml_lpm_fuzzer::Encoding::UTF16: + g_encoding = "UTF-16"; + break; + + case xml_lpm_fuzzer::Encoding::ISO88591: + g_encoding = "ISO-8859-1"; + break; + + case xml_lpm_fuzzer::Encoding::ASCII: + g_encoding = "US-ASCII"; + break; + + case xml_lpm_fuzzer::Encoding::NONE: + g_encoding = NULL; + break; + + default: + g_encoding = "UNKNOWN"; + break; + } +} + +static int g_allocation_count = 0; +static std::vector g_fail_allocations = {}; + +void * +MallocHook(size_t size) { + g_allocation_count += 1; + for (auto index : g_fail_allocations) { + if (index == g_allocation_count) { + return NULL; + } + } + return malloc(size); +} + +void * +ReallocHook(void *ptr, size_t size) { + g_allocation_count += 1; + for (auto index : g_fail_allocations) { + if (index == g_allocation_count) { + return NULL; + } + } + return realloc(ptr, size); +} + +void +FreeHook(void *ptr) { + free(ptr); +} + +XML_Memory_Handling_Suite memory_handling_suite + = {MallocHook, ReallocHook, FreeHook}; + +void InitializeParser(XML_Parser parser); + +// We want a parse function that supports resumption, so that we can cover the +// suspend/resume code. +enum XML_Status +Parse(XML_Parser parser, const char *input, int input_len, int is_final) { + enum XML_Status status = XML_Parse(parser, input, input_len, is_final); + while (status == XML_STATUS_SUSPENDED) { + status = XML_ResumeParser(parser); + } + return status; +} + +// When the fuzzer is compiled with instrumentation such as ASan, then the +// accesses in TouchString will fault if they access invalid memory (ie. detect +// either a use-after-free or buffer-overflow). By calling TouchString in each +// of the callbacks, we can check that the arguments meet the API specifications +// in terms of length/null-termination. no_optimize is used to ensure that the +// compiler has to emit actual memory reads, instead of removing them. +static volatile size_t no_optimize = 0; +static void +TouchString(const XML_Char *ptr, int len = -1) { + if (! ptr) { + return; + } + + if (len == -1) { + for (XML_Char value = *ptr++; value; value = *ptr++) { + no_optimize += value; + } + } else { + for (int i = 0; i < len; ++i) { + no_optimize += ptr[i]; + } + } +} + +static void +TouchNodeAndRecurse(XML_Content *content) { + switch (content->type) { + case XML_CTYPE_EMPTY: + case XML_CTYPE_ANY: + assert(content->quant == XML_CQUANT_NONE); + assert(content->name == NULL); + assert(content->numchildren == 0); + assert(content->children == NULL); + break; + + case XML_CTYPE_MIXED: + assert(content->quant == XML_CQUANT_NONE + || content->quant == XML_CQUANT_REP); + assert(content->name == NULL); + for (unsigned int i = 0; i < content->numchildren; ++i) { + assert(content->children[i].type == XML_CTYPE_NAME); + assert(content->children[i].quant == XML_CQUANT_NONE); + assert(content->children[i].numchildren == 0); + assert(content->children[i].children == NULL); + TouchString(content->children[i].name); + } + break; + + case XML_CTYPE_NAME: + assert((content->quant == XML_CQUANT_NONE) + || (content->quant == XML_CQUANT_OPT) + || (content->quant == XML_CQUANT_REP) + || (content->quant == XML_CQUANT_PLUS)); + assert(content->numchildren == 0); + assert(content->children == NULL); + TouchString(content->name); + break; + + case XML_CTYPE_CHOICE: + case XML_CTYPE_SEQ: + assert((content->quant == XML_CQUANT_NONE) + || (content->quant == XML_CQUANT_OPT) + || (content->quant == XML_CQUANT_REP) + || (content->quant == XML_CQUANT_PLUS)); + assert(content->name == NULL); + for (unsigned int i = 0; i < content->numchildren; ++i) { + TouchNodeAndRecurse(&content->children[i]); + } + break; + + default: + assert(false); + } +} + +static void XMLCALL +ElementDeclHandler(void *userData, const XML_Char *name, XML_Content *model) { + TouchString(name); + TouchNodeAndRecurse(model); + XML_FreeContentModel((XML_Parser)userData, model); +} + +static void XMLCALL +AttlistDeclHandler(void *userData, const XML_Char *elname, + const XML_Char *attname, const XML_Char *atttype, + const XML_Char *dflt, int isrequired) { + (void)userData; + TouchString(elname); + TouchString(attname); + TouchString(atttype); + TouchString(dflt); + (void)isrequired; +} + +static void XMLCALL +XmlDeclHandler(void *userData, const XML_Char *version, + const XML_Char *encoding, int standalone) { + (void)userData; + TouchString(version); + TouchString(encoding); + (void)standalone; +} + +static void XMLCALL +StartElementHandler(void *userData, const XML_Char *name, + const XML_Char **atts) { + (void)userData; + TouchString(name); + for (size_t i = 0; atts[i] != NULL; ++i) { + TouchString(atts[i]); + } +} + +static void XMLCALL +EndElementHandler(void *userData, const XML_Char *name) { + (void)userData; + TouchString(name); +} + +static void XMLCALL +CharacterDataHandler(void *userData, const XML_Char *s, int len) { + (void)userData; + TouchString(s, len); +} + +static void XMLCALL +ProcessingInstructionHandler(void *userData, const XML_Char *target, + const XML_Char *data) { + (void)userData; + TouchString(target); + TouchString(data); +} + +static void XMLCALL +CommentHandler(void *userData, const XML_Char *data) { + TouchString(data); + // Use the comment handler to trigger parser suspend, so that we can get + // coverage of that code. + XML_StopParser((XML_Parser)userData, XML_TRUE); +} + +static void XMLCALL +StartCdataSectionHandler(void *userData) { + (void)userData; +} + +static void XMLCALL +EndCdataSectionHandler(void *userData) { + (void)userData; +} + +static void XMLCALL +DefaultHandler(void *userData, const XML_Char *s, int len) { + (void)userData; + TouchString(s, len); +} + +static void XMLCALL +StartDoctypeDeclHandler(void *userData, const XML_Char *doctypeName, + const XML_Char *sysid, const XML_Char *pubid, + int has_internal_subset) { + (void)userData; + TouchString(doctypeName); + TouchString(sysid); + TouchString(pubid); + (void)has_internal_subset; +} + +static void XMLCALL +EndDoctypeDeclHandler(void *userData) { + (void)userData; +} + +static void XMLCALL +EntityDeclHandler(void *userData, const XML_Char *entityName, + int is_parameter_entity, const XML_Char *value, + int value_length, const XML_Char *base, + const XML_Char *systemId, const XML_Char *publicId, + const XML_Char *notationName) { + (void)userData; + TouchString(entityName); + (void)is_parameter_entity; + TouchString(value, value_length); + TouchString(base); + TouchString(systemId); + TouchString(publicId); + TouchString(notationName); +} + +static void XMLCALL +NotationDeclHandler(void *userData, const XML_Char *notationName, + const XML_Char *base, const XML_Char *systemId, + const XML_Char *publicId) { + (void)userData; + TouchString(notationName); + TouchString(base); + TouchString(systemId); + TouchString(publicId); +} + +static void XMLCALL +StartNamespaceDeclHandler(void *userData, const XML_Char *prefix, + const XML_Char *uri) { + (void)userData; + TouchString(prefix); + TouchString(uri); +} + +static void XMLCALL +EndNamespaceDeclHandler(void *userData, const XML_Char *prefix) { + (void)userData; + TouchString(prefix); +} + +static int XMLCALL +NotStandaloneHandler(void *userData) { + (void)userData; + return XML_STATUS_OK; +} + +static int XMLCALL +ExternalEntityRefHandler(XML_Parser parser, const XML_Char *context, + const XML_Char *base, const XML_Char *systemId, + const XML_Char *publicId) { + int rc = XML_STATUS_ERROR; + TouchString(context); + TouchString(base); + TouchString(systemId); + TouchString(publicId); + + if (g_external_entity) { + XML_Parser ext_parser + = XML_ExternalEntityParserCreate(parser, context, g_encoding); + rc = Parse(ext_parser, g_external_entity, g_external_entity_size, 1); + XML_ParserFree(ext_parser); + } + + return rc; +} + +static void XMLCALL +SkippedEntityHandler(void *userData, const XML_Char *entityName, + int is_parameter_entity) { + (void)userData; + TouchString(entityName); + (void)is_parameter_entity; +} + +static int XMLCALL +UnknownEncodingHandler(void *encodingHandlerData, const XML_Char *name, + XML_Encoding *info) { + (void)encodingHandlerData; + TouchString(name); + (void)info; + return XML_STATUS_ERROR; +} + +void +InitializeParser(XML_Parser parser) { + XML_SetUserData(parser, (void *)parser); + XML_SetHashSalt(parser, 0x41414141); + XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); + + XML_SetElementDeclHandler(parser, ElementDeclHandler); + XML_SetAttlistDeclHandler(parser, AttlistDeclHandler); + XML_SetXmlDeclHandler(parser, XmlDeclHandler); + XML_SetElementHandler(parser, StartElementHandler, EndElementHandler); + XML_SetCharacterDataHandler(parser, CharacterDataHandler); + XML_SetProcessingInstructionHandler(parser, ProcessingInstructionHandler); + XML_SetCommentHandler(parser, CommentHandler); + XML_SetCdataSectionHandler(parser, StartCdataSectionHandler, + EndCdataSectionHandler); + // XML_SetDefaultHandler disables entity expansion + XML_SetDefaultHandlerExpand(parser, DefaultHandler); + XML_SetDoctypeDeclHandler(parser, StartDoctypeDeclHandler, + EndDoctypeDeclHandler); + // Note: This is mutually exclusive with XML_SetUnparsedEntityDeclHandler, + // and there isn't any significant code change between the two. + XML_SetEntityDeclHandler(parser, EntityDeclHandler); + XML_SetNotationDeclHandler(parser, NotationDeclHandler); + XML_SetNamespaceDeclHandler(parser, StartNamespaceDeclHandler, + EndNamespaceDeclHandler); + XML_SetNotStandaloneHandler(parser, NotStandaloneHandler); + XML_SetExternalEntityRefHandler(parser, ExternalEntityRefHandler); + XML_SetSkippedEntityHandler(parser, SkippedEntityHandler); + XML_SetUnknownEncodingHandler(parser, UnknownEncodingHandler, (void *)parser); +} + +DEFINE_TEXT_PROTO_FUZZER(const xml_lpm_fuzzer::Testcase &testcase) { + g_external_entity = nullptr; + + if (! testcase.actions_size()) { + return; + } + + g_allocation_count = 0; + g_fail_allocations.clear(); + for (int i = 0; i < testcase.fail_allocations_size(); ++i) { + g_fail_allocations.push_back(testcase.fail_allocations(i)); + } + + SetEncoding(testcase.encoding()); + XML_Parser parser + = XML_ParserCreate_MM(g_encoding, &memory_handling_suite, "|"); + InitializeParser(parser); + + for (int i = 0; i < testcase.actions_size(); ++i) { + const auto &action = testcase.actions(i); + switch (action.action_case()) { + case xml_lpm_fuzzer::Action::kChunk: + if (XML_STATUS_ERROR + == Parse(parser, action.chunk().data(), action.chunk().size(), 0)) { + // Force a reset after parse error. + XML_ParserReset(parser, g_encoding); + InitializeParser(parser); + } + break; + + case xml_lpm_fuzzer::Action::kLastChunk: + Parse(parser, action.last_chunk().data(), action.last_chunk().size(), 1); + XML_ParserReset(parser, g_encoding); + InitializeParser(parser); + break; + + case xml_lpm_fuzzer::Action::kReset: + XML_ParserReset(parser, g_encoding); + InitializeParser(parser); + break; + + case xml_lpm_fuzzer::Action::kExternalEntity: + g_external_entity = action.external_entity().data(); + g_external_entity_size = action.external_entity().size(); + break; + + default: + break; + } + } + + XML_ParserFree(parser); +} diff --git a/expat/fuzz/xml_lpm_fuzzer.proto b/expat/fuzz/xml_lpm_fuzzer.proto new file mode 100644 index 00000000..ddc4e958 --- /dev/null +++ b/expat/fuzz/xml_lpm_fuzzer.proto @@ -0,0 +1,58 @@ +/* + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 2022 Mark Brand + Copyright (c) 2025 Sebastian Pipping + Licensed under the MIT license: + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to permit + persons to whom the Software is furnished to do so, subject to the + following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN + NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +syntax = "proto2"; +package xml_lpm_fuzzer; + +enum Encoding { + UTF8 = 0; + UTF16 = 1; + ISO88591 = 2; + ASCII = 3; + UNKNOWN = 4; + NONE = 5; +} + +message Action { + oneof action { + string chunk = 1; + string last_chunk = 2; + bool reset = 3; + string external_entity = 4; + } +} + +message Testcase { + required Encoding encoding = 1; + repeated Action actions = 2; + repeated int32 fail_allocations = 3; +}