From 6b53a8de82088f00e8d7f19831a84a8a61447a05 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Mon, 24 Oct 2022 02:35:52 +0200 Subject: [PATCH 1/6] examples: Clone example "examples" to "element_declarations" --- expat/CMakeLists.txt | 3 +- expat/examples/Makefile.am | 5 +- expat/examples/element_declarations.c | 121 ++++++++++++++++++++++++++ 3 files changed, 127 insertions(+), 2 deletions(-) create mode 100644 expat/examples/element_declarations.c diff --git a/expat/CMakeLists.txt b/expat/CMakeLists.txt index 2b4c13c5..0194bb94 100644 --- a/expat/CMakeLists.txt +++ b/expat/CMakeLists.txt @@ -579,10 +579,11 @@ endif() # C code examples # if(EXPAT_BUILD_EXAMPLES) + add_executable(element_declarations examples/element_declarations.c) add_executable(elements examples/elements.c) add_executable(outline examples/outline.c) - foreach(_target elements outline) + foreach(_target element_declarations elements outline) set_property(TARGET ${_target} PROPERTY RUNTIME_OUTPUT_DIRECTORY examples) target_link_libraries(${_target} expat) endforeach() diff --git a/expat/examples/Makefile.am b/expat/examples/Makefile.am index d386b592..bb5fa91a 100644 --- a/expat/examples/Makefile.am +++ b/expat/examples/Makefile.am @@ -31,7 +31,10 @@ AM_CPPFLAGS = @AM_CPPFLAGS@ -I$(srcdir)/../lib -noinst_PROGRAMS = elements outline +noinst_PROGRAMS = element_declarations elements outline + +element_declarations_SOURCES = element_declarations.c +element_declarations_LDADD = ../lib/libexpat.la elements_SOURCES = elements.c elements_LDADD = ../lib/libexpat.la diff --git a/expat/examples/element_declarations.c b/expat/examples/element_declarations.c new file mode 100644 index 00000000..5098d7d9 --- /dev/null +++ b/expat/examples/element_declarations.c @@ -0,0 +1,121 @@ +/* This is simple demonstration of how to use expat. This program + reads an XML document from standard input and writes a line with + the name of each element to standard output indenting child + elements by one tab stop more than their parent element. + It must be used with Expat compiled for UTF-8 output. + __ __ _ + ___\ \/ /_ __ __ _| |_ + / _ \\ /| '_ \ / _` | __| + | __// \| |_) | (_| | |_ + \___/_/\_\ .__/ \__,_|\__| + |_| XML parser + + Copyright (c) 1997-2000 Thai Open Source Software Center Ltd + Copyright (c) 2001-2003 Fred L. Drake, Jr. + Copyright (c) 2004-2006 Karl Waclawek + Copyright (c) 2005-2007 Steven Solie + Copyright (c) 2016-2019 Sebastian Pipping + Copyright (c) 2017 Rhodri James + Copyright (c) 2019 Zhongyuan Zhou + 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. +*/ + +#include +#include + +#ifdef XML_LARGE_SIZE +# define XML_FMT_INT_MOD "ll" +#else +# define XML_FMT_INT_MOD "l" +#endif + +#ifdef XML_UNICODE_WCHAR_T +# define XML_FMT_STR "ls" +#else +# define XML_FMT_STR "s" +#endif + +static void XMLCALL +startElement(void *userData, const XML_Char *name, const XML_Char **atts) { + int i; + int *const depthPtr = (int *)userData; + (void)atts; + + for (i = 0; i < *depthPtr; i++) + putchar('\t'); + printf("%" XML_FMT_STR "\n", name); + *depthPtr += 1; +} + +static void XMLCALL +endElement(void *userData, const XML_Char *name) { + int *const depthPtr = (int *)userData; + (void)name; + + *depthPtr -= 1; +} + +int +main(void) { + XML_Parser parser = XML_ParserCreate(NULL); + int done; + int depth = 0; + + if (! parser) { + fprintf(stderr, "Couldn't allocate memory for parser\n"); + return 1; + } + + XML_SetUserData(parser, &depth); + XML_SetElementHandler(parser, startElement, endElement); + + do { + void *const buf = XML_GetBuffer(parser, BUFSIZ); + if (! buf) { + fprintf(stderr, "Couldn't allocate memory for buffer\n"); + XML_ParserFree(parser); + return 1; + } + + const size_t len = fread(buf, 1, BUFSIZ, stdin); + + if (ferror(stdin)) { + fprintf(stderr, "Read error\n"); + XML_ParserFree(parser); + return 1; + } + + done = feof(stdin); + + if (XML_ParseBuffer(parser, (int)len, done) == XML_STATUS_ERROR) { + fprintf(stderr, + "Parse error at line %" XML_FMT_INT_MOD "u:\n%" XML_FMT_STR "\n", + XML_GetCurrentLineNumber(parser), + XML_ErrorString(XML_GetErrorCode(parser))); + XML_ParserFree(parser); + return 1; + } + } while (! done); + + XML_ParserFree(parser); + return 0; +} From 7483d04a189694cd2e31cdce501f2366606ff221 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Mon, 24 Oct 2022 02:59:22 +0200 Subject: [PATCH 2/6] examples/element_declarations.c: Handle element declarations rather than elements --- expat/examples/element_declarations.c | 92 +++++++++++++++++++++------ 1 file changed, 71 insertions(+), 21 deletions(-) diff --git a/expat/examples/element_declarations.c b/expat/examples/element_declarations.c index 5098d7d9..0e14afb3 100644 --- a/expat/examples/element_declarations.c +++ b/expat/examples/element_declarations.c @@ -1,7 +1,5 @@ -/* This is simple demonstration of how to use expat. This program - reads an XML document from standard input and writes a line with - the name of each element to standard output indenting child - elements by one tab stop more than their parent element. +/* Read an XML document from standard input and print + element declarations (if any) to standard output. It must be used with Expat compiled for UTF-8 output. __ __ _ ___\ \/ /_ __ __ _| |_ @@ -54,39 +52,91 @@ # define XML_FMT_STR "s" #endif -static void XMLCALL -startElement(void *userData, const XML_Char *name, const XML_Char **atts) { - int i; - int *const depthPtr = (int *)userData; - (void)atts; +static char * +contentTypeName(enum XML_Content_Type contentType) { + switch (contentType) { + case XML_CTYPE_EMPTY: + return "EMPTY"; + case XML_CTYPE_ANY: + return "ANY"; + case XML_CTYPE_MIXED: + return "MIXED"; + case XML_CTYPE_NAME: + return "NAME"; + case XML_CTYPE_CHOICE: + return "CHOICE"; + case XML_CTYPE_SEQ: + return "SEQ"; + default: + return "???"; + } +} - for (i = 0; i < *depthPtr; i++) - putchar('\t'); - printf("%" XML_FMT_STR "\n", name); - *depthPtr += 1; +static char * +contentQuantName(enum XML_Content_Quant contentQuant) { + switch (contentQuant) { + case XML_CQUANT_NONE: + return "NONE"; + case XML_CQUANT_OPT: + return "OPT"; + case XML_CQUANT_REP: + return "REP"; + case XML_CQUANT_PLUS: + return "PLUS"; + default: + return "???"; + } +} + +static void +dumpContentModel(const XML_Content *model, unsigned level, + const XML_Content *root) { + // Indent + unsigned u = 0; + for (; u < level; u++) { + printf(" "); + } + + // Node + printf("[%u] type=%s(%d), quant=%s(%d)", (unsigned)(model - root), + contentTypeName(model->type), model->type, + contentQuantName(model->quant), model->quant); + if (model->name) { + printf(", name=\"%" XML_FMT_STR "\"", model->name); + } else { + printf(", name=NULL"); + } + printf(", numchildren=%d", model->numchildren); + printf("\n"); + + // Children + for (u = 0; u < model->numchildren; u++) { + dumpContentModel(model->children + u, level + 1, root); + } } static void XMLCALL -endElement(void *userData, const XML_Char *name) { - int *const depthPtr = (int *)userData; - (void)name; - - *depthPtr -= 1; +handleElementDeclaration(void *userData, const XML_Char *name, + XML_Content *model) { + XML_Parser parser = (XML_Parser)userData; + printf("Element \"%" XML_FMT_STR "\":\n", name); + dumpContentModel(model, 1, model); + printf("\n"); + XML_FreeContentModel(parser, model); } int main(void) { XML_Parser parser = XML_ParserCreate(NULL); int done; - int depth = 0; if (! parser) { fprintf(stderr, "Couldn't allocate memory for parser\n"); return 1; } - XML_SetUserData(parser, &depth); - XML_SetElementHandler(parser, startElement, endElement); + XML_SetUserData(parser, parser); + XML_SetElementDeclHandler(parser, handleElementDeclaration); do { void *const buf = XML_GetBuffer(parser, BUFSIZ); From 48bc51fcf118a6c7441f42f00ae9e2bb7dce848c Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Sun, 30 Oct 2022 17:44:32 +0100 Subject: [PATCH 3/6] examples/element_declarations.c: Resolve recursion --- expat/examples/element_declarations.c | 78 +++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/expat/examples/element_declarations.c b/expat/examples/element_declarations.c index 0e14afb3..ca94c3c1 100644 --- a/expat/examples/element_declarations.c +++ b/expat/examples/element_declarations.c @@ -37,7 +37,9 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include #include +#include #include #ifdef XML_LARGE_SIZE @@ -52,6 +54,33 @@ # define XML_FMT_STR "s" #endif +// While traversing the XML_Content tree, we avoid recursion +// to not be vulnerable to a denial of service attack. +typedef struct StackStruct { + const XML_Content *model; + unsigned level; + struct StackStruct *prev; +} Stack; + +static Stack * +stackPushMalloc(Stack *stackTop, const XML_Content *model, unsigned level) { + Stack *const newStackTop = malloc(sizeof(Stack)); + if (! newStackTop) { + return NULL; + } + newStackTop->model = model; + newStackTop->level = level; + newStackTop->prev = stackTop; + return newStackTop; +} + +static Stack * +stackPopFree(Stack *stackTop) { + Stack *const newStackTop = stackTop->prev; + free(stackTop); + return newStackTop; +} + static char * contentTypeName(enum XML_Content_Type contentType) { switch (contentType) { @@ -89,8 +118,8 @@ contentQuantName(enum XML_Content_Quant contentQuant) { } static void -dumpContentModel(const XML_Content *model, unsigned level, - const XML_Content *root) { +dumpContentModelElement(const XML_Content *model, unsigned level, + const XML_Content *root) { // Indent unsigned u = 0; for (; u < level; u++) { @@ -108,21 +137,47 @@ dumpContentModel(const XML_Content *model, unsigned level, } printf(", numchildren=%d", model->numchildren); printf("\n"); +} - // Children - for (u = 0; u < model->numchildren; u++) { - dumpContentModel(model->children + u, level + 1, root); +static bool +dumpContentModel(const XML_Char *name, const XML_Content *root) { + printf("Element \"%" XML_FMT_STR "\":\n", name); + Stack *stackTop = NULL; + stackTop = stackPushMalloc(stackTop, root, 1); + if (! stackTop) { + return false; } + + while (stackTop) { + const XML_Content *const model = stackTop->model; + const unsigned level = stackTop->level; + + dumpContentModelElement(model, level, root); + + stackTop = stackPopFree(stackTop); + + for (size_t u = model->numchildren; u >= 1; u--) { + stackTop + = stackPushMalloc(stackTop, model->children + (u - 1), level + 1); + if (! stackTop) { + return false; + } + } + } + + printf("\n"); + return true; } static void XMLCALL handleElementDeclaration(void *userData, const XML_Char *name, XML_Content *model) { XML_Parser parser = (XML_Parser)userData; - printf("Element \"%" XML_FMT_STR "\":\n", name); - dumpContentModel(model, 1, model); - printf("\n"); + const bool success = dumpContentModel(name, model); XML_FreeContentModel(parser, model); + if (! success) { + XML_StopParser(parser, /* resumable= */ XML_FALSE); + } } int @@ -157,10 +212,13 @@ main(void) { done = feof(stdin); if (XML_ParseBuffer(parser, (int)len, done) == XML_STATUS_ERROR) { + enum XML_Error errorCode = XML_GetErrorCode(parser); + if (errorCode == XML_ERROR_ABORTED) { + errorCode = XML_ERROR_NO_MEMORY; + } fprintf(stderr, "Parse error at line %" XML_FMT_INT_MOD "u:\n%" XML_FMT_STR "\n", - XML_GetCurrentLineNumber(parser), - XML_ErrorString(XML_GetErrorCode(parser))); + XML_GetCurrentLineNumber(parser), XML_ErrorString(errorCode)); XML_ParserFree(parser); return 1; } From 59fea9dd51aeda939912b1206d7ba1ffdab8b3cd Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Sun, 30 Oct 2022 17:43:10 +0100 Subject: [PATCH 4/6] Sync file headers --- expat/examples/Makefile.am | 4 ++-- expat/examples/element_declarations.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/expat/examples/Makefile.am b/expat/examples/Makefile.am index bb5fa91a..e2e22bce 100644 --- a/expat/examples/Makefile.am +++ b/expat/examples/Makefile.am @@ -6,8 +6,8 @@ # \___/_/\_\ .__/ \__,_|\__| # |_| XML parser # -# Copyright (c) 2017 Sebastian Pipping -# Copyright (c) 2020 Jeffrey Walton +# Copyright (c) 2017-2022 Sebastian Pipping +# Copyright (c) 2020 Jeffrey Walton # Licensed under the MIT license: # # Permission is hereby granted, free of charge, to any person obtaining diff --git a/expat/examples/element_declarations.c b/expat/examples/element_declarations.c index ca94c3c1..437b0996 100644 --- a/expat/examples/element_declarations.c +++ b/expat/examples/element_declarations.c @@ -12,7 +12,7 @@ Copyright (c) 2001-2003 Fred L. Drake, Jr. Copyright (c) 2004-2006 Karl Waclawek Copyright (c) 2005-2007 Steven Solie - Copyright (c) 2016-2019 Sebastian Pipping + Copyright (c) 2016-2022 Sebastian Pipping Copyright (c) 2017 Rhodri James Copyright (c) 2019 Zhongyuan Zhou Licensed under the MIT license: From ad697455cbc217bb053de1036156c7ca22150657 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Tue, 1 Nov 2022 21:43:20 +0100 Subject: [PATCH 5/6] cmake: Resolve duplication related to building examples --- expat/CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/expat/CMakeLists.txt b/expat/CMakeLists.txt index 0194bb94..72cf0b01 100644 --- a/expat/CMakeLists.txt +++ b/expat/CMakeLists.txt @@ -579,11 +579,8 @@ endif() # C code examples # if(EXPAT_BUILD_EXAMPLES) - add_executable(element_declarations examples/element_declarations.c) - add_executable(elements examples/elements.c) - add_executable(outline examples/outline.c) - foreach(_target element_declarations elements outline) + add_executable(${_target} examples/${_target}.c) set_property(TARGET ${_target} PROPERTY RUNTIME_OUTPUT_DIRECTORY examples) target_link_libraries(${_target} expat) endforeach() From e924330f98ef454f5ce2e294c72dd814cb96b4d8 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Tue, 1 Nov 2022 21:46:01 +0100 Subject: [PATCH 6/6] Changes: Document #673 --- expat/Changes | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/expat/Changes b/expat/Changes index e6717105..4032ef77 100644 --- a/expat/Changes +++ b/expat/Changes @@ -2,6 +2,10 @@ NOTE: We are looking for help with a few things: https://github.com/libexpat/libexpat/labels/help%20wanted If you can help, please get in touch. Thanks! +Release 2.5.1 xxx xxxxxxx xx xxxx + Other changes: + #673 examples: Add new example "element_declarations.c" + Release 2.5.0 Tue October 25 2022 Security fixes: #616 #649 #650 CVE-2022-43680 -- Fix heap use-after-free after overeager