From 78366891a586f293aeff60a14a55e4afe1169586 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Tue, 2 Sep 2025 16:44:00 +0200 Subject: [PATCH] xmlwf: Wire allocation tracker config to existing arguments -a and -b CVE: CVE-2025-59375 Upstream-Status: Backport [https://github.com/libexpat/libexpat/commit/78366891a586f293aeff60a14a55e4afe1169586] Signed-off-by: Peter Marko --- doc/xmlwf.xml | 26 ++++++++++++++++++++------ xmlwf/xmlwf.c | 7 +++++-- xmlwf/xmlwf_helpgen.py | 4 ++-- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/doc/xmlwf.xml b/doc/xmlwf.xml index 17e9cf51..65d8ae9b 100644 --- a/doc/xmlwf.xml +++ b/doc/xmlwf.xml @@ -158,19 +158,31 @@ supports both. Sets the maximum tolerated amplification factor - for protection against billion laughs attacks (default: 100.0). + for protection against amplification attacks + like the billion laughs attack + (default: 100.0 + for the sum of direct and indirect output and also + for allocations of dynamic memory). The amplification factor is calculated as .. amplification := (direct + indirect) / direct - .. while parsing, whereas + .. with regard to use of entities and .. + + + amplification := allocated / direct + + + .. with regard to dynamic memory while parsing. <direct> is the number of bytes read - from the primary document in parsing and + from the primary document in parsing, <indirect> is the number of bytes added by expanding entities and reading of external DTD files, - combined. + combined, and + <allocated> is the total number of bytes of dynamic memory + allocated (and not freed) per hierarchy of parsers. NOTE: @@ -185,8 +197,10 @@ supports both. Sets the number of output bytes (including amplification) - needed to activate protection against billion laughs attacks - (default: 8 MiB). + needed to activate protection against amplification attacks + like billion laughs + (default: 8 MiB for the sum of direct and indirect output, + and 64 MiB for allocations of dynamic memory). This can be thought of as an "activation threshold". diff --git a/xmlwf/xmlwf.c b/xmlwf/xmlwf.c index b9d0a7fc..14206d9e 100644 --- a/xmlwf/xmlwf.c +++ b/xmlwf/xmlwf.c @@ -913,11 +913,11 @@ 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("amplification attack protection (e.g. billion laughs):\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(" -b BYTES set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)\n") T("\n") T("reparse deferral:\n") T(" -q disable reparse deferral, and allow [q]uadratic parse runtime with large tokens\n") @@ -1181,12 +1181,15 @@ tmain(int argc, XML_Char **argv) { #if XML_GE == 1 XML_SetBillionLaughsAttackProtectionMaximumAmplification( parser, attackMaximumAmplification); + XML_SetAllocTrackerMaximumAmplification(parser, + attackMaximumAmplification); #endif } if (attackThresholdGiven) { #if XML_GE == 1 XML_SetBillionLaughsAttackProtectionActivationThreshold( parser, attackThresholdBytes); + XML_SetAllocTrackerActivationThreshold(parser, attackThresholdBytes); #else (void)attackThresholdBytes; // silence -Wunused-but-set-variable #endif diff --git a/xmlwf/xmlwf_helpgen.py b/xmlwf/xmlwf_helpgen.py index 2360820d..e91c285c 100755 --- a/xmlwf/xmlwf_helpgen.py +++ b/xmlwf/xmlwf_helpgen.py @@ -84,13 +84,13 @@ output_mode.add_argument('-m', action='store_true', help='write [m]eta XML, not output_mode.add_argument('-t', action='store_true', help='write no XML output for [t]iming of plain parsing') output_related.add_argument('-N', action='store_true', help='enable adding doctype and [n]otation declarations') -billion_laughs = parser.add_argument_group('billion laughs attack protection', +billion_laughs = parser.add_argument_group('amplification attack protection (e.g. billion laughs)', description='NOTE: ' 'If you ever need to increase these values ' 'for non-attack payload, please file a bug report.') billion_laughs.add_argument('-a', metavar='FACTOR', help='set maximum tolerated [a]mplification factor (default: 100.0)') -billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB)') +billion_laughs.add_argument('-b', metavar='BYTES', help='set number of output [b]ytes needed to activate (default: 8 MiB/64 MiB)') reparse_deferral = parser.add_argument_group('reparse deferral') reparse_deferral.add_argument('-q', metavar='FACTOR',