summaryrefslogtreecommitdiff
path: root/xmlwf/xmlwf.c
diff options
context:
space:
mode:
Diffstat (limited to 'xmlwf/xmlwf.c')
-rw-r--r--xmlwf/xmlwf.c186
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;
}