diff options
Diffstat (limited to 'xmlwf/xmlwf.c')
| -rw-r--r-- | xmlwf/xmlwf.c | 186 |
1 files changed, 157 insertions, 29 deletions
diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c index 2d5a87e7d99d..29118bb64620 100644 --- a/xmlwf/xmlwf.c +++ b/xmlwf/xmlwf.c @@ -7,7 +7,16 @@ |_| XML parser Copyright (c) 1997-2000 Thai Open Source Software Center Ltd - Copyright (c) 2000-2017 Expat development team + Copyright (c) 2000 Clark Cooper <coopercc@users.sourceforge.net> + Copyright (c) 2001-2003 Fred L. Drake, Jr. <fdrake@users.sourceforge.net> + Copyright (c) 2004-2009 Karl Waclawek <karl@waclawek.net> + Copyright (c) 2005-2007 Steven Solie <ssolie@users.sourceforge.net> + Copyright (c) 2016-2021 Sebastian Pipping <sebastian@pipping.org> + Copyright (c) 2017 Rhodri James <rhodri@wildebeest.org.uk> + Copyright (c) 2019 David Loffredo <loffredo@steptools.com> + Copyright (c) 2020 Joe Orton <jorton@redhat.com> + Copyright (c) 2020 Kleber TarcĂsio <klebertarcisio@yahoo.com.br> + Copyright (c) 2021 Tim Bray <tbray@textuality.com> Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -30,11 +39,15 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. */ +#include <expat_config.h> + #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <string.h> +#include <math.h> /* for isnan */ +#include <errno.h> #include "expat.h" #include "codepage.h" @@ -50,6 +63,14 @@ # include <wchar.h> #endif +enum ExitCode { + XMLWF_EXIT_SUCCESS = 0, + XMLWF_EXIT_INTERNAL_ERROR = 1, + XMLWF_EXIT_NOT_WELLFORMED = 2, + XMLWF_EXIT_OUTPUT_ERROR = 3, + XMLWF_EXIT_USAGE_ERROR = 4, +}; + /* Structures for handler user data */ typedef struct NotationList { struct NotationList *next; @@ -322,6 +343,13 @@ freeNotations(XmlwfUserData *data) { data->notationListHead = NULL; } +static void +cleanupUserData(XmlwfUserData *userData) { + free((void *)userData->currentDoctypeName); + userData->currentDoctypeName = NULL; + freeNotations(userData); +} + static int xcscmp(const XML_Char *xs, const XML_Char *xt) { while (*xs != 0 && *xt != 0) { @@ -848,16 +876,19 @@ usage(const XML_Char *prog, int rc) { stderr, /* Generated with: * $ xmlwf/xmlwf_helpgen.sh + * To update, change xmlwf/xmlwf_helpgen.py, then paste the output of + * xmlwf/xmlwf_helpgen.sh in here. */ /* clang-format off */ - T("usage: %s [-s] [-n] [-p] [-x] [-e ENCODING] [-w] [-r] [-d DIRECTORY]\n") - T(" [-c | -m | -t] [-N]\n") - T(" [FILE [FILE ...]]\n") + T("usage:\n") + T(" %s [OPTIONS] [FILE ...]\n") + T(" %s -h\n") + T(" %s -v\n") T("\n") T("xmlwf - Determines if an XML document is well-formed\n") T("\n") T("positional arguments:\n") - T(" FILE files to process (default: STDIN)\n") + T(" FILE file to process (default: STDIN)\n") T("\n") T("input control arguments:\n") T(" -s print an error if the document is not [s]tandalone\n") @@ -867,6 +898,7 @@ usage(const XML_Char *prog, int rc) { T(" -e ENCODING override any in-document [e]ncoding declaration\n") T(" -w enable support for [W]indows code pages\n") T(" -r disable memory-mapping and use normal file [r]ead IO calls instead\n") + T(" -k when processing multiple files, [k]eep processing after first file with error\n") T("\n") T("output control arguments:\n") T(" -d DIRECTORY output [d]estination directory\n") @@ -875,14 +907,27 @@ usage(const XML_Char *prog, int rc) { T(" -t write no XML output for [t]iming of plain parsing\n") T(" -N enable adding doctype and [n]otation declarations\n") T("\n") + T("billion laughs attack protection:\n") + T(" NOTE: If you ever need to increase these values for non-attack payload, please file a bug report.\n") + T("\n") + T(" -a FACTOR set maximum tolerated [a]mplification factor (default: 100.0)\n") + T(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB)\n") + T("\n") T("info arguments:\n") T(" -h show this [h]elp message and exit\n") T(" -v show program's [v]ersion number and exit\n") T("\n") - T("libexpat is software libre, licensed under the MIT license.\n") + T("exit status:\n") + T(" 0 the input files are well-formed and the output (if requested) was written successfully\n") + T(" 1 could not allocate data structures, signals a serious problem with execution environment\n") + T(" 2 one or more input files were not well-formed\n") + T(" 3 could not create an output file\n") + T(" 4 command-line argument error\n") + T("\n") + T("xmlwf of libexpat is software libre, licensed under the MIT license.\n") T("Please report bugs at https://github.com/libexpat/libexpat/issues. Thank you!\n") , /* clang-format on */ - prog); + prog, prog, prog); exit(rc); } @@ -891,6 +936,19 @@ usage(const XML_Char *prog, int rc) { int wmain(int argc, XML_Char **argv); #endif +#define XMLWF_SHIFT_ARG_INTO(constCharStarTarget, argc, argv, i, j) \ + { \ + if (argv[i][j + 1] == T('\0')) { \ + if (++i == argc) \ + usage(argv[0], XMLWF_EXIT_USAGE_ERROR); \ + constCharStarTarget = argv[i]; \ + } else { \ + constCharStarTarget = argv[i] + j + 1; \ + } \ + i++; \ + j = 0; \ + } + int tmain(int argc, XML_Char **argv) { int i, j; @@ -902,6 +960,13 @@ tmain(int argc, XML_Char **argv) { int useNamespaces = 0; int requireStandalone = 0; int requiresNotations = 0; + int continueOnError = 0; + + float attackMaximumAmplification = -1.0f; /* signaling "not set" */ + unsigned long long attackThresholdBytes; + XML_Bool attackThresholdGiven = XML_FALSE; + + int exitCode = XMLWF_EXIT_SUCCESS; enum XML_ParamEntityParsing paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; int useStdin = 0; @@ -965,31 +1030,64 @@ tmain(int argc, XML_Char **argv) { j++; break; case T('d'): - if (argv[i][j + 1] == T('\0')) { - if (++i == argc) - usage(argv[0], 2); - outputDir = argv[i]; - } else - outputDir = argv[i] + j + 1; - i++; - j = 0; + XMLWF_SHIFT_ARG_INTO(outputDir, argc, argv, i, j); break; case T('e'): - if (argv[i][j + 1] == T('\0')) { - if (++i == argc) - usage(argv[0], 2); - encoding = argv[i]; - } else - encoding = argv[i] + j + 1; - i++; - j = 0; + XMLWF_SHIFT_ARG_INTO(encoding, argc, argv, i, j); break; case T('h'): - usage(argv[0], 0); + usage(argv[0], XMLWF_EXIT_SUCCESS); return 0; case T('v'): showVersion(argv[0]); return 0; + case T('k'): + continueOnError = 1; + j++; + break; + case T('a'): { + const XML_Char *valueText = NULL; + XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j); + + errno = 0; + XML_Char *afterValueText = (XML_Char *)valueText; + attackMaximumAmplification = tcstof(valueText, &afterValueText); + if ((errno != 0) || (afterValueText[0] != T('\0')) + || isnan(attackMaximumAmplification) + || (attackMaximumAmplification < 1.0f)) { + // This prevents tperror(..) from reporting misleading "[..]: Success" + errno = ERANGE; + tperror(T("invalid amplification limit") T( + " (needs a floating point number greater or equal than 1.0)")); + exit(XMLWF_EXIT_USAGE_ERROR); + } +#ifndef XML_DTD + ftprintf(stderr, T("Warning: Given amplification limit ignored") T( + ", xmlwf has been compiled without DTD support.\n")); +#endif + break; + } + case T('b'): { + const XML_Char *valueText = NULL; + XMLWF_SHIFT_ARG_INTO(valueText, argc, argv, i, j); + + errno = 0; + XML_Char *afterValueText = (XML_Char *)valueText; + attackThresholdBytes = tcstoull(valueText, &afterValueText, 10); + if ((errno != 0) || (afterValueText[0] != T('\0'))) { + // This prevents tperror(..) from reporting misleading "[..]: Success" + errno = ERANGE; + tperror(T("invalid ignore threshold") + T(" (needs an integer from 0 to 2^64-1)")); + exit(XMLWF_EXIT_USAGE_ERROR); + } + attackThresholdGiven = XML_TRUE; +#ifndef XML_DTD + ftprintf(stderr, T("Warning: Given attack threshold ignored") T( + ", xmlwf has been compiled without DTD support.\n")); +#endif + break; + } case T('\0'): if (j > 1) { i++; @@ -998,7 +1096,7 @@ tmain(int argc, XML_Char **argv) { } /* fall through */ default: - usage(argv[0], 2); + usage(argv[0], XMLWF_EXIT_USAGE_ERROR); } } if (i == argc) { @@ -1017,7 +1115,22 @@ tmain(int argc, XML_Char **argv) { if (! parser) { tperror(T("Could not instantiate parser")); - exit(1); + exit(XMLWF_EXIT_INTERNAL_ERROR); + } + + if (attackMaximumAmplification != -1.0f) { +#ifdef XML_DTD + XML_SetBillionLaughsAttackProtectionMaximumAmplification( + parser, attackMaximumAmplification); +#endif + } + if (attackThresholdGiven) { +#ifdef XML_DTD + XML_SetBillionLaughsAttackProtectionActivationThreshold( + parser, attackThresholdBytes); +#else + (void)attackThresholdBytes; // silence -Wunused-but-set-variable +#endif } if (requireStandalone) @@ -1051,13 +1164,24 @@ tmain(int argc, XML_Char **argv) { } outName = (XML_Char *)malloc((tcslen(outputDir) + tcslen(file) + 2) * sizeof(XML_Char)); + if (! outName) { + tperror(T("Could not allocate memory")); + exit(XMLWF_EXIT_INTERNAL_ERROR); + } tcscpy(outName, outputDir); tcscat(outName, delim); tcscat(outName, file); userData.fp = tfopen(outName, T("wb")); if (! userData.fp) { tperror(outName); - exit(1); + exitCode = XMLWF_EXIT_OUTPUT_ERROR; + if (continueOnError) { + free(outName); + cleanupUserData(&userData); + continue; + } else { + break; + } } setvbuf(userData.fp, NULL, _IOFBF, 16384); #ifdef XML_UNICODE @@ -1119,8 +1243,12 @@ tmain(int argc, XML_Char **argv) { } XML_ParserFree(parser); if (! result) { - exit(2); + exitCode = XMLWF_EXIT_NOT_WELLFORMED; + cleanupUserData(&userData); + if (! continueOnError) { + break; + } } } - return 0; + return exitCode; } |
