xref: /openbsd-src/lib/libexpat/tests/acc_tests.c (revision 5c4051bc8b1de6c1b835dba98547ab4be28fa3d9)
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=\"&amp;&apos;&gt;&lt;&quot;\" />", 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>&amp;&apos;&gt;&lt;&quot;</e>", NULL, NULL,
79bd8f1dc3Sbluhm        sizeof(XML_Char) * 5 /* number of predefined entities */},
80bd8f1dc3Sbluhm       {"<e>&#65;&#41;</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 \"&#60;!ENTITY nine &#34;123456789&#34;&#62;\">\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 \"&#37;comment;<!--22-->&#37;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 \"&#60;!ENTITY five2 &#34;[&#37;five;][&#37;five;]]]]&#34;&#62;\">\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