1bd8f1dc3Sbluhm /* Tests in the "accounting" test case for the Expat test suite
2bd8f1dc3Sbluhm __ __ _
3bd8f1dc3Sbluhm ___\ \/ /_ __ __ _| |_
4bd8f1dc3Sbluhm / _ \\ /| '_ \ / _` | __|
5bd8f1dc3Sbluhm | __// \| |_) | (_| | |_
6bd8f1dc3Sbluhm \___/_/\_\ .__/ \__,_|\__|
7bd8f1dc3Sbluhm |_| XML parser
8bd8f1dc3Sbluhm
9bd8f1dc3Sbluhm Copyright (c) 2001-2006 Fred L. Drake, Jr. <fdrake@users.sourceforge.net>
10bd8f1dc3Sbluhm Copyright (c) 2003 Greg Stein <gstein@users.sourceforge.net>
11bd8f1dc3Sbluhm Copyright (c) 2005-2007 Steven Solie <steven@solie.ca>
12bd8f1dc3Sbluhm Copyright (c) 2005-2012 Karl Waclawek <karl@waclawek.net>
13bd8f1dc3Sbluhm Copyright (c) 2016-2024 Sebastian Pipping <sebastian@pipping.org>
14bd8f1dc3Sbluhm Copyright (c) 2017-2022 Rhodri James <rhodri@wildebeest.org.uk>
15bd8f1dc3Sbluhm Copyright (c) 2017 Joe Orton <jorton@redhat.com>
16bd8f1dc3Sbluhm Copyright (c) 2017 José Gutiérrez de la Concha <jose@zeroc.com>
17bd8f1dc3Sbluhm Copyright (c) 2018 Marco Maggi <marco.maggi-ipsu@poste.it>
18bd8f1dc3Sbluhm Copyright (c) 2019 David Loffredo <loffredo@steptools.com>
19bd8f1dc3Sbluhm Copyright (c) 2020 Tim Gates <tim.gates@iress.com>
20bd8f1dc3Sbluhm Copyright (c) 2021 Donghee Na <donghee.na@python.org>
21bd8f1dc3Sbluhm Copyright (c) 2023 Sony Corporation / Snild Dolkow <snild@sony.com>
22bd8f1dc3Sbluhm Licensed under the MIT license:
23bd8f1dc3Sbluhm
24bd8f1dc3Sbluhm Permission is hereby granted, free of charge, to any person obtaining
25bd8f1dc3Sbluhm a copy of this software and associated documentation files (the
26bd8f1dc3Sbluhm "Software"), to deal in the Software without restriction, including
27bd8f1dc3Sbluhm without limitation the rights to use, copy, modify, merge, publish,
28bd8f1dc3Sbluhm distribute, sublicense, and/or sell copies of the Software, and to permit
29bd8f1dc3Sbluhm persons to whom the Software is furnished to do so, subject to the
30bd8f1dc3Sbluhm following conditions:
31bd8f1dc3Sbluhm
32bd8f1dc3Sbluhm The above copyright notice and this permission notice shall be included
33bd8f1dc3Sbluhm in all copies or substantial portions of the Software.
34bd8f1dc3Sbluhm
35bd8f1dc3Sbluhm THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
36bd8f1dc3Sbluhm EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
37bd8f1dc3Sbluhm MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
38bd8f1dc3Sbluhm NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
39bd8f1dc3Sbluhm DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
40bd8f1dc3Sbluhm OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
41bd8f1dc3Sbluhm USE OR OTHER DEALINGS IN THE SOFTWARE.
42bd8f1dc3Sbluhm */
43bd8f1dc3Sbluhm
44bd8f1dc3Sbluhm #include <math.h> /* NAN, INFINITY */
45bd8f1dc3Sbluhm #include <stdio.h>
46bd8f1dc3Sbluhm #include <string.h>
47bd8f1dc3Sbluhm
48bd8f1dc3Sbluhm #include "expat_config.h"
49bd8f1dc3Sbluhm
50bd8f1dc3Sbluhm #include "expat.h"
51bd8f1dc3Sbluhm #include "internal.h"
52bd8f1dc3Sbluhm #include "common.h"
53bd8f1dc3Sbluhm #include "minicheck.h"
54bd8f1dc3Sbluhm #include "chardata.h"
55bd8f1dc3Sbluhm #include "handlers.h"
56bd8f1dc3Sbluhm #include "acc_tests.h"
57bd8f1dc3Sbluhm
58bd8f1dc3Sbluhm #if XML_GE == 1
START_TEST(test_accounting_precision)59bd8f1dc3Sbluhm START_TEST(test_accounting_precision) {
60bd8f1dc3Sbluhm struct AccountingTestCase cases[] = {
61bd8f1dc3Sbluhm {"<e/>", NULL, NULL, 0},
62bd8f1dc3Sbluhm {"<e></e>", NULL, NULL, 0},
63bd8f1dc3Sbluhm
64bd8f1dc3Sbluhm /* Attributes */
65bd8f1dc3Sbluhm {"<e k1=\"v2\" k2=\"v2\"/>", NULL, NULL, 0},
66bd8f1dc3Sbluhm {"<e k1=\"v2\" k2=\"v2\"></e>", NULL, NULL, 0},
67bd8f1dc3Sbluhm {"<p:e xmlns:p=\"https://domain.invalid/\" />", NULL, NULL, 0},
68bd8f1dc3Sbluhm {"<e k=\"&'><"\" />", NULL, NULL,
69bd8f1dc3Sbluhm sizeof(XML_Char) * 5 /* number of predefined entities */},
70bd8f1dc3Sbluhm {"<e1 xmlns='https://example.org/'>\n"
71bd8f1dc3Sbluhm " <e2 xmlns=''/>\n"
72bd8f1dc3Sbluhm "</e1>",
73bd8f1dc3Sbluhm NULL, NULL, 0},
74bd8f1dc3Sbluhm
75bd8f1dc3Sbluhm /* Text */
76bd8f1dc3Sbluhm {"<e>text</e>", NULL, NULL, 0},
77bd8f1dc3Sbluhm {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0},
78bd8f1dc3Sbluhm {"<e>&'><"</e>", NULL, NULL,
79bd8f1dc3Sbluhm sizeof(XML_Char) * 5 /* number of predefined entities */},
80bd8f1dc3Sbluhm {"<e>A)</e>", NULL, NULL, 0},
81bd8f1dc3Sbluhm
82bd8f1dc3Sbluhm /* Prolog */
83bd8f1dc3Sbluhm {"<?xml version=\"1.0\"?><root/>", NULL, NULL, 0},
84bd8f1dc3Sbluhm
85bd8f1dc3Sbluhm /* Whitespace */
86bd8f1dc3Sbluhm {" <e1> <e2> </e2> </e1> ", NULL, NULL, 0},
87bd8f1dc3Sbluhm {"<e1 ><e2 /></e1 >", NULL, NULL, 0},
88bd8f1dc3Sbluhm {"<e1><e2 k = \"v\"/><e3 k = 'v'/></e1>", NULL, NULL, 0},
89bd8f1dc3Sbluhm
90bd8f1dc3Sbluhm /* Comments */
91bd8f1dc3Sbluhm {"<!-- Comment --><e><!-- Comment --></e>", NULL, NULL, 0},
92bd8f1dc3Sbluhm
93bd8f1dc3Sbluhm /* Processing instructions */
94bd8f1dc3Sbluhm {"<?xml-stylesheet type=\"text/xsl\" href=\"https://domain.invalid/\" media=\"all\"?><e/>",
95bd8f1dc3Sbluhm NULL, NULL, 0},
96bd8f1dc3Sbluhm {"<?pi0?><?pi1 ?><?pi2 ?><r/><?pi4?>", NULL, NULL, 0},
97bd8f1dc3Sbluhm # ifdef XML_DTD
98bd8f1dc3Sbluhm {"<?pi0?><?pi1 ?><?pi2 ?><!DOCTYPE r SYSTEM 'first.ent'><r/>",
99bd8f1dc3Sbluhm "<?pi3?><!ENTITY % e1 SYSTEM 'second.ent'><?pi4?>%e1;<?pi5?>", "<?pi6?>",
100bd8f1dc3Sbluhm 0},
101bd8f1dc3Sbluhm # endif /* XML_DTD */
102bd8f1dc3Sbluhm
103bd8f1dc3Sbluhm /* CDATA */
104bd8f1dc3Sbluhm {"<e><![CDATA[one two three]]></e>", NULL, NULL, 0},
105bd8f1dc3Sbluhm /* The following is the essence of this OSS-Fuzz finding:
106bd8f1dc3Sbluhm https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=34302
107bd8f1dc3Sbluhm https://oss-fuzz.com/testcase-detail/4860575394955264
108bd8f1dc3Sbluhm */
109bd8f1dc3Sbluhm {"<!DOCTYPE r [\n"
110bd8f1dc3Sbluhm "<!ENTITY e \"111<![CDATA[2 <= 2]]>333\">\n"
111bd8f1dc3Sbluhm "]>\n"
112bd8f1dc3Sbluhm "<r>&e;</r>\n",
113bd8f1dc3Sbluhm NULL, NULL, sizeof(XML_Char) * strlen("111<![CDATA[2 <= 2]]>333")},
114bd8f1dc3Sbluhm
115bd8f1dc3Sbluhm # ifdef XML_DTD
116bd8f1dc3Sbluhm /* Conditional sections */
117bd8f1dc3Sbluhm {"<!DOCTYPE r [\n"
118bd8f1dc3Sbluhm "<!ENTITY % draft 'INCLUDE'>\n"
119bd8f1dc3Sbluhm "<!ENTITY % final 'IGNORE'>\n"
120bd8f1dc3Sbluhm "<!ENTITY % import SYSTEM \"first.ent\">\n"
121bd8f1dc3Sbluhm "%import;\n"
122bd8f1dc3Sbluhm "]>\n"
123bd8f1dc3Sbluhm "<r/>\n",
124bd8f1dc3Sbluhm "<![%draft;[<!--1-->]]>\n"
125bd8f1dc3Sbluhm "<![%final;[<!--22-->]]>",
126bd8f1dc3Sbluhm NULL, sizeof(XML_Char) * (strlen("INCLUDE") + strlen("IGNORE"))},
127bd8f1dc3Sbluhm # endif /* XML_DTD */
128bd8f1dc3Sbluhm
129bd8f1dc3Sbluhm /* General entities */
130bd8f1dc3Sbluhm {"<!DOCTYPE root [\n"
131bd8f1dc3Sbluhm "<!ENTITY nine \"123456789\">\n"
132bd8f1dc3Sbluhm "]>\n"
133bd8f1dc3Sbluhm "<root>&nine;</root>",
134bd8f1dc3Sbluhm NULL, NULL, sizeof(XML_Char) * strlen("123456789")},
135bd8f1dc3Sbluhm {"<!DOCTYPE root [\n"
136bd8f1dc3Sbluhm "<!ENTITY nine \"123456789\">\n"
137bd8f1dc3Sbluhm "]>\n"
138bd8f1dc3Sbluhm "<root k1=\"&nine;\"/>",
139bd8f1dc3Sbluhm NULL, NULL, sizeof(XML_Char) * strlen("123456789")},
140bd8f1dc3Sbluhm {"<!DOCTYPE root [\n"
141bd8f1dc3Sbluhm "<!ENTITY nine \"123456789\">\n"
142bd8f1dc3Sbluhm "<!ENTITY nine2 \"&nine;&nine;\">\n"
143bd8f1dc3Sbluhm "]>\n"
144bd8f1dc3Sbluhm "<root>&nine2;&nine2;&nine2;</root>",
145bd8f1dc3Sbluhm NULL, NULL,
146bd8f1dc3Sbluhm sizeof(XML_Char) * 3 /* calls to &nine2; */ * 2 /* calls to &nine; */
147bd8f1dc3Sbluhm * (strlen("&nine;") + strlen("123456789"))},
148bd8f1dc3Sbluhm {"<!DOCTYPE r [\n"
149bd8f1dc3Sbluhm " <!ENTITY five SYSTEM 'first.ent'>\n"
150bd8f1dc3Sbluhm "]>\n"
151bd8f1dc3Sbluhm "<r>&five;</r>",
152bd8f1dc3Sbluhm "12345", NULL, 0},
153bd8f1dc3Sbluhm {"<!DOCTYPE r [\n"
154bd8f1dc3Sbluhm " <!ENTITY five SYSTEM 'first.ent'>\n"
155bd8f1dc3Sbluhm "]>\n"
156bd8f1dc3Sbluhm "<r>&five;</r>",
157bd8f1dc3Sbluhm "\xEF\xBB\xBF" /* UTF-8 BOM */, NULL, 0},
158bd8f1dc3Sbluhm
159bd8f1dc3Sbluhm # ifdef XML_DTD
160bd8f1dc3Sbluhm /* Parameter entities */
161bd8f1dc3Sbluhm {"<!DOCTYPE r [\n"
162bd8f1dc3Sbluhm "<!ENTITY % comment \"<!---->\">\n"
163bd8f1dc3Sbluhm "%comment;\n"
164bd8f1dc3Sbluhm "]>\n"
165bd8f1dc3Sbluhm "<r/>",
166bd8f1dc3Sbluhm NULL, NULL, sizeof(XML_Char) * strlen("<!---->")},
167bd8f1dc3Sbluhm {"<!DOCTYPE r [\n"
168bd8f1dc3Sbluhm "<!ENTITY % ninedef \"<!ENTITY nine "123456789">\">\n"
169bd8f1dc3Sbluhm "%ninedef;\n"
170bd8f1dc3Sbluhm "]>\n"
171bd8f1dc3Sbluhm "<r>&nine;</r>",
172bd8f1dc3Sbluhm NULL, NULL,
173bd8f1dc3Sbluhm sizeof(XML_Char)
174bd8f1dc3Sbluhm * (strlen("<!ENTITY nine \"123456789\">") + strlen("123456789"))},
175bd8f1dc3Sbluhm {"<!DOCTYPE r [\n"
176bd8f1dc3Sbluhm "<!ENTITY % comment \"<!--1-->\">\n"
177bd8f1dc3Sbluhm "<!ENTITY % comment2 \"%comment;<!--22-->%comment;\">\n"
178bd8f1dc3Sbluhm "%comment2;\n"
179bd8f1dc3Sbluhm "]>\n"
180bd8f1dc3Sbluhm "<r/>\n",
181bd8f1dc3Sbluhm NULL, NULL,
182bd8f1dc3Sbluhm sizeof(XML_Char)
183bd8f1dc3Sbluhm * (strlen("%comment;<!--22-->%comment;") + 2 * strlen("<!--1-->"))},
184bd8f1dc3Sbluhm {"<!DOCTYPE r [\n"
185bd8f1dc3Sbluhm " <!ENTITY % five \"12345\">\n"
186bd8f1dc3Sbluhm " <!ENTITY % five2def \"<!ENTITY five2 "[%five;][%five;]]]]">\">\n"
187bd8f1dc3Sbluhm " %five2def;\n"
188bd8f1dc3Sbluhm "]>\n"
189bd8f1dc3Sbluhm "<r>&five2;</r>",
190bd8f1dc3Sbluhm NULL, NULL, /* from "%five2def;": */
191bd8f1dc3Sbluhm sizeof(XML_Char)
192bd8f1dc3Sbluhm * (strlen("<!ENTITY five2 \"[%five;][%five;]]]]\">")
193bd8f1dc3Sbluhm + 2 /* calls to "%five;" */ * strlen("12345")
194bd8f1dc3Sbluhm + /* from "&five2;": */ strlen("[12345][12345]]]]"))},
195bd8f1dc3Sbluhm {"<!DOCTYPE r SYSTEM \"first.ent\">\n"
196bd8f1dc3Sbluhm "<r/>",
197bd8f1dc3Sbluhm "<!ENTITY % comment '<!--1-->'>\n"
198bd8f1dc3Sbluhm "<!ENTITY % comment2 '<!--22-->%comment;<!--22-->%comment;<!--22-->'>\n"
199bd8f1dc3Sbluhm "%comment2;",
200bd8f1dc3Sbluhm NULL,
201bd8f1dc3Sbluhm sizeof(XML_Char)
202bd8f1dc3Sbluhm * (strlen("<!--22-->%comment;<!--22-->%comment;<!--22-->")
203bd8f1dc3Sbluhm + 2 /* calls to "%comment;" */ * strlen("<!---->"))},
204bd8f1dc3Sbluhm {"<!DOCTYPE r SYSTEM 'first.ent'>\n"
205bd8f1dc3Sbluhm "<r/>",
206bd8f1dc3Sbluhm "<!ENTITY % e1 PUBLIC 'foo' 'second.ent'>\n"
207bd8f1dc3Sbluhm "<!ENTITY % e2 '<!--22-->%e1;<!--22-->'>\n"
208bd8f1dc3Sbluhm "%e2;\n",
209bd8f1dc3Sbluhm "<!--1-->", sizeof(XML_Char) * strlen("<!--22--><!--1--><!--22-->")},
210bd8f1dc3Sbluhm {
211bd8f1dc3Sbluhm "<!DOCTYPE r SYSTEM 'first.ent'>\n"
212bd8f1dc3Sbluhm "<r/>",
213bd8f1dc3Sbluhm "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
214bd8f1dc3Sbluhm "<!ENTITY % e2 '%e1;'>",
215bd8f1dc3Sbluhm "<?xml version='1.0' encoding='utf-8'?>\n"
216bd8f1dc3Sbluhm "hello\n"
217bd8f1dc3Sbluhm "xml" /* without trailing newline! */,
218bd8f1dc3Sbluhm 0,
219bd8f1dc3Sbluhm },
220bd8f1dc3Sbluhm {
221bd8f1dc3Sbluhm "<!DOCTYPE r SYSTEM 'first.ent'>\n"
222bd8f1dc3Sbluhm "<r/>",
223bd8f1dc3Sbluhm "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
224bd8f1dc3Sbluhm "<!ENTITY % e2 '%e1;'>",
225bd8f1dc3Sbluhm "<?xml version='1.0' encoding='utf-8'?>\n"
226bd8f1dc3Sbluhm "hello\n"
227bd8f1dc3Sbluhm "xml\n" /* with trailing newline! */,
228bd8f1dc3Sbluhm 0,
229bd8f1dc3Sbluhm },
230bd8f1dc3Sbluhm {"<!DOCTYPE doc SYSTEM 'first.ent'>\n"
231bd8f1dc3Sbluhm "<doc></doc>\n",
232bd8f1dc3Sbluhm "<!ELEMENT doc EMPTY>\n"
233bd8f1dc3Sbluhm "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
234bd8f1dc3Sbluhm "<!ENTITY % e2 '%e1;'>\n"
235bd8f1dc3Sbluhm "%e1;\n",
236bd8f1dc3Sbluhm "\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>" /* UTF-8 BOM */,
237bd8f1dc3Sbluhm strlen("\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>")},
238bd8f1dc3Sbluhm # endif /* XML_DTD */
239bd8f1dc3Sbluhm };
240bd8f1dc3Sbluhm
241bd8f1dc3Sbluhm const size_t countCases = sizeof(cases) / sizeof(cases[0]);
242bd8f1dc3Sbluhm size_t u = 0;
243bd8f1dc3Sbluhm for (; u < countCases; u++) {
244bd8f1dc3Sbluhm const unsigned long long expectedCountBytesDirect
245bd8f1dc3Sbluhm = strlen(cases[u].primaryText);
246bd8f1dc3Sbluhm const unsigned long long expectedCountBytesIndirect
247bd8f1dc3Sbluhm = (cases[u].firstExternalText ? strlen(cases[u].firstExternalText) : 0)
248bd8f1dc3Sbluhm + (cases[u].secondExternalText ? strlen(cases[u].secondExternalText)
249bd8f1dc3Sbluhm : 0)
250bd8f1dc3Sbluhm + cases[u].expectedCountBytesIndirectExtra;
251bd8f1dc3Sbluhm
252bd8f1dc3Sbluhm XML_Parser parser = XML_ParserCreate(NULL);
253bd8f1dc3Sbluhm XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
254bd8f1dc3Sbluhm if (cases[u].firstExternalText) {
255bd8f1dc3Sbluhm XML_SetExternalEntityRefHandler(parser,
256bd8f1dc3Sbluhm accounting_external_entity_ref_handler);
257bd8f1dc3Sbluhm XML_SetUserData(parser, (void *)&cases[u]);
258bd8f1dc3Sbluhm }
259bd8f1dc3Sbluhm
260bd8f1dc3Sbluhm enum XML_Status status
261bd8f1dc3Sbluhm = _XML_Parse_SINGLE_BYTES(parser, cases[u].primaryText,
262bd8f1dc3Sbluhm (int)strlen(cases[u].primaryText), XML_TRUE);
263bd8f1dc3Sbluhm if (status != XML_STATUS_OK) {
264bd8f1dc3Sbluhm _xml_failure(parser, __FILE__, __LINE__);
265bd8f1dc3Sbluhm }
266bd8f1dc3Sbluhm
267bd8f1dc3Sbluhm const unsigned long long actualCountBytesDirect
268bd8f1dc3Sbluhm = testingAccountingGetCountBytesDirect(parser);
269bd8f1dc3Sbluhm const unsigned long long actualCountBytesIndirect
270bd8f1dc3Sbluhm = testingAccountingGetCountBytesIndirect(parser);
271bd8f1dc3Sbluhm
272bd8f1dc3Sbluhm XML_ParserFree(parser);
273bd8f1dc3Sbluhm
274bd8f1dc3Sbluhm if (actualCountBytesDirect != expectedCountBytesDirect) {
275bd8f1dc3Sbluhm fprintf(
276bd8f1dc3Sbluhm stderr,
277bd8f1dc3Sbluhm "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ": Expected " EXPAT_FMT_ULL(
278bd8f1dc3Sbluhm "") " count direct bytes, got " EXPAT_FMT_ULL("") " instead.\n",
279bd8f1dc3Sbluhm u + 1, countCases, expectedCountBytesDirect, actualCountBytesDirect);
280bd8f1dc3Sbluhm fail("Count of direct bytes is off");
281bd8f1dc3Sbluhm }
282bd8f1dc3Sbluhm
283bd8f1dc3Sbluhm if (actualCountBytesIndirect != expectedCountBytesIndirect) {
284bd8f1dc3Sbluhm fprintf(
285bd8f1dc3Sbluhm stderr,
286bd8f1dc3Sbluhm "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ": Expected " EXPAT_FMT_ULL(
287bd8f1dc3Sbluhm "") " count indirect bytes, got " EXPAT_FMT_ULL("") " instead.\n",
288bd8f1dc3Sbluhm u + 1, countCases, expectedCountBytesIndirect,
289bd8f1dc3Sbluhm actualCountBytesIndirect);
290bd8f1dc3Sbluhm fail("Count of indirect bytes is off");
291bd8f1dc3Sbluhm }
292bd8f1dc3Sbluhm }
293bd8f1dc3Sbluhm }
294bd8f1dc3Sbluhm END_TEST
295bd8f1dc3Sbluhm
START_TEST(test_billion_laughs_attack_protection_api)296bd8f1dc3Sbluhm START_TEST(test_billion_laughs_attack_protection_api) {
297bd8f1dc3Sbluhm XML_Parser parserWithoutParent = XML_ParserCreate(NULL);
298bd8f1dc3Sbluhm XML_Parser parserWithParent = XML_ExternalEntityParserCreate(
299bd8f1dc3Sbluhm parserWithoutParent, XCS("entity123"), NULL);
300bd8f1dc3Sbluhm if (parserWithoutParent == NULL)
301bd8f1dc3Sbluhm fail("parserWithoutParent is NULL");
302bd8f1dc3Sbluhm if (parserWithParent == NULL)
303bd8f1dc3Sbluhm fail("parserWithParent is NULL");
304bd8f1dc3Sbluhm
305bd8f1dc3Sbluhm // XML_SetBillionLaughsAttackProtectionMaximumAmplification, error cases
306bd8f1dc3Sbluhm if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(NULL, 123.0f)
307bd8f1dc3Sbluhm == XML_TRUE)
308bd8f1dc3Sbluhm fail("Call with NULL parser is NOT supposed to succeed");
309bd8f1dc3Sbluhm if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(parserWithParent,
310bd8f1dc3Sbluhm 123.0f)
311bd8f1dc3Sbluhm == XML_TRUE)
312bd8f1dc3Sbluhm fail("Call with non-root parser is NOT supposed to succeed");
313bd8f1dc3Sbluhm if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
314bd8f1dc3Sbluhm parserWithoutParent, NAN)
315bd8f1dc3Sbluhm == XML_TRUE)
316bd8f1dc3Sbluhm fail("Call with NaN limit is NOT supposed to succeed");
317bd8f1dc3Sbluhm if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
318bd8f1dc3Sbluhm parserWithoutParent, -1.0f)
319bd8f1dc3Sbluhm == XML_TRUE)
320bd8f1dc3Sbluhm fail("Call with negative limit is NOT supposed to succeed");
321bd8f1dc3Sbluhm if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
322bd8f1dc3Sbluhm parserWithoutParent, 0.9f)
323bd8f1dc3Sbluhm == XML_TRUE)
324bd8f1dc3Sbluhm fail("Call with positive limit <1.0 is NOT supposed to succeed");
325bd8f1dc3Sbluhm
326bd8f1dc3Sbluhm // XML_SetBillionLaughsAttackProtectionMaximumAmplification, success cases
327bd8f1dc3Sbluhm if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
328bd8f1dc3Sbluhm parserWithoutParent, 1.0f)
329bd8f1dc3Sbluhm == XML_FALSE)
330bd8f1dc3Sbluhm fail("Call with positive limit >=1.0 is supposed to succeed");
331bd8f1dc3Sbluhm if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
332bd8f1dc3Sbluhm parserWithoutParent, 123456.789f)
333bd8f1dc3Sbluhm == XML_FALSE)
334bd8f1dc3Sbluhm fail("Call with positive limit >=1.0 is supposed to succeed");
335bd8f1dc3Sbluhm if (XML_SetBillionLaughsAttackProtectionMaximumAmplification(
336bd8f1dc3Sbluhm parserWithoutParent, INFINITY)
337bd8f1dc3Sbluhm == XML_FALSE)
338bd8f1dc3Sbluhm fail("Call with positive limit >=1.0 is supposed to succeed");
339bd8f1dc3Sbluhm
340bd8f1dc3Sbluhm // XML_SetBillionLaughsAttackProtectionActivationThreshold, error cases
341bd8f1dc3Sbluhm if (XML_SetBillionLaughsAttackProtectionActivationThreshold(NULL, 123)
342bd8f1dc3Sbluhm == XML_TRUE)
343bd8f1dc3Sbluhm fail("Call with NULL parser is NOT supposed to succeed");
344bd8f1dc3Sbluhm if (XML_SetBillionLaughsAttackProtectionActivationThreshold(parserWithParent,
345bd8f1dc3Sbluhm 123)
346bd8f1dc3Sbluhm == XML_TRUE)
347bd8f1dc3Sbluhm fail("Call with non-root parser is NOT supposed to succeed");
348bd8f1dc3Sbluhm
349bd8f1dc3Sbluhm // XML_SetBillionLaughsAttackProtectionActivationThreshold, success cases
350bd8f1dc3Sbluhm if (XML_SetBillionLaughsAttackProtectionActivationThreshold(
351bd8f1dc3Sbluhm parserWithoutParent, 123)
352bd8f1dc3Sbluhm == XML_FALSE)
353bd8f1dc3Sbluhm fail("Call with non-NULL parentless parser is supposed to succeed");
354bd8f1dc3Sbluhm
355bd8f1dc3Sbluhm XML_ParserFree(parserWithParent);
356bd8f1dc3Sbluhm XML_ParserFree(parserWithoutParent);
357bd8f1dc3Sbluhm }
358bd8f1dc3Sbluhm END_TEST
359bd8f1dc3Sbluhm
START_TEST(test_helper_unsigned_char_to_printable)360bd8f1dc3Sbluhm START_TEST(test_helper_unsigned_char_to_printable) {
361bd8f1dc3Sbluhm // Smoke test
362bd8f1dc3Sbluhm unsigned char uc = 0;
363bd8f1dc3Sbluhm for (; uc < (unsigned char)-1; uc++) {
364bd8f1dc3Sbluhm set_subtest("char %u", (unsigned)uc);
365bd8f1dc3Sbluhm const char *const printable = unsignedCharToPrintable(uc);
366bd8f1dc3Sbluhm if (printable == NULL)
367bd8f1dc3Sbluhm fail("unsignedCharToPrintable returned NULL");
368bd8f1dc3Sbluhm else if (strlen(printable) < (size_t)1)
369bd8f1dc3Sbluhm fail("unsignedCharToPrintable returned empty string");
370bd8f1dc3Sbluhm }
371bd8f1dc3Sbluhm
372bd8f1dc3Sbluhm // Two concrete samples
373bd8f1dc3Sbluhm set_subtest("char 'A'");
374bd8f1dc3Sbluhm if (strcmp(unsignedCharToPrintable('A'), "A") != 0)
375bd8f1dc3Sbluhm fail("unsignedCharToPrintable result mistaken");
376bd8f1dc3Sbluhm set_subtest("char '\\'");
377bd8f1dc3Sbluhm if (strcmp(unsignedCharToPrintable('\\'), "\\\\") != 0)
378bd8f1dc3Sbluhm fail("unsignedCharToPrintable result mistaken");
379bd8f1dc3Sbluhm }
380bd8f1dc3Sbluhm END_TEST
381*5c4051bcSbluhm
START_TEST(test_amplification_isolated_external_parser)382*5c4051bcSbluhm START_TEST(test_amplification_isolated_external_parser) {
383*5c4051bcSbluhm // NOTE: Length 44 is precisely twice the length of "<!ENTITY a SYSTEM 'b'>"
384*5c4051bcSbluhm // (22) that is used in function accountingGetCurrentAmplification in
385*5c4051bcSbluhm // xmlparse.c.
386*5c4051bcSbluhm // 1.........1.........1.........1.........1..4 => 44
387*5c4051bcSbluhm const char doc[] = "<!ENTITY % p1 '123456789_123456789_1234567'>";
388*5c4051bcSbluhm const int docLen = (int)sizeof(doc) - 1;
389*5c4051bcSbluhm const float maximumToleratedAmplification = 2.0f;
390*5c4051bcSbluhm
391*5c4051bcSbluhm struct TestCase {
392*5c4051bcSbluhm int offsetOfThreshold;
393*5c4051bcSbluhm enum XML_Status expectedStatus;
394*5c4051bcSbluhm };
395*5c4051bcSbluhm
396*5c4051bcSbluhm struct TestCase cases[] = {
397*5c4051bcSbluhm {-2, XML_STATUS_ERROR}, {-1, XML_STATUS_ERROR}, {0, XML_STATUS_ERROR},
398*5c4051bcSbluhm {+1, XML_STATUS_OK}, {+2, XML_STATUS_OK},
399*5c4051bcSbluhm };
400*5c4051bcSbluhm
401*5c4051bcSbluhm for (size_t i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {
402*5c4051bcSbluhm const int offsetOfThreshold = cases[i].offsetOfThreshold;
403*5c4051bcSbluhm const enum XML_Status expectedStatus = cases[i].expectedStatus;
404*5c4051bcSbluhm const unsigned long long activationThresholdBytes
405*5c4051bcSbluhm = docLen + offsetOfThreshold;
406*5c4051bcSbluhm
407*5c4051bcSbluhm set_subtest("offsetOfThreshold=%d, expectedStatus=%d", offsetOfThreshold,
408*5c4051bcSbluhm expectedStatus);
409*5c4051bcSbluhm
410*5c4051bcSbluhm XML_Parser parser = XML_ParserCreate(NULL);
411*5c4051bcSbluhm assert_true(parser != NULL);
412*5c4051bcSbluhm
413*5c4051bcSbluhm assert_true(XML_SetBillionLaughsAttackProtectionMaximumAmplification(
414*5c4051bcSbluhm parser, maximumToleratedAmplification)
415*5c4051bcSbluhm == XML_TRUE);
416*5c4051bcSbluhm assert_true(XML_SetBillionLaughsAttackProtectionActivationThreshold(
417*5c4051bcSbluhm parser, activationThresholdBytes)
418*5c4051bcSbluhm == XML_TRUE);
419*5c4051bcSbluhm
420*5c4051bcSbluhm XML_Parser ext_parser = XML_ExternalEntityParserCreate(parser, NULL, NULL);
421*5c4051bcSbluhm assert_true(ext_parser != NULL);
422*5c4051bcSbluhm
423*5c4051bcSbluhm const enum XML_Status actualStatus
424*5c4051bcSbluhm = _XML_Parse_SINGLE_BYTES(ext_parser, doc, docLen, XML_TRUE);
425*5c4051bcSbluhm
426*5c4051bcSbluhm assert_true(actualStatus == expectedStatus);
427*5c4051bcSbluhm if (actualStatus != XML_STATUS_OK) {
428*5c4051bcSbluhm assert_true(XML_GetErrorCode(ext_parser)
429*5c4051bcSbluhm == XML_ERROR_AMPLIFICATION_LIMIT_BREACH);
430*5c4051bcSbluhm }
431*5c4051bcSbluhm
432*5c4051bcSbluhm XML_ParserFree(ext_parser);
433*5c4051bcSbluhm XML_ParserFree(parser);
434*5c4051bcSbluhm }
435*5c4051bcSbluhm }
436*5c4051bcSbluhm END_TEST
437*5c4051bcSbluhm
438bd8f1dc3Sbluhm #endif // XML_GE == 1
439bd8f1dc3Sbluhm
440bd8f1dc3Sbluhm void
make_accounting_test_case(Suite * s)441bd8f1dc3Sbluhm make_accounting_test_case(Suite *s) {
442bd8f1dc3Sbluhm #if XML_GE == 1
443bd8f1dc3Sbluhm TCase *tc_accounting = tcase_create("accounting tests");
444bd8f1dc3Sbluhm
445bd8f1dc3Sbluhm suite_add_tcase(s, tc_accounting);
446bd8f1dc3Sbluhm
447bd8f1dc3Sbluhm tcase_add_test(tc_accounting, test_accounting_precision);
448bd8f1dc3Sbluhm tcase_add_test(tc_accounting, test_billion_laughs_attack_protection_api);
449bd8f1dc3Sbluhm tcase_add_test(tc_accounting, test_helper_unsigned_char_to_printable);
450*5c4051bcSbluhm tcase_add_test__ifdef_xml_dtd(tc_accounting,
451*5c4051bcSbluhm test_amplification_isolated_external_parser);
452bd8f1dc3Sbluhm #else
453bd8f1dc3Sbluhm UNUSED_P(s);
454bd8f1dc3Sbluhm #endif /* XML_GE == 1 */
455bd8f1dc3Sbluhm }
456