summaryrefslogtreecommitdiffstats
path: root/meta/recipes-core/expat/expat/CVE-2013-0340.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-core/expat/expat/CVE-2013-0340.patch')
-rw-r--r--meta/recipes-core/expat/expat/CVE-2013-0340.patch1758
1 files changed, 1758 insertions, 0 deletions
diff --git a/meta/recipes-core/expat/expat/CVE-2013-0340.patch b/meta/recipes-core/expat/expat/CVE-2013-0340.patch
new file mode 100644
index 0000000000..1ab4d06508
--- /dev/null
+++ b/meta/recipes-core/expat/expat/CVE-2013-0340.patch
@@ -0,0 +1,1758 @@
1From a644ccf25392523b1329872310e24d0fc5f40629 Mon Sep 17 00:00:00 2001
2From: Sebastian Pipping <sebastian@pipping.org>
3Date: Mon, 19 Apr 2021 21:42:51 +0200
4Subject: [PATCH] expat: Backport fix for CVE-2013-0340
5
6Issue: https://github.com/libexpat/libexpat/issues/34
7
8This patch cherry-picks the following commits from upstream release
92.4.0 onto 2.2.9:
10
11- b1d039607d3d8a042bf0466bfcc1c0f104e353c8
12- 60959f2b491876199879d97c8ed956eabb0c2e73
13
14Upstream-Status: Backport
15CVE: CVE-2013-0340
16Signed-off-by: Jasper Orschulko <jasper@fancydomain.eu>
17---
18 lib/expat.h | 21 +-
19 lib/internal.h | 30 +
20 lib/libexpat.def | 3 +
21 lib/libexpatw.def | 3 +
22 lib/xmlparse.c | 1147 +++++++++++++++++++++++++++++++++++++--
23 5 files changed, 1143 insertions(+), 61 deletions(-)
24
25diff --git a/lib/expat.h b/lib/expat.h
26index 48a6e2a3..0fb70d9d 100644
27--- a/lib/expat.h
28+++ b/lib/expat.h
29@@ -115,7 +115,9 @@ enum XML_Error {
30 XML_ERROR_RESERVED_PREFIX_XMLNS,
31 XML_ERROR_RESERVED_NAMESPACE_URI,
32 /* Added in 2.2.1. */
33- XML_ERROR_INVALID_ARGUMENT
34+ XML_ERROR_INVALID_ARGUMENT,
35+ /* Added in 2.4.0. */
36+ XML_ERROR_AMPLIFICATION_LIMIT_BREACH
37 };
38
39 enum XML_Content_Type {
40@@ -997,7 +999,10 @@ enum XML_FeatureEnum {
41 XML_FEATURE_SIZEOF_XML_LCHAR,
42 XML_FEATURE_NS,
43 XML_FEATURE_LARGE_SIZE,
44- XML_FEATURE_ATTR_INFO
45+ XML_FEATURE_ATTR_INFO,
46+ /* Added in Expat 2.4.0. */
47+ XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
48+ XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT
49 /* Additional features must be added to the end of this enum. */
50 };
51
52@@ -1010,6 +1015,18 @@ typedef struct {
53 XMLPARSEAPI(const XML_Feature *)
54 XML_GetFeatureList(void);
55
56+#ifdef XML_DTD
57+/* Added in Expat 2.4.0. */
58+XMLPARSEAPI(XML_Bool)
59+XML_SetBillionLaughsAttackProtectionMaximumAmplification(
60+ XML_Parser parser, float maximumAmplificationFactor);
61+
62+/* Added in Expat 2.4.0. */
63+XMLPARSEAPI(XML_Bool)
64+XML_SetBillionLaughsAttackProtectionActivationThreshold(
65+ XML_Parser parser, unsigned long long activationThresholdBytes);
66+#endif
67+
68 /* Expat follows the semantic versioning convention.
69 See http://semver.org.
70 */
71diff --git a/lib/internal.h b/lib/internal.h
72index 60913dab..d8b31fa2 100644
73--- a/lib/internal.h
74+++ b/lib/internal.h
75@@ -101,10 +101,40 @@
76 # endif
77 #endif
78
79+#include <limits.h> // ULONG_MAX
80+
81+#if defined(_WIN32) && ! defined(__USE_MINGW_ANSI_STDIO)
82+# define EXPAT_FMT_ULL(midpart) "%" midpart "I64u"
83+# if defined(_WIN64) // Note: modifier "td" does not work for MinGW
84+# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d"
85+# else
86+# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
87+# endif
88+#else
89+# define EXPAT_FMT_ULL(midpart) "%" midpart "llu"
90+# if ! defined(ULONG_MAX)
91+# error Compiler did not define ULONG_MAX for us
92+# elif ULONG_MAX == 18446744073709551615u // 2^64-1
93+# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld"
94+# else
95+# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
96+# endif
97+#endif
98+
99 #ifndef UNUSED_P
100 # define UNUSED_P(p) (void)p
101 #endif
102
103+/* NOTE BEGIN If you ever patch these defaults to greater values
104+ for non-attack XML payload in your environment,
105+ please file a bug report with libexpat. Thank you!
106+*/
107+#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT \
108+ 100.0f
109+#define EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT \
110+ 8388608 // 8 MiB, 2^23
111+/* NOTE END */
112+
113 #ifdef __cplusplus
114 extern "C" {
115 #endif
116diff --git a/lib/libexpat.def b/lib/libexpat.def
117index 16faf595..5aefa6df 100644
118--- a/lib/libexpat.def
119+++ b/lib/libexpat.def
120@@ -76,3 +76,6 @@ EXPORTS
121 XML_SetHashSalt @67
122 ; added with version 2.2.5
123 _INTERNAL_trim_to_complete_utf8_characters @68
124+; added with version 2.4.0
125+ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
126+ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
127diff --git a/lib/libexpatw.def b/lib/libexpatw.def
128index 16faf595..5aefa6df 100644
129--- a/lib/libexpatw.def
130+++ b/lib/libexpatw.def
131@@ -76,3 +76,6 @@ EXPORTS
132 XML_SetHashSalt @67
133 ; added with version 2.2.5
134 _INTERNAL_trim_to_complete_utf8_characters @68
135+; added with version 2.4.0
136+ XML_SetBillionLaughsAttackProtectionActivationThreshold @69
137+ XML_SetBillionLaughsAttackProtectionMaximumAmplification @70
138diff --git a/lib/xmlparse.c b/lib/xmlparse.c
139index 3aaf35b9..6790bc28 100644
140--- a/lib/xmlparse.c
141+++ b/lib/xmlparse.c
142@@ -47,6 +47,8 @@
143 #include <limits.h> /* UINT_MAX */
144 #include <stdio.h> /* fprintf */
145 #include <stdlib.h> /* getenv, rand_s */
146+#include <stdint.h> /* uintptr_t */
147+#include <math.h> /* isnan */
148
149 #ifdef _WIN32
150 # define getpid GetCurrentProcessId
151@@ -373,6 +375,31 @@ typedef struct open_internal_entity {
152 XML_Bool betweenDecl; /* WFC: PE Between Declarations */
153 } OPEN_INTERNAL_ENTITY;
154
155+enum XML_Account {
156+ XML_ACCOUNT_DIRECT, /* bytes directly passed to the Expat parser */
157+ XML_ACCOUNT_ENTITY_EXPANSION, /* intermediate bytes produced during entity
158+ expansion */
159+ XML_ACCOUNT_NONE /* i.e. do not account, was accounted already */
160+};
161+
162+#ifdef XML_DTD
163+typedef unsigned long long XmlBigCount;
164+typedef struct accounting {
165+ XmlBigCount countBytesDirect;
166+ XmlBigCount countBytesIndirect;
167+ int debugLevel;
168+ float maximumAmplificationFactor; // >=1.0
169+ unsigned long long activationThresholdBytes;
170+} ACCOUNTING;
171+
172+typedef struct entity_stats {
173+ unsigned int countEverOpened;
174+ unsigned int currentDepth;
175+ unsigned int maximumDepthSeen;
176+ int debugLevel;
177+} ENTITY_STATS;
178+#endif /* XML_DTD */
179+
180 typedef enum XML_Error PTRCALL Processor(XML_Parser parser, const char *start,
181 const char *end, const char **endPtr);
182
183@@ -403,16 +430,18 @@ static enum XML_Error initializeEncoding(XML_Parser parser);
184 static enum XML_Error doProlog(XML_Parser parser, const ENCODING *enc,
185 const char *s, const char *end, int tok,
186 const char *next, const char **nextPtr,
187- XML_Bool haveMore, XML_Bool allowClosingDoctype);
188+ XML_Bool haveMore, XML_Bool allowClosingDoctype,
189+ enum XML_Account account);
190 static enum XML_Error processInternalEntity(XML_Parser parser, ENTITY *entity,
191 XML_Bool betweenDecl);
192 static enum XML_Error doContent(XML_Parser parser, int startTagLevel,
193 const ENCODING *enc, const char *start,
194 const char *end, const char **endPtr,
195- XML_Bool haveMore);
196+ XML_Bool haveMore, enum XML_Account account);
197 static enum XML_Error doCdataSection(XML_Parser parser, const ENCODING *,
198 const char **startPtr, const char *end,
199- const char **nextPtr, XML_Bool haveMore);
200+ const char **nextPtr, XML_Bool haveMore,
201+ enum XML_Account account);
202 #ifdef XML_DTD
203 static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *,
204 const char **startPtr, const char *end,
205@@ -422,7 +451,8 @@ static enum XML_Error doIgnoreSection(XML_Parser parser, const ENCODING *,
206 static void freeBindings(XML_Parser parser, BINDING *bindings);
207 static enum XML_Error storeAtts(XML_Parser parser, const ENCODING *,
208 const char *s, TAG_NAME *tagNamePtr,
209- BINDING **bindingsPtr);
210+ BINDING **bindingsPtr,
211+ enum XML_Account account);
212 static enum XML_Error addBinding(XML_Parser parser, PREFIX *prefix,
213 const ATTRIBUTE_ID *attId, const XML_Char *uri,
214 BINDING **bindingsPtr);
215@@ -431,15 +461,18 @@ static int defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata,
216 XML_Parser parser);
217 static enum XML_Error storeAttributeValue(XML_Parser parser, const ENCODING *,
218 XML_Bool isCdata, const char *,
219- const char *, STRING_POOL *);
220+ const char *, STRING_POOL *,
221+ enum XML_Account account);
222 static enum XML_Error appendAttributeValue(XML_Parser parser, const ENCODING *,
223 XML_Bool isCdata, const char *,
224- const char *, STRING_POOL *);
225+ const char *, STRING_POOL *,
226+ enum XML_Account account);
227 static ATTRIBUTE_ID *getAttributeId(XML_Parser parser, const ENCODING *enc,
228 const char *start, const char *end);
229 static int setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *);
230 static enum XML_Error storeEntityValue(XML_Parser parser, const ENCODING *enc,
231- const char *start, const char *end);
232+ const char *start, const char *end,
233+ enum XML_Account account);
234 static int reportProcessingInstruction(XML_Parser parser, const ENCODING *enc,
235 const char *start, const char *end);
236 static int reportComment(XML_Parser parser, const ENCODING *enc,
237@@ -503,6 +536,35 @@ static XML_Parser parserCreate(const XML_Char *encodingName,
238
239 static void parserInit(XML_Parser parser, const XML_Char *encodingName);
240
241+#ifdef XML_DTD
242+static float accountingGetCurrentAmplification(XML_Parser rootParser);
243+static void accountingReportStats(XML_Parser originParser, const char *epilog);
244+static void accountingOnAbort(XML_Parser originParser);
245+static void accountingReportDiff(XML_Parser rootParser,
246+ unsigned int levelsAwayFromRootParser,
247+ const char *before, const char *after,
248+ ptrdiff_t bytesMore, int source_line,
249+ enum XML_Account account);
250+static XML_Bool accountingDiffTolerated(XML_Parser originParser, int tok,
251+ const char *before, const char *after,
252+ int source_line,
253+ enum XML_Account account);
254+
255+static void entityTrackingReportStats(XML_Parser parser, ENTITY *entity,
256+ const char *action, int sourceLine);
257+static void entityTrackingOnOpen(XML_Parser parser, ENTITY *entity,
258+ int sourceLine);
259+static void entityTrackingOnClose(XML_Parser parser, ENTITY *entity,
260+ int sourceLine);
261+
262+static XML_Parser getRootParserOf(XML_Parser parser,
263+ unsigned int *outLevelDiff);
264+static const char *unsignedCharToPrintable(unsigned char c);
265+#endif /* XML_DTD */
266+
267+static unsigned long getDebugLevel(const char *variableName,
268+ unsigned long defaultDebugLevel);
269+
270 #define poolStart(pool) ((pool)->start)
271 #define poolEnd(pool) ((pool)->ptr)
272 #define poolLength(pool) ((pool)->ptr - (pool)->start)
273@@ -616,6 +678,10 @@ struct XML_ParserStruct {
274 enum XML_ParamEntityParsing m_paramEntityParsing;
275 #endif
276 unsigned long m_hash_secret_salt;
277+#ifdef XML_DTD
278+ ACCOUNTING m_accounting;
279+ ENTITY_STATS m_entity_stats;
280+#endif
281 };
282
283 #define MALLOC(parser, s) (parser->m_mem.malloc_fcn((s)))
284@@ -1055,6 +1121,18 @@ parserInit(XML_Parser parser, const XML_Char *encodingName) {
285 parser->m_paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER;
286 #endif
287 parser->m_hash_secret_salt = 0;
288+
289+#ifdef XML_DTD
290+ memset(&parser->m_accounting, 0, sizeof(ACCOUNTING));
291+ parser->m_accounting.debugLevel = getDebugLevel("EXPAT_ACCOUNTING_DEBUG", 0u);
292+ parser->m_accounting.maximumAmplificationFactor
293+ = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT;
294+ parser->m_accounting.activationThresholdBytes
295+ = EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT;
296+
297+ memset(&parser->m_entity_stats, 0, sizeof(ENTITY_STATS));
298+ parser->m_entity_stats.debugLevel = getDebugLevel("EXPAT_ENTITY_DEBUG", 0u);
299+#endif
300 }
301
302 /* moves list of bindings to m_freeBindingList */
303@@ -2318,6 +2396,10 @@ XML_ErrorString(enum XML_Error code) {
304 /* Added in 2.2.5. */
305 case XML_ERROR_INVALID_ARGUMENT: /* Constant added in 2.2.1, already */
306 return XML_L("invalid argument");
307+ /* Added in 2.4.0. */
308+ case XML_ERROR_AMPLIFICATION_LIMIT_BREACH:
309+ return XML_L(
310+ "limit on input amplification factor (from DTD and entities) breached");
311 }
312 return NULL;
313 }
314@@ -2354,41 +2436,75 @@ XML_ExpatVersionInfo(void) {
315
316 const XML_Feature *XMLCALL
317 XML_GetFeatureList(void) {
318- static const XML_Feature features[]
319- = {{XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"),
320- sizeof(XML_Char)},
321- {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"),
322- sizeof(XML_LChar)},
323+ static const XML_Feature features[] = {
324+ {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"),
325+ sizeof(XML_Char)},
326+ {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"),
327+ sizeof(XML_LChar)},
328 #ifdef XML_UNICODE
329- {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0},
330+ {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0},
331 #endif
332 #ifdef XML_UNICODE_WCHAR_T
333- {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0},
334+ {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0},
335 #endif
336 #ifdef XML_DTD
337- {XML_FEATURE_DTD, XML_L("XML_DTD"), 0},
338+ {XML_FEATURE_DTD, XML_L("XML_DTD"), 0},
339 #endif
340 #ifdef XML_CONTEXT_BYTES
341- {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"),
342- XML_CONTEXT_BYTES},
343+ {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"),
344+ XML_CONTEXT_BYTES},
345 #endif
346 #ifdef XML_MIN_SIZE
347- {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0},
348+ {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0},
349 #endif
350 #ifdef XML_NS
351- {XML_FEATURE_NS, XML_L("XML_NS"), 0},
352+ {XML_FEATURE_NS, XML_L("XML_NS"), 0},
353 #endif
354 #ifdef XML_LARGE_SIZE
355- {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0},
356+ {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0},
357 #endif
358 #ifdef XML_ATTR_INFO
359- {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
360+ {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0},
361 #endif
362- {XML_FEATURE_END, NULL, 0}};
363+#ifdef XML_DTD
364+ /* Added in Expat 2.4.0. */
365+ {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT,
366+ XML_L("XML_BLAP_MAX_AMP"),
367+ (long int)
368+ EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_MAXIMUM_AMPLIFICATION_DEFAULT},
369+ {XML_FEATURE_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT,
370+ XML_L("XML_BLAP_ACT_THRES"),
371+ EXPAT_BILLION_LAUGHS_ATTACK_PROTECTION_ACTIVATION_THRESHOLD_DEFAULT},
372+#endif
373+ {XML_FEATURE_END, NULL, 0}};
374
375 return features;
376 }
377
378+#ifdef XML_DTD
379+XML_Bool XMLCALL
380+XML_SetBillionLaughsAttackProtectionMaximumAmplification(
381+ XML_Parser parser, float maximumAmplificationFactor) {
382+ if ((parser == NULL) || (parser->m_parentParser != NULL)
383+ || isnan(maximumAmplificationFactor)
384+ || (maximumAmplificationFactor < 1.0f)) {
385+ return XML_FALSE;
386+ }
387+ parser->m_accounting.maximumAmplificationFactor = maximumAmplificationFactor;
388+ return XML_TRUE;
389+}
390+
391+XML_Bool XMLCALL
392+XML_SetBillionLaughsAttackProtectionActivationThreshold(
393+ XML_Parser parser, unsigned long long activationThresholdBytes) {
394+ if ((parser == NULL) || (parser->m_parentParser != NULL)) {
395+ return XML_FALSE;
396+ }
397+ parser->m_accounting.activationThresholdBytes = activationThresholdBytes;
398+ return XML_TRUE;
399+}
400+#endif /* XML_DTD */
401+
402 /* Initially tag->rawName always points into the parse buffer;
403 for those TAG instances opened while the current parse buffer was
404 processed, and not yet closed, we need to store tag->rawName in a more
405@@ -2441,9 +2557,9 @@ storeRawNames(XML_Parser parser) {
406 static enum XML_Error PTRCALL
407 contentProcessor(XML_Parser parser, const char *start, const char *end,
408 const char **endPtr) {
409- enum XML_Error result
410- = doContent(parser, 0, parser->m_encoding, start, end, endPtr,
411- (XML_Bool)! parser->m_parsingStatus.finalBuffer);
412+ enum XML_Error result = doContent(
413+ parser, 0, parser->m_encoding, start, end, endPtr,
414+ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
415 if (result == XML_ERROR_NONE) {
416 if (! storeRawNames(parser))
417 return XML_ERROR_NO_MEMORY;
418@@ -2468,6 +2584,14 @@ externalEntityInitProcessor2(XML_Parser parser, const char *start,
419 int tok = XmlContentTok(parser->m_encoding, start, end, &next);
420 switch (tok) {
421 case XML_TOK_BOM:
422+#ifdef XML_DTD
423+ if (! accountingDiffTolerated(parser, tok, start, next, __LINE__,
424+ XML_ACCOUNT_DIRECT)) {
425+ accountingOnAbort(parser);
426+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
427+ }
428+#endif /* XML_DTD */
429+
430 /* If we are at the end of the buffer, this would cause the next stage,
431 i.e. externalEntityInitProcessor3, to pass control directly to
432 doContent (by detecting XML_TOK_NONE) without processing any xml text
433@@ -2505,6 +2629,10 @@ externalEntityInitProcessor3(XML_Parser parser, const char *start,
434 const char *next = start; /* XmlContentTok doesn't always set the last arg */
435 parser->m_eventPtr = start;
436 tok = XmlContentTok(parser->m_encoding, start, end, &next);
437+ /* Note: These bytes are accounted later in:
438+ - processXmlDecl
439+ - externalEntityContentProcessor
440+ */
441 parser->m_eventEndPtr = next;
442
443 switch (tok) {
444@@ -2546,7 +2674,8 @@ externalEntityContentProcessor(XML_Parser parser, const char *start,
445 const char *end, const char **endPtr) {
446 enum XML_Error result
447 = doContent(parser, 1, parser->m_encoding, start, end, endPtr,
448- (XML_Bool)! parser->m_parsingStatus.finalBuffer);
449+ (XML_Bool)! parser->m_parsingStatus.finalBuffer,
450+ XML_ACCOUNT_ENTITY_EXPANSION);
451 if (result == XML_ERROR_NONE) {
452 if (! storeRawNames(parser))
453 return XML_ERROR_NO_MEMORY;
454@@ -2557,7 +2686,7 @@ externalEntityContentProcessor(XML_Parser parser, const char *start,
455 static enum XML_Error
456 doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
457 const char *s, const char *end, const char **nextPtr,
458- XML_Bool haveMore) {
459+ XML_Bool haveMore, enum XML_Account account) {
460 /* save one level of indirection */
461 DTD *const dtd = parser->m_dtd;
462
463@@ -2575,6 +2704,17 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
464 for (;;) {
465 const char *next = s; /* XmlContentTok doesn't always set the last arg */
466 int tok = XmlContentTok(enc, s, end, &next);
467+#ifdef XML_DTD
468+ const char *accountAfter
469+ = ((tok == XML_TOK_TRAILING_RSQB) || (tok == XML_TOK_TRAILING_CR))
470+ ? (haveMore ? s /* i.e. 0 bytes */ : end)
471+ : next;
472+ if (! accountingDiffTolerated(parser, tok, s, accountAfter, __LINE__,
473+ account)) {
474+ accountingOnAbort(parser);
475+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
476+ }
477+#endif
478 *eventEndPP = next;
479 switch (tok) {
480 case XML_TOK_TRAILING_CR:
481@@ -2630,6 +2770,14 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
482 XML_Char ch = (XML_Char)XmlPredefinedEntityName(
483 enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar);
484 if (ch) {
485+#ifdef XML_DTD
486+ /* NOTE: We are replacing 4-6 characters original input for 1 character
487+ * so there is no amplification and hence recording without
488+ * protection. */
489+ accountingDiffTolerated(parser, tok, (char *)&ch,
490+ ((char *)&ch) + sizeof(XML_Char), __LINE__,
491+ XML_ACCOUNT_ENTITY_EXPANSION);
492+#endif /* XML_DTD */
493 if (parser->m_characterDataHandler)
494 parser->m_characterDataHandler(parser->m_handlerArg, &ch, 1);
495 else if (parser->m_defaultHandler)
496@@ -2748,7 +2896,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
497 }
498 tag->name.str = (XML_Char *)tag->buf;
499 *toPtr = XML_T('\0');
500- result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings));
501+ result
502+ = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings), account);
503 if (result)
504 return result;
505 if (parser->m_startElementHandler)
506@@ -2772,7 +2921,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
507 if (! name.str)
508 return XML_ERROR_NO_MEMORY;
509 poolFinish(&parser->m_tempPool);
510- result = storeAtts(parser, enc, s, &name, &bindings);
511+ result = storeAtts(parser, enc, s, &name, &bindings,
512+ XML_ACCOUNT_NONE /* token spans whole start tag */);
513 if (result != XML_ERROR_NONE) {
514 freeBindings(parser, bindings);
515 return result;
516@@ -2907,7 +3057,8 @@ doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc,
517 /* END disabled code */
518 else if (parser->m_defaultHandler)
519 reportDefault(parser, enc, s, next);
520- result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore);
521+ result
522+ = doCdataSection(parser, enc, &next, end, nextPtr, haveMore, account);
523 if (result != XML_ERROR_NONE)
524 return result;
525 else if (! next) {
526@@ -3036,7 +3187,8 @@ freeBindings(XML_Parser parser, BINDING *bindings) {
527 */
528 static enum XML_Error
529 storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
530- TAG_NAME *tagNamePtr, BINDING **bindingsPtr) {
531+ TAG_NAME *tagNamePtr, BINDING **bindingsPtr,
532+ enum XML_Account account) {
533 DTD *const dtd = parser->m_dtd; /* save one level of indirection */
534 ELEMENT_TYPE *elementType;
535 int nDefaultAtts;
536@@ -3146,7 +3298,7 @@ storeAtts(XML_Parser parser, const ENCODING *enc, const char *attStr,
537 /* normalize the attribute value */
538 result = storeAttributeValue(
539 parser, enc, isCdata, parser->m_atts[i].valuePtr,
540- parser->m_atts[i].valueEnd, &parser->m_tempPool);
541+ parser->m_atts[i].valueEnd, &parser->m_tempPool, account);
542 if (result)
543 return result;
544 appAtts[attIndex] = poolStart(&parser->m_tempPool);
545@@ -3535,9 +3687,9 @@ addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId,
546 static enum XML_Error PTRCALL
547 cdataSectionProcessor(XML_Parser parser, const char *start, const char *end,
548 const char **endPtr) {
549- enum XML_Error result
550- = doCdataSection(parser, parser->m_encoding, &start, end, endPtr,
551- (XML_Bool)! parser->m_parsingStatus.finalBuffer);
552+ enum XML_Error result = doCdataSection(
553+ parser, parser->m_encoding, &start, end, endPtr,
554+ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_ACCOUNT_DIRECT);
555 if (result != XML_ERROR_NONE)
556 return result;
557 if (start) {
558@@ -3557,7 +3709,8 @@ cdataSectionProcessor(XML_Parser parser, const char *start, const char *end,
559 */
560 static enum XML_Error
561 doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
562- const char *end, const char **nextPtr, XML_Bool haveMore) {
563+ const char *end, const char **nextPtr, XML_Bool haveMore,
564+ enum XML_Account account) {
565 const char *s = *startPtr;
566 const char **eventPP;
567 const char **eventEndPP;
568@@ -3575,6 +3728,14 @@ doCdataSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
569 for (;;) {
570 const char *next;
571 int tok = XmlCdataSectionTok(enc, s, end, &next);
572+#ifdef XML_DTD
573+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
574+ accountingOnAbort(parser);
575+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
576+ }
577+#else
578+ UNUSED_P(account);
579+#endif
580 *eventEndPP = next;
581 switch (tok) {
582 case XML_TOK_CDATA_SECT_CLOSE:
583@@ -3719,6 +3880,13 @@ doIgnoreSection(XML_Parser parser, const ENCODING *enc, const char **startPtr,
584 *eventPP = s;
585 *startPtr = NULL;
586 tok = XmlIgnoreSectionTok(enc, s, end, &next);
587+# ifdef XML_DTD
588+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
589+ XML_ACCOUNT_DIRECT)) {
590+ accountingOnAbort(parser);
591+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
592+ }
593+# endif
594 *eventEndPP = next;
595 switch (tok) {
596 case XML_TOK_IGNORE_SECT:
597@@ -3803,6 +3971,15 @@ processXmlDecl(XML_Parser parser, int isGeneralTextEntity, const char *s,
598 const char *versionend;
599 const XML_Char *storedversion = NULL;
600 int standalone = -1;
601+
602+#ifdef XML_DTD
603+ if (! accountingDiffTolerated(parser, XML_TOK_XML_DECL, s, next, __LINE__,
604+ XML_ACCOUNT_DIRECT)) {
605+ accountingOnAbort(parser);
606+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
607+ }
608+#endif
609+
610 if (! (parser->m_ns ? XmlParseXmlDeclNS : XmlParseXmlDecl)(
611 isGeneralTextEntity, parser->m_encoding, s, next, &parser->m_eventPtr,
612 &version, &versionend, &encodingName, &newEncoding, &standalone)) {
613@@ -3952,6 +4129,10 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
614
615 for (;;) {
616 tok = XmlPrologTok(parser->m_encoding, start, end, &next);
617+ /* Note: Except for XML_TOK_BOM below, these bytes are accounted later in:
618+ - storeEntityValue
619+ - processXmlDecl
620+ */
621 parser->m_eventEndPtr = next;
622 if (tok <= 0) {
623 if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
624@@ -3970,7 +4151,8 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
625 break;
626 }
627 /* found end of entity value - can store it now */
628- return storeEntityValue(parser, parser->m_encoding, s, end);
629+ return storeEntityValue(parser, parser->m_encoding, s, end,
630+ XML_ACCOUNT_DIRECT);
631 } else if (tok == XML_TOK_XML_DECL) {
632 enum XML_Error result;
633 result = processXmlDecl(parser, 0, start, next);
634@@ -3997,6 +4179,14 @@ entityValueInitProcessor(XML_Parser parser, const char *s, const char *end,
635 */
636 else if (tok == XML_TOK_BOM && next == end
637 && ! parser->m_parsingStatus.finalBuffer) {
638+# ifdef XML_DTD
639+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
640+ XML_ACCOUNT_DIRECT)) {
641+ accountingOnAbort(parser);
642+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
643+ }
644+# endif
645+
646 *nextPtr = next;
647 return XML_ERROR_NONE;
648 }
649@@ -4039,16 +4229,24 @@ externalParEntProcessor(XML_Parser parser, const char *s, const char *end,
650 }
651 /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM.
652 However, when parsing an external subset, doProlog will not accept a BOM
653- as valid, and report a syntax error, so we have to skip the BOM
654+ as valid, and report a syntax error, so we have to skip the BOM, and
655+ account for the BOM bytes.
656 */
657 else if (tok == XML_TOK_BOM) {
658+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
659+ XML_ACCOUNT_DIRECT)) {
660+ accountingOnAbort(parser);
661+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
662+ }
663+
664 s = next;
665 tok = XmlPrologTok(parser->m_encoding, s, end, &next);
666 }
667
668 parser->m_processor = prologProcessor;
669 return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
670- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
671+ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
672+ XML_ACCOUNT_DIRECT);
673 }
674
675 static enum XML_Error PTRCALL
676@@ -4061,6 +4259,9 @@ entityValueProcessor(XML_Parser parser, const char *s, const char *end,
677
678 for (;;) {
679 tok = XmlPrologTok(enc, start, end, &next);
680+ /* Note: These bytes are accounted later in:
681+ - storeEntityValue
682+ */
683 if (tok <= 0) {
684 if (! parser->m_parsingStatus.finalBuffer && tok != XML_TOK_INVALID) {
685 *nextPtr = s;
686@@ -4078,7 +4279,7 @@ entityValueProcessor(XML_Parser parser, const char *s, const char *end,
687 break;
688 }
689 /* found end of entity value - can store it now */
690- return storeEntityValue(parser, enc, s, end);
691+ return storeEntityValue(parser, enc, s, end, XML_ACCOUNT_DIRECT);
692 }
693 start = next;
694 }
695@@ -4092,13 +4293,14 @@ prologProcessor(XML_Parser parser, const char *s, const char *end,
696 const char *next = s;
697 int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
698 return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
699- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
700+ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
701+ XML_ACCOUNT_DIRECT);
702 }
703
704 static enum XML_Error
705 doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
706 int tok, const char *next, const char **nextPtr, XML_Bool haveMore,
707- XML_Bool allowClosingDoctype) {
708+ XML_Bool allowClosingDoctype, enum XML_Account account) {
709 #ifdef XML_DTD
710 static const XML_Char externalSubsetName[] = {ASCII_HASH, '\0'};
711 #endif /* XML_DTD */
712@@ -4125,6 +4327,10 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
713 static const XML_Char enumValueSep[] = {ASCII_PIPE, '\0'};
714 static const XML_Char enumValueStart[] = {ASCII_LPAREN, '\0'};
715
716+#ifndef XML_DTD
717+ UNUSED_P(account);
718+#endif
719+
720 /* save one level of indirection */
721 DTD *const dtd = parser->m_dtd;
722
723@@ -4189,6 +4395,19 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
724 }
725 }
726 role = XmlTokenRole(&parser->m_prologState, tok, s, next, enc);
727+#ifdef XML_DTD
728+ switch (role) {
729+ case XML_ROLE_INSTANCE_START: // bytes accounted in contentProcessor
730+ case XML_ROLE_XML_DECL: // bytes accounted in processXmlDecl
731+ case XML_ROLE_TEXT_DECL: // bytes accounted in processXmlDecl
732+ break;
733+ default:
734+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__, account)) {
735+ accountingOnAbort(parser);
736+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
737+ }
738+ }
739+#endif
740 switch (role) {
741 case XML_ROLE_XML_DECL: {
742 enum XML_Error result = processXmlDecl(parser, 0, s, next);
743@@ -4464,7 +4683,8 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
744 const XML_Char *attVal;
745 enum XML_Error result = storeAttributeValue(
746 parser, enc, parser->m_declAttributeIsCdata,
747- s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool);
748+ s + enc->minBytesPerChar, next - enc->minBytesPerChar, &dtd->pool,
749+ XML_ACCOUNT_NONE);
750 if (result)
751 return result;
752 attVal = poolStart(&dtd->pool);
753@@ -4497,8 +4717,9 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
754 break;
755 case XML_ROLE_ENTITY_VALUE:
756 if (dtd->keepProcessing) {
757- enum XML_Error result = storeEntityValue(
758- parser, enc, s + enc->minBytesPerChar, next - enc->minBytesPerChar);
759+ enum XML_Error result
760+ = storeEntityValue(parser, enc, s + enc->minBytesPerChar,
761+ next - enc->minBytesPerChar, XML_ACCOUNT_NONE);
762 if (parser->m_declEntity) {
763 parser->m_declEntity->textPtr = poolStart(&dtd->entityValuePool);
764 parser->m_declEntity->textLen
765@@ -4888,12 +5109,15 @@ doProlog(XML_Parser parser, const ENCODING *enc, const char *s, const char *end,
766 if (parser->m_externalEntityRefHandler) {
767 dtd->paramEntityRead = XML_FALSE;
768 entity->open = XML_TRUE;
769+ entityTrackingOnOpen(parser, entity, __LINE__);
770 if (! parser->m_externalEntityRefHandler(
771 parser->m_externalEntityRefHandlerArg, 0, entity->base,
772 entity->systemId, entity->publicId)) {
773+ entityTrackingOnClose(parser, entity, __LINE__);
774 entity->open = XML_FALSE;
775 return XML_ERROR_EXTERNAL_ENTITY_HANDLING;
776 }
777+ entityTrackingOnClose(parser, entity, __LINE__);
778 entity->open = XML_FALSE;
779 handleDefault = XML_FALSE;
780 if (! dtd->paramEntityRead) {
781@@ -5091,6 +5315,13 @@ epilogProcessor(XML_Parser parser, const char *s, const char *end,
782 for (;;) {
783 const char *next = NULL;
784 int tok = XmlPrologTok(parser->m_encoding, s, end, &next);
785+#ifdef XML_DTD
786+ if (! accountingDiffTolerated(parser, tok, s, next, __LINE__,
787+ XML_ACCOUNT_DIRECT)) {
788+ accountingOnAbort(parser);
789+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
790+ }
791+#endif
792 parser->m_eventEndPtr = next;
793 switch (tok) {
794 /* report partial linebreak - it might be the last token */
795@@ -5164,6 +5395,9 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
796 return XML_ERROR_NO_MEMORY;
797 }
798 entity->open = XML_TRUE;
799+#ifdef XML_DTD
800+ entityTrackingOnOpen(parser, entity, __LINE__);
801+#endif
802 entity->processed = 0;
803 openEntity->next = parser->m_openInternalEntities;
804 parser->m_openInternalEntities = openEntity;
805@@ -5182,17 +5416,22 @@ processInternalEntity(XML_Parser parser, ENTITY *entity, XML_Bool betweenDecl) {
806 int tok
807 = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
808 result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
809- tok, next, &next, XML_FALSE, XML_FALSE);
810+ tok, next, &next, XML_FALSE, XML_FALSE,
811+ XML_ACCOUNT_ENTITY_EXPANSION);
812 } else
813 #endif /* XML_DTD */
814 result = doContent(parser, parser->m_tagLevel, parser->m_internalEncoding,
815- textStart, textEnd, &next, XML_FALSE);
816+ textStart, textEnd, &next, XML_FALSE,
817+ XML_ACCOUNT_ENTITY_EXPANSION);
818
819 if (result == XML_ERROR_NONE) {
820 if (textEnd != next && parser->m_parsingStatus.parsing == XML_SUSPENDED) {
821 entity->processed = (int)(next - textStart);
822 parser->m_processor = internalEntityProcessor;
823 } else {
824+#ifdef XML_DTD
825+ entityTrackingOnClose(parser, entity, __LINE__);
826+#endif /* XML_DTD */
827 entity->open = XML_FALSE;
828 parser->m_openInternalEntities = openEntity->next;
829 /* put openEntity back in list of free instances */
830@@ -5225,12 +5464,13 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
831 int tok
832 = XmlPrologTok(parser->m_internalEncoding, textStart, textEnd, &next);
833 result = doProlog(parser, parser->m_internalEncoding, textStart, textEnd,
834- tok, next, &next, XML_FALSE, XML_TRUE);
835+ tok, next, &next, XML_FALSE, XML_TRUE,
836+ XML_ACCOUNT_ENTITY_EXPANSION);
837 } else
838 #endif /* XML_DTD */
839 result = doContent(parser, openEntity->startTagLevel,
840 parser->m_internalEncoding, textStart, textEnd, &next,
841- XML_FALSE);
842+ XML_FALSE, XML_ACCOUNT_ENTITY_EXPANSION);
843
844 if (result != XML_ERROR_NONE)
845 return result;
846@@ -5239,6 +5479,9 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
847 entity->processed = (int)(next - (char *)entity->textPtr);
848 return result;
849 } else {
850+#ifdef XML_DTD
851+ entityTrackingOnClose(parser, entity, __LINE__);
852+#endif
853 entity->open = XML_FALSE;
854 parser->m_openInternalEntities = openEntity->next;
855 /* put openEntity back in list of free instances */
856@@ -5252,7 +5495,8 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
857 parser->m_processor = prologProcessor;
858 tok = XmlPrologTok(parser->m_encoding, s, end, &next);
859 return doProlog(parser, parser->m_encoding, s, end, tok, next, nextPtr,
860- (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE);
861+ (XML_Bool)! parser->m_parsingStatus.finalBuffer, XML_TRUE,
862+ XML_ACCOUNT_DIRECT);
863 } else
864 #endif /* XML_DTD */
865 {
866@@ -5260,7 +5504,8 @@ internalEntityProcessor(XML_Parser parser, const char *s, const char *end,
867 /* see externalEntityContentProcessor vs contentProcessor */
868 return doContent(parser, parser->m_parentParser ? 1 : 0, parser->m_encoding,
869 s, end, nextPtr,
870- (XML_Bool)! parser->m_parsingStatus.finalBuffer);
871+ (XML_Bool)! parser->m_parsingStatus.finalBuffer,
872+ XML_ACCOUNT_DIRECT);
873 }
874 }
875
876@@ -5275,9 +5520,10 @@ errorProcessor(XML_Parser parser, const char *s, const char *end,
877
878 static enum XML_Error
879 storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
880- const char *ptr, const char *end, STRING_POOL *pool) {
881+ const char *ptr, const char *end, STRING_POOL *pool,
882+ enum XML_Account account) {
883 enum XML_Error result
884- = appendAttributeValue(parser, enc, isCdata, ptr, end, pool);
885+ = appendAttributeValue(parser, enc, isCdata, ptr, end, pool, account);
886 if (result)
887 return result;
888 if (! isCdata && poolLength(pool) && poolLastChar(pool) == 0x20)
889@@ -5289,11 +5535,22 @@ storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
890
891 static enum XML_Error
892 appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
893- const char *ptr, const char *end, STRING_POOL *pool) {
894+ const char *ptr, const char *end, STRING_POOL *pool,
895+ enum XML_Account account) {
896 DTD *const dtd = parser->m_dtd; /* save one level of indirection */
897+#ifndef XML_DTD
898+ UNUSED_P(account);
899+#endif
900+
901 for (;;) {
902 const char *next;
903 int tok = XmlAttributeValueTok(enc, ptr, end, &next);
904+#ifdef XML_DTD
905+ if (! accountingDiffTolerated(parser, tok, ptr, next, __LINE__, account)) {
906+ accountingOnAbort(parser);
907+ return XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
908+ }
909+#endif
910 switch (tok) {
911 case XML_TOK_NONE:
912 return XML_ERROR_NONE;
913@@ -5353,6 +5610,14 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
914 XML_Char ch = (XML_Char)XmlPredefinedEntityName(
915 enc, ptr + enc->minBytesPerChar, next - enc->minBytesPerChar);
916 if (ch) {
917+#ifdef XML_DTD
918+ /* NOTE: We are replacing 4-6 characters original input for 1 character
919+ * so there is no amplification and hence recording without
920+ * protection. */
921+ accountingDiffTolerated(parser, tok, (char *)&ch,
922+ ((char *)&ch) + sizeof(XML_Char), __LINE__,
923+ XML_ACCOUNT_ENTITY_EXPANSION);
924+#endif /* XML_DTD */
925 if (! poolAppendChar(pool, ch))
926 return XML_ERROR_NO_MEMORY;
927 break;
928@@ -5430,9 +5695,16 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
929 enum XML_Error result;
930 const XML_Char *textEnd = entity->textPtr + entity->textLen;
931 entity->open = XML_TRUE;
932+#ifdef XML_DTD
933+ entityTrackingOnOpen(parser, entity, __LINE__);
934+#endif
935 result = appendAttributeValue(parser, parser->m_internalEncoding,
936- isCdata, (char *)entity->textPtr,
937- (char *)textEnd, pool);
938+ isCdata, (const char *)entity->textPtr,
939+ (const char *)textEnd, pool,
940+ XML_ACCOUNT_ENTITY_EXPANSION);
941+#ifdef XML_DTD
942+ entityTrackingOnClose(parser, entity, __LINE__);
943+#endif
944 entity->open = XML_FALSE;
945 if (result)
946 return result;
947@@ -5462,13 +5734,16 @@ appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata,
948
949 static enum XML_Error
950 storeEntityValue(XML_Parser parser, const ENCODING *enc,
951- const char *entityTextPtr, const char *entityTextEnd) {
952+ const char *entityTextPtr, const char *entityTextEnd,
953+ enum XML_Account account) {
954 DTD *const dtd = parser->m_dtd; /* save one level of indirection */
955 STRING_POOL *pool = &(dtd->entityValuePool);
956 enum XML_Error result = XML_ERROR_NONE;
957 #ifdef XML_DTD
958 int oldInEntityValue = parser->m_prologState.inEntityValue;
959 parser->m_prologState.inEntityValue = 1;
960+#else
961+ UNUSED_P(account);
962 #endif /* XML_DTD */
963 /* never return Null for the value argument in EntityDeclHandler,
964 since this would indicate an external entity; therefore we
965@@ -5481,6 +5756,16 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
966 for (;;) {
967 const char *next;
968 int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next);
969+
970+#ifdef XML_DTD
971+ if (! accountingDiffTolerated(parser, tok, entityTextPtr, next, __LINE__,
972+ account)) {
973+ accountingOnAbort(parser);
974+ result = XML_ERROR_AMPLIFICATION_LIMIT_BREACH;
975+ goto endEntityValue;
976+ }
977+#endif
978+
979 switch (tok) {
980 case XML_TOK_PARAM_ENTITY_REF:
981 #ifdef XML_DTD
982@@ -5516,13 +5801,16 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
983 if (parser->m_externalEntityRefHandler) {
984 dtd->paramEntityRead = XML_FALSE;
985 entity->open = XML_TRUE;
986+ entityTrackingOnOpen(parser, entity, __LINE__);
987 if (! parser->m_externalEntityRefHandler(
988 parser->m_externalEntityRefHandlerArg, 0, entity->base,
989 entity->systemId, entity->publicId)) {
990+ entityTrackingOnClose(parser, entity, __LINE__);
991 entity->open = XML_FALSE;
992 result = XML_ERROR_EXTERNAL_ENTITY_HANDLING;
993 goto endEntityValue;
994 }
995+ entityTrackingOnClose(parser, entity, __LINE__);
996 entity->open = XML_FALSE;
997 if (! dtd->paramEntityRead)
998 dtd->keepProcessing = dtd->standalone;
999@@ -5530,9 +5818,12 @@ storeEntityValue(XML_Parser parser, const ENCODING *enc,
1000 dtd->keepProcessing = dtd->standalone;
1001 } else {
1002 entity->open = XML_TRUE;
1003+ entityTrackingOnOpen(parser, entity, __LINE__);
1004 result = storeEntityValue(
1005- parser, parser->m_internalEncoding, (char *)entity->textPtr,
1006- (char *)(entity->textPtr + entity->textLen));
1007+ parser, parser->m_internalEncoding, (const char *)entity->textPtr,
1008+ (const char *)(entity->textPtr + entity->textLen),
1009+ XML_ACCOUNT_ENTITY_EXPANSION);
1010+ entityTrackingOnClose(parser, entity, __LINE__);
1011 entity->open = XML_FALSE;
1012 if (result)
1013 goto endEntityValue;
1014@@ -6893,3 +7184,741 @@ copyString(const XML_Char *s, const XML_Memory_Handling_Suite *memsuite) {
1015 memcpy(result, s, charsRequired * sizeof(XML_Char));
1016 return result;
1017 }
1018+
1019+#ifdef XML_DTD
1020+
1021+static float
1022+accountingGetCurrentAmplification(XML_Parser rootParser) {
1023+ const XmlBigCount countBytesOutput
1024+ = rootParser->m_accounting.countBytesDirect
1025+ + rootParser->m_accounting.countBytesIndirect;
1026+ const float amplificationFactor
1027+ = rootParser->m_accounting.countBytesDirect
1028+ ? (countBytesOutput
1029+ / (float)(rootParser->m_accounting.countBytesDirect))
1030+ : 1.0f;
1031+ assert(! rootParser->m_parentParser);
1032+ return amplificationFactor;
1033+}
1034+
1035+static void
1036+accountingReportStats(XML_Parser originParser, const char *epilog) {
1037+ const XML_Parser rootParser = getRootParserOf(originParser, NULL);
1038+ assert(! rootParser->m_parentParser);
1039+
1040+ if (rootParser->m_accounting.debugLevel < 1) {
1041+ return;
1042+ }
1043+
1044+ const float amplificationFactor
1045+ = accountingGetCurrentAmplification(rootParser);
1046+ fprintf(stderr,
1047+ "expat: Accounting(%p): Direct " EXPAT_FMT_ULL(
1048+ "10") ", indirect " EXPAT_FMT_ULL("10") ", amplification %8.2f%s",
1049+ (void *)rootParser, rootParser->m_accounting.countBytesDirect,
1050+ rootParser->m_accounting.countBytesIndirect,
1051+ (double)amplificationFactor, epilog);
1052+}
1053+
1054+static void
1055+accountingOnAbort(XML_Parser originParser) {
1056+ accountingReportStats(originParser, " ABORTING\n");
1057+}
1058+
1059+static void
1060+accountingReportDiff(XML_Parser rootParser,
1061+ unsigned int levelsAwayFromRootParser, const char *before,
1062+ const char *after, ptrdiff_t bytesMore, int source_line,
1063+ enum XML_Account account) {
1064+ assert(! rootParser->m_parentParser);
1065+
1066+ fprintf(stderr,
1067+ " (+" EXPAT_FMT_PTRDIFF_T("6") " bytes %s|%d, xmlparse.c:%d) %*s\"",
1068+ bytesMore, (account == XML_ACCOUNT_DIRECT) ? "DIR" : "EXP",
1069+ levelsAwayFromRootParser, source_line, 10, "");
1070+
1071+ const char ellipis[] = "[..]";
1072+ const size_t ellipsisLength = sizeof(ellipis) /* because compile-time */ - 1;
1073+ const unsigned int contextLength = 10;
1074+
1075+ /* Note: Performance is of no concern here */
1076+ const char *walker = before;
1077+ if ((rootParser->m_accounting.debugLevel >= 3)
1078+ || (after - before)
1079+ <= (ptrdiff_t)(contextLength + ellipsisLength + contextLength)) {
1080+ for (; walker < after; walker++) {
1081+ fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
1082+ }
1083+ } else {
1084+ for (; walker < before + contextLength; walker++) {
1085+ fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
1086+ }
1087+ fprintf(stderr, ellipis);
1088+ walker = after - contextLength;
1089+ for (; walker < after; walker++) {
1090+ fprintf(stderr, "%s", unsignedCharToPrintable(walker[0]));
1091+ }
1092+ }
1093+ fprintf(stderr, "\"\n");
1094+}
1095+
1096+static XML_Bool
1097+accountingDiffTolerated(XML_Parser originParser, int tok, const char *before,
1098+ const char *after, int source_line,
1099+ enum XML_Account account) {
1100+ /* Note: We need to check the token type *first* to be sure that
1101+ * we can even access variable <after>, safely.
1102+ * E.g. for XML_TOK_NONE <after> may hold an invalid pointer. */
1103+ switch (tok) {
1104+ case XML_TOK_INVALID:
1105+ case XML_TOK_PARTIAL:
1106+ case XML_TOK_PARTIAL_CHAR:
1107+ case XML_TOK_NONE:
1108+ return XML_TRUE;
1109+ }
1110+
1111+ if (account == XML_ACCOUNT_NONE)
1112+ return XML_TRUE; /* because these bytes have been accounted for, already */
1113+
1114+ unsigned int levelsAwayFromRootParser;
1115+ const XML_Parser rootParser
1116+ = getRootParserOf(originParser, &levelsAwayFromRootParser);
1117+ assert(! rootParser->m_parentParser);
1118+
1119+ const int isDirect
1120+ = (account == XML_ACCOUNT_DIRECT) && (originParser == rootParser);
1121+ const ptrdiff_t bytesMore = after - before;
1122+
1123+ XmlBigCount *const additionTarget
1124+ = isDirect ? &rootParser->m_accounting.countBytesDirect
1125+ : &rootParser->m_accounting.countBytesIndirect;
1126+
1127+ /* Detect and avoid integer overflow */
1128+ if (*additionTarget > (XmlBigCount)(-1) - (XmlBigCount)bytesMore)
1129+ return XML_FALSE;
1130+ *additionTarget += bytesMore;
1131+
1132+ const XmlBigCount countBytesOutput
1133+ = rootParser->m_accounting.countBytesDirect
1134+ + rootParser->m_accounting.countBytesIndirect;
1135+ const float amplificationFactor
1136+ = accountingGetCurrentAmplification(rootParser);
1137+ const XML_Bool tolerated
1138+ = (countBytesOutput < rootParser->m_accounting.activationThresholdBytes)
1139+ || (amplificationFactor
1140+ <= rootParser->m_accounting.maximumAmplificationFactor);
1141+
1142+ if (rootParser->m_accounting.debugLevel >= 2) {
1143+ accountingReportStats(rootParser, "");
1144+ accountingReportDiff(rootParser, levelsAwayFromRootParser, before, after,
1145+ bytesMore, source_line, account);
1146+ }
1147+
1148+ return tolerated;
1149+}
1150+
1151+static void
1152+entityTrackingReportStats(XML_Parser rootParser, ENTITY *entity,
1153+ const char *action, int sourceLine) {
1154+ assert(! rootParser->m_parentParser);
1155+ if (rootParser->m_entity_stats.debugLevel < 1)
1156+ return;
1157+
1158+# if defined(XML_UNICODE)
1159+ const char *const entityName = "[..]";
1160+# else
1161+ const char *const entityName = entity->name;
1162+# endif
1163+
1164+ fprintf(
1165+ stderr,
1166+ "expat: Entities(%p): Count %9d, depth %2d/%2d %*s%s%s; %s length %d (xmlparse.c:%d)\n",
1167+ (void *)rootParser, rootParser->m_entity_stats.countEverOpened,
1168+ rootParser->m_entity_stats.currentDepth,
1169+ rootParser->m_entity_stats.maximumDepthSeen,
1170+ (rootParser->m_entity_stats.currentDepth - 1) * 2, "",
1171+ entity->is_param ? "%" : "&", entityName, action, entity->textLen,
1172+ sourceLine);
1173+}
1174+
1175+static void
1176+entityTrackingOnOpen(XML_Parser originParser, ENTITY *entity, int sourceLine) {
1177+ const XML_Parser rootParser = getRootParserOf(originParser, NULL);
1178+ assert(! rootParser->m_parentParser);
1179+
1180+ rootParser->m_entity_stats.countEverOpened++;
1181+ rootParser->m_entity_stats.currentDepth++;
1182+ if (rootParser->m_entity_stats.currentDepth
1183+ > rootParser->m_entity_stats.maximumDepthSeen) {
1184+ rootParser->m_entity_stats.maximumDepthSeen++;
1185+ }
1186+
1187+ entityTrackingReportStats(rootParser, entity, "OPEN ", sourceLine);
1188+}
1189+
1190+static void
1191+entityTrackingOnClose(XML_Parser originParser, ENTITY *entity, int sourceLine) {
1192+ const XML_Parser rootParser = getRootParserOf(originParser, NULL);
1193+ assert(! rootParser->m_parentParser);
1194+
1195+ entityTrackingReportStats(rootParser, entity, "CLOSE", sourceLine);
1196+ rootParser->m_entity_stats.currentDepth--;
1197+}
1198+
1199+static XML_Parser
1200+getRootParserOf(XML_Parser parser, unsigned int *outLevelDiff) {
1201+ XML_Parser rootParser = parser;
1202+ unsigned int stepsTakenUpwards = 0;
1203+ while (rootParser->m_parentParser) {
1204+ rootParser = rootParser->m_parentParser;
1205+ stepsTakenUpwards++;
1206+ }
1207+ assert(! rootParser->m_parentParser);
1208+ if (outLevelDiff != NULL) {
1209+ *outLevelDiff = stepsTakenUpwards;
1210+ }
1211+ return rootParser;
1212+}
1213+
1214+static const char *
1215+unsignedCharToPrintable(unsigned char c) {
1216+ switch (c) {
1217+ case 0:
1218+ return "\\0";
1219+ case 1:
1220+ return "\\x1";
1221+ case 2:
1222+ return "\\x2";
1223+ case 3:
1224+ return "\\x3";
1225+ case 4:
1226+ return "\\x4";
1227+ case 5:
1228+ return "\\x5";
1229+ case 6:
1230+ return "\\x6";
1231+ case 7:
1232+ return "\\x7";
1233+ case 8:
1234+ return "\\x8";
1235+ case 9:
1236+ return "\\t";
1237+ case 10:
1238+ return "\\n";
1239+ case 11:
1240+ return "\\xB";
1241+ case 12:
1242+ return "\\xC";
1243+ case 13:
1244+ return "\\r";
1245+ case 14:
1246+ return "\\xE";
1247+ case 15:
1248+ return "\\xF";
1249+ case 16:
1250+ return "\\x10";
1251+ case 17:
1252+ return "\\x11";
1253+ case 18:
1254+ return "\\x12";
1255+ case 19:
1256+ return "\\x13";
1257+ case 20:
1258+ return "\\x14";
1259+ case 21:
1260+ return "\\x15";
1261+ case 22:
1262+ return "\\x16";
1263+ case 23:
1264+ return "\\x17";
1265+ case 24:
1266+ return "\\x18";
1267+ case 25:
1268+ return "\\x19";
1269+ case 26:
1270+ return "\\x1A";
1271+ case 27:
1272+ return "\\x1B";
1273+ case 28:
1274+ return "\\x1C";
1275+ case 29:
1276+ return "\\x1D";
1277+ case 30:
1278+ return "\\x1E";
1279+ case 31:
1280+ return "\\x1F";
1281+ case 32:
1282+ return " ";
1283+ case 33:
1284+ return "!";
1285+ case 34:
1286+ return "\\\"";
1287+ case 35:
1288+ return "#";
1289+ case 36:
1290+ return "$";
1291+ case 37:
1292+ return "%";
1293+ case 38:
1294+ return "&";
1295+ case 39:
1296+ return "'";
1297+ case 40:
1298+ return "(";
1299+ case 41:
1300+ return ")";
1301+ case 42:
1302+ return "*";
1303+ case 43:
1304+ return "+";
1305+ case 44:
1306+ return ",";
1307+ case 45:
1308+ return "-";
1309+ case 46:
1310+ return ".";
1311+ case 47:
1312+ return "/";
1313+ case 48:
1314+ return "0";
1315+ case 49:
1316+ return "1";
1317+ case 50:
1318+ return "2";
1319+ case 51:
1320+ return "3";
1321+ case 52:
1322+ return "4";
1323+ case 53:
1324+ return "5";
1325+ case 54:
1326+ return "6";
1327+ case 55:
1328+ return "7";
1329+ case 56:
1330+ return "8";
1331+ case 57:
1332+ return "9";
1333+ case 58:
1334+ return ":";
1335+ case 59:
1336+ return ";";
1337+ case 60:
1338+ return "<";
1339+ case 61:
1340+ return "=";
1341+ case 62:
1342+ return ">";
1343+ case 63:
1344+ return "?";
1345+ case 64:
1346+ return "@";
1347+ case 65:
1348+ return "A";
1349+ case 66:
1350+ return "B";
1351+ case 67:
1352+ return "C";
1353+ case 68:
1354+ return "D";
1355+ case 69:
1356+ return "E";
1357+ case 70:
1358+ return "F";
1359+ case 71:
1360+ return "G";
1361+ case 72:
1362+ return "H";
1363+ case 73:
1364+ return "I";
1365+ case 74:
1366+ return "J";
1367+ case 75:
1368+ return "K";
1369+ case 76:
1370+ return "L";
1371+ case 77:
1372+ return "M";
1373+ case 78:
1374+ return "N";
1375+ case 79:
1376+ return "O";
1377+ case 80:
1378+ return "P";
1379+ case 81:
1380+ return "Q";
1381+ case 82:
1382+ return "R";
1383+ case 83:
1384+ return "S";
1385+ case 84:
1386+ return "T";
1387+ case 85:
1388+ return "U";
1389+ case 86:
1390+ return "V";
1391+ case 87:
1392+ return "W";
1393+ case 88:
1394+ return "X";
1395+ case 89:
1396+ return "Y";
1397+ case 90:
1398+ return "Z";
1399+ case 91:
1400+ return "[";
1401+ case 92:
1402+ return "\\\\";
1403+ case 93:
1404+ return "]";
1405+ case 94:
1406+ return "^";
1407+ case 95:
1408+ return "_";
1409+ case 96:
1410+ return "`";
1411+ case 97:
1412+ return "a";
1413+ case 98:
1414+ return "b";
1415+ case 99:
1416+ return "c";
1417+ case 100:
1418+ return "d";
1419+ case 101:
1420+ return "e";
1421+ case 102:
1422+ return "f";
1423+ case 103:
1424+ return "g";
1425+ case 104:
1426+ return "h";
1427+ case 105:
1428+ return "i";
1429+ case 106:
1430+ return "j";
1431+ case 107:
1432+ return "k";
1433+ case 108:
1434+ return "l";
1435+ case 109:
1436+ return "m";
1437+ case 110:
1438+ return "n";
1439+ case 111:
1440+ return "o";
1441+ case 112:
1442+ return "p";
1443+ case 113:
1444+ return "q";
1445+ case 114:
1446+ return "r";
1447+ case 115:
1448+ return "s";
1449+ case 116:
1450+ return "t";
1451+ case 117:
1452+ return "u";
1453+ case 118:
1454+ return "v";
1455+ case 119:
1456+ return "w";
1457+ case 120:
1458+ return "x";
1459+ case 121:
1460+ return "y";
1461+ case 122:
1462+ return "z";
1463+ case 123:
1464+ return "{";
1465+ case 124:
1466+ return "|";
1467+ case 125:
1468+ return "}";
1469+ case 126:
1470+ return "~";
1471+ case 127:
1472+ return "\\x7F";
1473+ case 128:
1474+ return "\\x80";
1475+ case 129:
1476+ return "\\x81";
1477+ case 130:
1478+ return "\\x82";
1479+ case 131:
1480+ return "\\x83";
1481+ case 132:
1482+ return "\\x84";
1483+ case 133:
1484+ return "\\x85";
1485+ case 134:
1486+ return "\\x86";
1487+ case 135:
1488+ return "\\x87";
1489+ case 136:
1490+ return "\\x88";
1491+ case 137:
1492+ return "\\x89";
1493+ case 138:
1494+ return "\\x8A";
1495+ case 139:
1496+ return "\\x8B";
1497+ case 140:
1498+ return "\\x8C";
1499+ case 141:
1500+ return "\\x8D";
1501+ case 142:
1502+ return "\\x8E";
1503+ case 143:
1504+ return "\\x8F";
1505+ case 144:
1506+ return "\\x90";
1507+ case 145:
1508+ return "\\x91";
1509+ case 146:
1510+ return "\\x92";
1511+ case 147:
1512+ return "\\x93";
1513+ case 148:
1514+ return "\\x94";
1515+ case 149:
1516+ return "\\x95";
1517+ case 150:
1518+ return "\\x96";
1519+ case 151:
1520+ return "\\x97";
1521+ case 152:
1522+ return "\\x98";
1523+ case 153:
1524+ return "\\x99";
1525+ case 154:
1526+ return "\\x9A";
1527+ case 155:
1528+ return "\\x9B";
1529+ case 156:
1530+ return "\\x9C";
1531+ case 157:
1532+ return "\\x9D";
1533+ case 158:
1534+ return "\\x9E";
1535+ case 159:
1536+ return "\\x9F";
1537+ case 160:
1538+ return "\\xA0";
1539+ case 161:
1540+ return "\\xA1";
1541+ case 162:
1542+ return "\\xA2";
1543+ case 163:
1544+ return "\\xA3";
1545+ case 164:
1546+ return "\\xA4";
1547+ case 165:
1548+ return "\\xA5";
1549+ case 166:
1550+ return "\\xA6";
1551+ case 167:
1552+ return "\\xA7";
1553+ case 168:
1554+ return "\\xA8";
1555+ case 169:
1556+ return "\\xA9";
1557+ case 170:
1558+ return "\\xAA";
1559+ case 171:
1560+ return "\\xAB";
1561+ case 172:
1562+ return "\\xAC";
1563+ case 173:
1564+ return "\\xAD";
1565+ case 174:
1566+ return "\\xAE";
1567+ case 175:
1568+ return "\\xAF";
1569+ case 176:
1570+ return "\\xB0";
1571+ case 177:
1572+ return "\\xB1";
1573+ case 178:
1574+ return "\\xB2";
1575+ case 179:
1576+ return "\\xB3";
1577+ case 180:
1578+ return "\\xB4";
1579+ case 181:
1580+ return "\\xB5";
1581+ case 182:
1582+ return "\\xB6";
1583+ case 183:
1584+ return "\\xB7";
1585+ case 184:
1586+ return "\\xB8";
1587+ case 185:
1588+ return "\\xB9";
1589+ case 186:
1590+ return "\\xBA";
1591+ case 187:
1592+ return "\\xBB";
1593+ case 188:
1594+ return "\\xBC";
1595+ case 189:
1596+ return "\\xBD";
1597+ case 190:
1598+ return "\\xBE";
1599+ case 191:
1600+ return "\\xBF";
1601+ case 192:
1602+ return "\\xC0";
1603+ case 193:
1604+ return "\\xC1";
1605+ case 194:
1606+ return "\\xC2";
1607+ case 195:
1608+ return "\\xC3";
1609+ case 196:
1610+ return "\\xC4";
1611+ case 197:
1612+ return "\\xC5";
1613+ case 198:
1614+ return "\\xC6";
1615+ case 199:
1616+ return "\\xC7";
1617+ case 200:
1618+ return "\\xC8";
1619+ case 201:
1620+ return "\\xC9";
1621+ case 202:
1622+ return "\\xCA";
1623+ case 203:
1624+ return "\\xCB";
1625+ case 204:
1626+ return "\\xCC";
1627+ case 205:
1628+ return "\\xCD";
1629+ case 206:
1630+ return "\\xCE";
1631+ case 207:
1632+ return "\\xCF";
1633+ case 208:
1634+ return "\\xD0";
1635+ case 209:
1636+ return "\\xD1";
1637+ case 210:
1638+ return "\\xD2";
1639+ case 211:
1640+ return "\\xD3";
1641+ case 212:
1642+ return "\\xD4";
1643+ case 213:
1644+ return "\\xD5";
1645+ case 214:
1646+ return "\\xD6";
1647+ case 215:
1648+ return "\\xD7";
1649+ case 216:
1650+ return "\\xD8";
1651+ case 217:
1652+ return "\\xD9";
1653+ case 218:
1654+ return "\\xDA";
1655+ case 219:
1656+ return "\\xDB";
1657+ case 220:
1658+ return "\\xDC";
1659+ case 221:
1660+ return "\\xDD";
1661+ case 222:
1662+ return "\\xDE";
1663+ case 223:
1664+ return "\\xDF";
1665+ case 224:
1666+ return "\\xE0";
1667+ case 225:
1668+ return "\\xE1";
1669+ case 226:
1670+ return "\\xE2";
1671+ case 227:
1672+ return "\\xE3";
1673+ case 228:
1674+ return "\\xE4";
1675+ case 229:
1676+ return "\\xE5";
1677+ case 230:
1678+ return "\\xE6";
1679+ case 231:
1680+ return "\\xE7";
1681+ case 232:
1682+ return "\\xE8";
1683+ case 233:
1684+ return "\\xE9";
1685+ case 234:
1686+ return "\\xEA";
1687+ case 235:
1688+ return "\\xEB";
1689+ case 236:
1690+ return "\\xEC";
1691+ case 237:
1692+ return "\\xED";
1693+ case 238:
1694+ return "\\xEE";
1695+ case 239:
1696+ return "\\xEF";
1697+ case 240:
1698+ return "\\xF0";
1699+ case 241:
1700+ return "\\xF1";
1701+ case 242:
1702+ return "\\xF2";
1703+ case 243:
1704+ return "\\xF3";
1705+ case 244:
1706+ return "\\xF4";
1707+ case 245:
1708+ return "\\xF5";
1709+ case 246:
1710+ return "\\xF6";
1711+ case 247:
1712+ return "\\xF7";
1713+ case 248:
1714+ return "\\xF8";
1715+ case 249:
1716+ return "\\xF9";
1717+ case 250:
1718+ return "\\xFA";
1719+ case 251:
1720+ return "\\xFB";
1721+ case 252:
1722+ return "\\xFC";
1723+ case 253:
1724+ return "\\xFD";
1725+ case 254:
1726+ return "\\xFE";
1727+ case 255:
1728+ return "\\xFF";
1729+ default:
1730+ assert(0); /* never gets here */
1731+ return "dead code";
1732+ }
1733+ assert(0); /* never gets here */
1734+}
1735+
1736+#endif /* XML_DTD */
1737+
1738+static unsigned long
1739+getDebugLevel(const char *variableName, unsigned long defaultDebugLevel) {
1740+ const char *const valueOrNull = getenv(variableName);
1741+ if (valueOrNull == NULL) {
1742+ return defaultDebugLevel;
1743+ }
1744+ const char *const value = valueOrNull;
1745+
1746+ errno = 0;
1747+ char *afterValue = (char *)value;
1748+ unsigned long debugLevel = strtoul(value, &afterValue, 10);
1749+ if ((errno != 0) || (afterValue[0] != '\0')) {
1750+ errno = 0;
1751+ return defaultDebugLevel;
1752+ }
1753+
1754+ return debugLevel;
1755+}
1756--
17572.32.0
1758