xref: /aosp_15_r20/external/cronet/third_party/libxml/src/testlimits.c (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 /*
2  * testlimits.c: C program to run libxml2 regression tests checking various
3  *       limits in document size. Will consume a lot of RAM and CPU cycles
4  *
5  * To compile on Unixes:
6  * cc -o testlimits `xml2-config --cflags` testlimits.c `xml2-config --libs` -lpthread
7  *
8  * See Copyright for the status of this software.
9  *
10  * [email protected]
11  */
12 
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/stat.h>
17 #include <time.h>
18 
19 #include <libxml/parser.h>
20 #include <libxml/parserInternals.h>
21 #include <libxml/tree.h>
22 #include <libxml/uri.h>
23 #ifdef LIBXML_READER_ENABLED
24 #include <libxml/xmlreader.h>
25 #endif
26 
27 static int verbose = 0;
28 static int tests_quiet = 0;
29 
30 /************************************************************************
31  *									*
32  *		time handling                                           *
33  *									*
34  ************************************************************************/
35 
36 /* maximum time for one parsing before declaring a timeout */
37 #define MAX_TIME 2 /* seconds */
38 
39 static clock_t t0;
40 int timeout = 0;
41 
reset_timout(void)42 static void reset_timout(void) {
43     timeout = 0;
44     t0 = clock();
45 }
46 
check_time(void)47 static int check_time(void) {
48     clock_t tnow = clock();
49     if (((tnow - t0) / CLOCKS_PER_SEC) > MAX_TIME) {
50         timeout = 1;
51         return(0);
52     }
53     return(1);
54 }
55 
56 /************************************************************************
57  *									*
58  *		Huge document generator					*
59  *									*
60  ************************************************************************/
61 
62 #include <libxml/xmlIO.h>
63 
64 /*
65  * Huge documents are built using fixed start and end chunks
66  * and filling between the two an unconventional amount of char data
67  */
68 typedef struct hugeTest hugeTest;
69 typedef hugeTest *hugeTestPtr;
70 struct hugeTest {
71     const char *description;
72     const char *name;
73     const char *start;
74     const char *end;
75 };
76 
77 static struct hugeTest hugeTests[] = {
78     { "Huge text node", "huge:textNode", "<foo>", "</foo>" },
79     { "Huge attribute node", "huge:attrNode", "<foo bar='", "'/>" },
80     { "Huge comment node", "huge:commentNode", "<foo><!--", "--></foo>" },
81     { "Huge PI node", "huge:piNode", "<foo><?bar ", "?></foo>" },
82 };
83 
84 static const char *current;
85 static int rlen;
86 static unsigned int currentTest = 0;
87 static int instate = 0;
88 
89 /**
90  * hugeMatch:
91  * @URI: an URI to test
92  *
93  * Check for an huge: query
94  *
95  * Returns 1 if yes and 0 if another Input module should be used
96  */
97 static int
hugeMatch(const char * URI)98 hugeMatch(const char * URI) {
99     if ((URI != NULL) && (!strncmp(URI, "huge:", 5)))
100         return(1);
101     return(0);
102 }
103 
104 /**
105  * hugeOpen:
106  * @URI: an URI to test
107  *
108  * Return a pointer to the huge: query handler, in this example simply
109  * the current pointer...
110  *
111  * Returns an Input context or NULL in case or error
112  */
113 static void *
hugeOpen(const char * URI)114 hugeOpen(const char * URI) {
115     if ((URI == NULL) || (strncmp(URI, "huge:", 5)))
116         return(NULL);
117 
118     for (currentTest = 0;currentTest < sizeof(hugeTests)/sizeof(hugeTests[0]);
119          currentTest++)
120          if (!strcmp(hugeTests[currentTest].name, URI))
121              goto found;
122 
123     return(NULL);
124 
125 found:
126     rlen = strlen(hugeTests[currentTest].start);
127     current = hugeTests[currentTest].start;
128     instate = 0;
129     return((void *) current);
130 }
131 
132 /**
133  * hugeClose:
134  * @context: the read context
135  *
136  * Close the huge: query handler
137  *
138  * Returns 0 or -1 in case of error
139  */
140 static int
hugeClose(void * context)141 hugeClose(void * context) {
142     if (context == NULL) return(-1);
143     fprintf(stderr, "\n");
144     return(0);
145 }
146 
147 #define CHUNK 4096
148 
149 char filling[CHUNK + 1];
150 
fillFilling(void)151 static void fillFilling(void) {
152     int i;
153 
154     for (i = 0;i < CHUNK;i++) {
155         filling[i] = 'a';
156     }
157     filling[CHUNK] = 0;
158 }
159 
160 size_t maxlen = 64 * 1024 * 1024;
161 size_t curlen = 0;
162 size_t dotlen;
163 
164 /**
165  * hugeRead:
166  * @context: the read context
167  * @buffer: where to store data
168  * @len: number of bytes to read
169  *
170  * Implement an huge: query read.
171  *
172  * Returns the number of bytes read or -1 in case of error
173  */
174 static int
hugeRead(void * context,char * buffer,int len)175 hugeRead(void *context, char *buffer, int len)
176 {
177     if ((context == NULL) || (buffer == NULL) || (len < 0))
178         return (-1);
179 
180     if (instate == 0) {
181         if (len >= rlen) {
182             len = rlen;
183             rlen = 0;
184             memcpy(buffer, current, len);
185             instate = 1;
186             curlen = 0;
187             dotlen = maxlen / 10;
188         } else {
189             memcpy(buffer, current, len);
190             rlen -= len;
191             current += len;
192         }
193     } else if (instate == 2) {
194         if (len >= rlen) {
195             len = rlen;
196             rlen = 0;
197             memcpy(buffer, current, len);
198             instate = 3;
199             curlen = 0;
200         } else {
201             memcpy(buffer, current, len);
202             rlen -= len;
203             current += len;
204         }
205     } else if (instate == 1) {
206         if (len > CHUNK) len = CHUNK;
207         memcpy(buffer, &filling[0], len);
208         curlen += len;
209         if (curlen >= maxlen) {
210             rlen = strlen(hugeTests[currentTest].end);
211             current = hugeTests[currentTest].end;
212             instate = 2;
213 	} else {
214             if (curlen > dotlen) {
215                 fprintf(stderr, ".");
216                 dotlen += maxlen / 10;
217             }
218         }
219     } else
220       len = 0;
221     return (len);
222 }
223 
224 /************************************************************************
225  *									*
226  *		Crazy document generator				*
227  *									*
228  ************************************************************************/
229 
230 unsigned int crazy_indx = 0;
231 
232 const char *crazy = "<?xml version='1.0' encoding='UTF-8'?>\
233 <?tst ?>\
234 <!-- tst -->\
235 <!DOCTYPE foo [\
236 <?tst ?>\
237 <!-- tst -->\
238 <!ELEMENT foo (#PCDATA)>\
239 <!ELEMENT p (#PCDATA|emph)* >\
240 ]>\
241 <?tst ?>\
242 <!-- tst -->\
243 <foo bar='foo'>\
244 <?tst ?>\
245 <!-- tst -->\
246 foo\
247 <![CDATA[ ]]>\
248 </foo>\
249 <?tst ?>\
250 <!-- tst -->";
251 
252 /**
253  * crazyMatch:
254  * @URI: an URI to test
255  *
256  * Check for a crazy: query
257  *
258  * Returns 1 if yes and 0 if another Input module should be used
259  */
260 static int
crazyMatch(const char * URI)261 crazyMatch(const char * URI) {
262     if ((URI != NULL) && (!strncmp(URI, "crazy:", 6)))
263         return(1);
264     return(0);
265 }
266 
267 /**
268  * crazyOpen:
269  * @URI: an URI to test
270  *
271  * Return a pointer to the crazy: query handler, in this example simply
272  * the current pointer...
273  *
274  * Returns an Input context or NULL in case or error
275  */
276 static void *
crazyOpen(const char * URI)277 crazyOpen(const char * URI) {
278     if ((URI == NULL) || (strncmp(URI, "crazy:", 6)))
279         return(NULL);
280 
281     if (crazy_indx > strlen(crazy))
282         return(NULL);
283     reset_timout();
284     rlen = crazy_indx;
285     current = &crazy[0];
286     instate = 0;
287     return((void *) current);
288 }
289 
290 /**
291  * crazyClose:
292  * @context: the read context
293  *
294  * Close the crazy: query handler
295  *
296  * Returns 0 or -1 in case of error
297  */
298 static int
crazyClose(void * context)299 crazyClose(void * context) {
300     if (context == NULL) return(-1);
301     return(0);
302 }
303 
304 
305 /**
306  * crazyRead:
307  * @context: the read context
308  * @buffer: where to store data
309  * @len: number of bytes to read
310  *
311  * Implement an crazy: query read.
312  *
313  * Returns the number of bytes read or -1 in case of error
314  */
315 static int
crazyRead(void * context,char * buffer,int len)316 crazyRead(void *context, char *buffer, int len)
317 {
318     if ((context == NULL) || (buffer == NULL) || (len < 0))
319         return (-1);
320 
321     if ((check_time() <= 0) && (instate == 1)) {
322         fprintf(stderr, "\ntimeout in crazy(%d)\n", crazy_indx);
323         rlen = strlen(crazy) - crazy_indx;
324         current = &crazy[crazy_indx];
325         instate = 2;
326     }
327     if (instate == 0) {
328         if (len >= rlen) {
329             len = rlen;
330             rlen = 0;
331             memcpy(buffer, current, len);
332             instate = 1;
333             curlen = 0;
334         } else {
335             memcpy(buffer, current, len);
336             rlen -= len;
337             current += len;
338         }
339     } else if (instate == 2) {
340         if (len >= rlen) {
341             len = rlen;
342             rlen = 0;
343             memcpy(buffer, current, len);
344             instate = 3;
345             curlen = 0;
346         } else {
347             memcpy(buffer, current, len);
348             rlen -= len;
349             current += len;
350         }
351     } else if (instate == 1) {
352         if (len > CHUNK) len = CHUNK;
353         memcpy(buffer, &filling[0], len);
354         curlen += len;
355         if (curlen >= maxlen) {
356             rlen = strlen(crazy) - crazy_indx;
357             current = &crazy[crazy_indx];
358             instate = 2;
359         }
360     } else
361       len = 0;
362     return (len);
363 }
364 /************************************************************************
365  *									*
366  *		Libxml2 specific routines				*
367  *									*
368  ************************************************************************/
369 
370 static int nb_tests = 0;
371 static int nb_errors = 0;
372 static int nb_leaks = 0;
373 static int extraMemoryFromResolver = 0;
374 
375 /*
376  * We need to trap calls to the resolver to not account memory for the catalog
377  * which is shared to the current running test. We also don't want to have
378  * network downloads modifying tests.
379  */
380 static xmlParserInputPtr
testExternalEntityLoader(const char * URL,const char * ID,xmlParserCtxtPtr ctxt)381 testExternalEntityLoader(const char *URL, const char *ID,
382 			 xmlParserCtxtPtr ctxt) {
383     xmlParserInputPtr ret;
384     int memused = xmlMemUsed();
385 
386     ret = xmlNoNetExternalEntityLoader(URL, ID, ctxt);
387     extraMemoryFromResolver += xmlMemUsed() - memused;
388 
389     return(ret);
390 }
391 
392 static void
initializeLibxml2(void)393 initializeLibxml2(void) {
394     xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
395     xmlInitParser();
396     xmlSetExternalEntityLoader(testExternalEntityLoader);
397     /*
398      * register the new I/O handlers
399      */
400     if (xmlRegisterInputCallbacks(hugeMatch, hugeOpen,
401                                   hugeRead, hugeClose) < 0) {
402         fprintf(stderr, "failed to register Huge handlers\n");
403 	exit(1);
404     }
405     if (xmlRegisterInputCallbacks(crazyMatch, crazyOpen,
406                                   crazyRead, crazyClose) < 0) {
407         fprintf(stderr, "failed to register Crazy handlers\n");
408 	exit(1);
409     }
410 }
411 
412 /************************************************************************
413  *									*
414  *		SAX empty callbacks                                     *
415  *									*
416  ************************************************************************/
417 
418 unsigned long callbacks = 0;
419 
420 /**
421  * isStandaloneCallback:
422  * @ctxt:  An XML parser context
423  *
424  * Is this document tagged standalone ?
425  *
426  * Returns 1 if true
427  */
428 static int
isStandaloneCallback(void * ctx ATTRIBUTE_UNUSED)429 isStandaloneCallback(void *ctx ATTRIBUTE_UNUSED)
430 {
431     callbacks++;
432     return (0);
433 }
434 
435 /**
436  * hasInternalSubsetCallback:
437  * @ctxt:  An XML parser context
438  *
439  * Does this document has an internal subset
440  *
441  * Returns 1 if true
442  */
443 static int
hasInternalSubsetCallback(void * ctx ATTRIBUTE_UNUSED)444 hasInternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
445 {
446     callbacks++;
447     return (0);
448 }
449 
450 /**
451  * hasExternalSubsetCallback:
452  * @ctxt:  An XML parser context
453  *
454  * Does this document has an external subset
455  *
456  * Returns 1 if true
457  */
458 static int
hasExternalSubsetCallback(void * ctx ATTRIBUTE_UNUSED)459 hasExternalSubsetCallback(void *ctx ATTRIBUTE_UNUSED)
460 {
461     callbacks++;
462     return (0);
463 }
464 
465 /**
466  * internalSubsetCallback:
467  * @ctxt:  An XML parser context
468  *
469  * Does this document has an internal subset
470  */
471 static void
internalSubsetCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * name ATTRIBUTE_UNUSED,const xmlChar * ExternalID ATTRIBUTE_UNUSED,const xmlChar * SystemID ATTRIBUTE_UNUSED)472 internalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
473                        const xmlChar * name ATTRIBUTE_UNUSED,
474                        const xmlChar * ExternalID ATTRIBUTE_UNUSED,
475                        const xmlChar * SystemID ATTRIBUTE_UNUSED)
476 {
477     callbacks++;
478     return;
479 }
480 
481 /**
482  * externalSubsetCallback:
483  * @ctxt:  An XML parser context
484  *
485  * Does this document has an external subset
486  */
487 static void
externalSubsetCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * name ATTRIBUTE_UNUSED,const xmlChar * ExternalID ATTRIBUTE_UNUSED,const xmlChar * SystemID ATTRIBUTE_UNUSED)488 externalSubsetCallback(void *ctx ATTRIBUTE_UNUSED,
489                        const xmlChar * name ATTRIBUTE_UNUSED,
490                        const xmlChar * ExternalID ATTRIBUTE_UNUSED,
491                        const xmlChar * SystemID ATTRIBUTE_UNUSED)
492 {
493     callbacks++;
494     return;
495 }
496 
497 /**
498  * resolveEntityCallback:
499  * @ctxt:  An XML parser context
500  * @publicId: The public ID of the entity
501  * @systemId: The system ID of the entity
502  *
503  * Special entity resolver, better left to the parser, it has
504  * more context than the application layer.
505  * The default behaviour is to NOT resolve the entities, in that case
506  * the ENTITY_REF nodes are built in the structure (and the parameter
507  * values).
508  *
509  * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
510  */
511 static xmlParserInputPtr
resolveEntityCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * publicId ATTRIBUTE_UNUSED,const xmlChar * systemId ATTRIBUTE_UNUSED)512 resolveEntityCallback(void *ctx ATTRIBUTE_UNUSED,
513                       const xmlChar * publicId ATTRIBUTE_UNUSED,
514                       const xmlChar * systemId ATTRIBUTE_UNUSED)
515 {
516     callbacks++;
517     return (NULL);
518 }
519 
520 /**
521  * getEntityCallback:
522  * @ctxt:  An XML parser context
523  * @name: The entity name
524  *
525  * Get an entity by name
526  *
527  * Returns the xmlParserInputPtr if inlined or NULL for DOM behaviour.
528  */
529 static xmlEntityPtr
getEntityCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * name ATTRIBUTE_UNUSED)530 getEntityCallback(void *ctx ATTRIBUTE_UNUSED,
531                   const xmlChar * name ATTRIBUTE_UNUSED)
532 {
533     callbacks++;
534     return (NULL);
535 }
536 
537 /**
538  * getParameterEntityCallback:
539  * @ctxt:  An XML parser context
540  * @name: The entity name
541  *
542  * Get a parameter entity by name
543  *
544  * Returns the xmlParserInputPtr
545  */
546 static xmlEntityPtr
getParameterEntityCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * name ATTRIBUTE_UNUSED)547 getParameterEntityCallback(void *ctx ATTRIBUTE_UNUSED,
548                            const xmlChar * name ATTRIBUTE_UNUSED)
549 {
550     callbacks++;
551     return (NULL);
552 }
553 
554 
555 /**
556  * entityDeclCallback:
557  * @ctxt:  An XML parser context
558  * @name:  the entity name
559  * @type:  the entity type
560  * @publicId: The public ID of the entity
561  * @systemId: The system ID of the entity
562  * @content: the entity value (without processing).
563  *
564  * An entity definition has been parsed
565  */
566 static void
entityDeclCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * name ATTRIBUTE_UNUSED,int type ATTRIBUTE_UNUSED,const xmlChar * publicId ATTRIBUTE_UNUSED,const xmlChar * systemId ATTRIBUTE_UNUSED,xmlChar * content ATTRIBUTE_UNUSED)567 entityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
568                    const xmlChar * name ATTRIBUTE_UNUSED,
569                    int type ATTRIBUTE_UNUSED,
570                    const xmlChar * publicId ATTRIBUTE_UNUSED,
571                    const xmlChar * systemId ATTRIBUTE_UNUSED,
572                    xmlChar * content ATTRIBUTE_UNUSED)
573 {
574     callbacks++;
575     return;
576 }
577 
578 /**
579  * attributeDeclCallback:
580  * @ctxt:  An XML parser context
581  * @name:  the attribute name
582  * @type:  the attribute type
583  *
584  * An attribute definition has been parsed
585  */
586 static void
attributeDeclCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * elem ATTRIBUTE_UNUSED,const xmlChar * name ATTRIBUTE_UNUSED,int type ATTRIBUTE_UNUSED,int def ATTRIBUTE_UNUSED,const xmlChar * defaultValue ATTRIBUTE_UNUSED,xmlEnumerationPtr tree ATTRIBUTE_UNUSED)587 attributeDeclCallback(void *ctx ATTRIBUTE_UNUSED,
588                       const xmlChar * elem ATTRIBUTE_UNUSED,
589                       const xmlChar * name ATTRIBUTE_UNUSED,
590                       int type ATTRIBUTE_UNUSED, int def ATTRIBUTE_UNUSED,
591                       const xmlChar * defaultValue ATTRIBUTE_UNUSED,
592                       xmlEnumerationPtr tree ATTRIBUTE_UNUSED)
593 {
594     callbacks++;
595     return;
596 }
597 
598 /**
599  * elementDeclCallback:
600  * @ctxt:  An XML parser context
601  * @name:  the element name
602  * @type:  the element type
603  * @content: the element value (without processing).
604  *
605  * An element definition has been parsed
606  */
607 static void
elementDeclCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * name ATTRIBUTE_UNUSED,int type ATTRIBUTE_UNUSED,xmlElementContentPtr content ATTRIBUTE_UNUSED)608 elementDeclCallback(void *ctx ATTRIBUTE_UNUSED,
609                     const xmlChar * name ATTRIBUTE_UNUSED,
610                     int type ATTRIBUTE_UNUSED,
611                     xmlElementContentPtr content ATTRIBUTE_UNUSED)
612 {
613     callbacks++;
614     return;
615 }
616 
617 /**
618  * notationDeclCallback:
619  * @ctxt:  An XML parser context
620  * @name: The name of the notation
621  * @publicId: The public ID of the entity
622  * @systemId: The system ID of the entity
623  *
624  * What to do when a notation declaration has been parsed.
625  */
626 static void
notationDeclCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * name ATTRIBUTE_UNUSED,const xmlChar * publicId ATTRIBUTE_UNUSED,const xmlChar * systemId ATTRIBUTE_UNUSED)627 notationDeclCallback(void *ctx ATTRIBUTE_UNUSED,
628                      const xmlChar * name ATTRIBUTE_UNUSED,
629                      const xmlChar * publicId ATTRIBUTE_UNUSED,
630                      const xmlChar * systemId ATTRIBUTE_UNUSED)
631 {
632     callbacks++;
633     return;
634 }
635 
636 /**
637  * unparsedEntityDeclCallback:
638  * @ctxt:  An XML parser context
639  * @name: The name of the entity
640  * @publicId: The public ID of the entity
641  * @systemId: The system ID of the entity
642  * @notationName: the name of the notation
643  *
644  * What to do when an unparsed entity declaration is parsed
645  */
646 static void
unparsedEntityDeclCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * name ATTRIBUTE_UNUSED,const xmlChar * publicId ATTRIBUTE_UNUSED,const xmlChar * systemId ATTRIBUTE_UNUSED,const xmlChar * notationName ATTRIBUTE_UNUSED)647 unparsedEntityDeclCallback(void *ctx ATTRIBUTE_UNUSED,
648                            const xmlChar * name ATTRIBUTE_UNUSED,
649                            const xmlChar * publicId ATTRIBUTE_UNUSED,
650                            const xmlChar * systemId ATTRIBUTE_UNUSED,
651                            const xmlChar * notationName ATTRIBUTE_UNUSED)
652 {
653     callbacks++;
654     return;
655 }
656 
657 /**
658  * setDocumentLocatorCallback:
659  * @ctxt:  An XML parser context
660  * @loc: A SAX Locator
661  *
662  * Receive the document locator at startup, actually xmlDefaultSAXLocator
663  * Everything is available on the context, so this is useless in our case.
664  */
665 static void
setDocumentLocatorCallback(void * ctx ATTRIBUTE_UNUSED,xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)666 setDocumentLocatorCallback(void *ctx ATTRIBUTE_UNUSED,
667                            xmlSAXLocatorPtr loc ATTRIBUTE_UNUSED)
668 {
669     callbacks++;
670     return;
671 }
672 
673 /**
674  * startDocumentCallback:
675  * @ctxt:  An XML parser context
676  *
677  * called when the document start being processed.
678  */
679 static void
startDocumentCallback(void * ctx ATTRIBUTE_UNUSED)680 startDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
681 {
682     callbacks++;
683     return;
684 }
685 
686 /**
687  * endDocumentCallback:
688  * @ctxt:  An XML parser context
689  *
690  * called when the document end has been detected.
691  */
692 static void
endDocumentCallback(void * ctx ATTRIBUTE_UNUSED)693 endDocumentCallback(void *ctx ATTRIBUTE_UNUSED)
694 {
695     callbacks++;
696     return;
697 }
698 
699 #if 0
700 /**
701  * startElementCallback:
702  * @ctxt:  An XML parser context
703  * @name:  The element name
704  *
705  * called when an opening tag has been processed.
706  */
707 static void
708 startElementCallback(void *ctx ATTRIBUTE_UNUSED,
709                      const xmlChar * name ATTRIBUTE_UNUSED,
710                      const xmlChar ** atts ATTRIBUTE_UNUSED)
711 {
712     callbacks++;
713     return;
714 }
715 
716 /**
717  * endElementCallback:
718  * @ctxt:  An XML parser context
719  * @name:  The element name
720  *
721  * called when the end of an element has been detected.
722  */
723 static void
724 endElementCallback(void *ctx ATTRIBUTE_UNUSED,
725                    const xmlChar * name ATTRIBUTE_UNUSED)
726 {
727     callbacks++;
728     return;
729 }
730 #endif
731 
732 /**
733  * charactersCallback:
734  * @ctxt:  An XML parser context
735  * @ch:  a xmlChar string
736  * @len: the number of xmlChar
737  *
738  * receiving some chars from the parser.
739  * Question: how much at a time ???
740  */
741 static void
charactersCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * ch ATTRIBUTE_UNUSED,int len ATTRIBUTE_UNUSED)742 charactersCallback(void *ctx ATTRIBUTE_UNUSED,
743                    const xmlChar * ch ATTRIBUTE_UNUSED,
744                    int len ATTRIBUTE_UNUSED)
745 {
746     callbacks++;
747     return;
748 }
749 
750 /**
751  * referenceCallback:
752  * @ctxt:  An XML parser context
753  * @name:  The entity name
754  *
755  * called when an entity reference is detected.
756  */
757 static void
referenceCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * name ATTRIBUTE_UNUSED)758 referenceCallback(void *ctx ATTRIBUTE_UNUSED,
759                   const xmlChar * name ATTRIBUTE_UNUSED)
760 {
761     callbacks++;
762     return;
763 }
764 
765 /**
766  * ignorableWhitespaceCallback:
767  * @ctxt:  An XML parser context
768  * @ch:  a xmlChar string
769  * @start: the first char in the string
770  * @len: the number of xmlChar
771  *
772  * receiving some ignorable whitespaces from the parser.
773  * Question: how much at a time ???
774  */
775 static void
ignorableWhitespaceCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * ch ATTRIBUTE_UNUSED,int len ATTRIBUTE_UNUSED)776 ignorableWhitespaceCallback(void *ctx ATTRIBUTE_UNUSED,
777                             const xmlChar * ch ATTRIBUTE_UNUSED,
778                             int len ATTRIBUTE_UNUSED)
779 {
780     callbacks++;
781     return;
782 }
783 
784 /**
785  * processingInstructionCallback:
786  * @ctxt:  An XML parser context
787  * @target:  the target name
788  * @data: the PI data's
789  * @len: the number of xmlChar
790  *
791  * A processing instruction has been parsed.
792  */
793 static void
processingInstructionCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * target ATTRIBUTE_UNUSED,const xmlChar * data ATTRIBUTE_UNUSED)794 processingInstructionCallback(void *ctx ATTRIBUTE_UNUSED,
795                               const xmlChar * target ATTRIBUTE_UNUSED,
796                               const xmlChar * data ATTRIBUTE_UNUSED)
797 {
798     callbacks++;
799     return;
800 }
801 
802 /**
803  * cdataBlockCallback:
804  * @ctx: the user data (XML parser context)
805  * @value:  The pcdata content
806  * @len:  the block length
807  *
808  * called when a pcdata block has been parsed
809  */
810 static void
cdataBlockCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * value ATTRIBUTE_UNUSED,int len ATTRIBUTE_UNUSED)811 cdataBlockCallback(void *ctx ATTRIBUTE_UNUSED,
812                    const xmlChar * value ATTRIBUTE_UNUSED,
813                    int len ATTRIBUTE_UNUSED)
814 {
815     callbacks++;
816     return;
817 }
818 
819 /**
820  * commentCallback:
821  * @ctxt:  An XML parser context
822  * @value:  the comment content
823  *
824  * A comment has been parsed.
825  */
826 static void
commentCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * value ATTRIBUTE_UNUSED)827 commentCallback(void *ctx ATTRIBUTE_UNUSED,
828                 const xmlChar * value ATTRIBUTE_UNUSED)
829 {
830     callbacks++;
831     return;
832 }
833 
834 /**
835  * warningCallback:
836  * @ctxt:  An XML parser context
837  * @msg:  the message to display/transmit
838  * @...:  extra parameters for the message display
839  *
840  * Display and format a warning messages, gives file, line, position and
841  * extra parameters.
842  */
843 static void
warningCallback(void * ctx ATTRIBUTE_UNUSED,const char * msg ATTRIBUTE_UNUSED,...)844 warningCallback(void *ctx ATTRIBUTE_UNUSED,
845                 const char *msg ATTRIBUTE_UNUSED, ...)
846 {
847     callbacks++;
848     return;
849 }
850 
851 /**
852  * errorCallback:
853  * @ctxt:  An XML parser context
854  * @msg:  the message to display/transmit
855  * @...:  extra parameters for the message display
856  *
857  * Display and format a error messages, gives file, line, position and
858  * extra parameters.
859  */
860 static void
errorCallback(void * ctx ATTRIBUTE_UNUSED,const char * msg ATTRIBUTE_UNUSED,...)861 errorCallback(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
862               ...)
863 {
864     callbacks++;
865     return;
866 }
867 
868 /**
869  * fatalErrorCallback:
870  * @ctxt:  An XML parser context
871  * @msg:  the message to display/transmit
872  * @...:  extra parameters for the message display
873  *
874  * Display and format a fatalError messages, gives file, line, position and
875  * extra parameters.
876  */
877 static void
fatalErrorCallback(void * ctx ATTRIBUTE_UNUSED,const char * msg ATTRIBUTE_UNUSED,...)878 fatalErrorCallback(void *ctx ATTRIBUTE_UNUSED,
879                    const char *msg ATTRIBUTE_UNUSED, ...)
880 {
881     return;
882 }
883 
884 
885 /*
886  * SAX2 specific callbacks
887  */
888 
889 /**
890  * startElementNsCallback:
891  * @ctxt:  An XML parser context
892  * @name:  The element name
893  *
894  * called when an opening tag has been processed.
895  */
896 static void
startElementNsCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * localname ATTRIBUTE_UNUSED,const xmlChar * prefix ATTRIBUTE_UNUSED,const xmlChar * URI ATTRIBUTE_UNUSED,int nb_namespaces ATTRIBUTE_UNUSED,const xmlChar ** namespaces ATTRIBUTE_UNUSED,int nb_attributes ATTRIBUTE_UNUSED,int nb_defaulted ATTRIBUTE_UNUSED,const xmlChar ** attributes ATTRIBUTE_UNUSED)897 startElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
898                        const xmlChar * localname ATTRIBUTE_UNUSED,
899                        const xmlChar * prefix ATTRIBUTE_UNUSED,
900                        const xmlChar * URI ATTRIBUTE_UNUSED,
901                        int nb_namespaces ATTRIBUTE_UNUSED,
902                        const xmlChar ** namespaces ATTRIBUTE_UNUSED,
903                        int nb_attributes ATTRIBUTE_UNUSED,
904                        int nb_defaulted ATTRIBUTE_UNUSED,
905                        const xmlChar ** attributes ATTRIBUTE_UNUSED)
906 {
907     callbacks++;
908     return;
909 }
910 
911 /**
912  * endElementCallback:
913  * @ctxt:  An XML parser context
914  * @name:  The element name
915  *
916  * called when the end of an element has been detected.
917  */
918 static void
endElementNsCallback(void * ctx ATTRIBUTE_UNUSED,const xmlChar * localname ATTRIBUTE_UNUSED,const xmlChar * prefix ATTRIBUTE_UNUSED,const xmlChar * URI ATTRIBUTE_UNUSED)919 endElementNsCallback(void *ctx ATTRIBUTE_UNUSED,
920                      const xmlChar * localname ATTRIBUTE_UNUSED,
921                      const xmlChar * prefix ATTRIBUTE_UNUSED,
922                      const xmlChar * URI ATTRIBUTE_UNUSED)
923 {
924     callbacks++;
925     return;
926 }
927 
928 static xmlSAXHandler callbackSAX2HandlerStruct = {
929     internalSubsetCallback,
930     isStandaloneCallback,
931     hasInternalSubsetCallback,
932     hasExternalSubsetCallback,
933     resolveEntityCallback,
934     getEntityCallback,
935     entityDeclCallback,
936     notationDeclCallback,
937     attributeDeclCallback,
938     elementDeclCallback,
939     unparsedEntityDeclCallback,
940     setDocumentLocatorCallback,
941     startDocumentCallback,
942     endDocumentCallback,
943     NULL,
944     NULL,
945     referenceCallback,
946     charactersCallback,
947     ignorableWhitespaceCallback,
948     processingInstructionCallback,
949     commentCallback,
950     warningCallback,
951     errorCallback,
952     fatalErrorCallback,
953     getParameterEntityCallback,
954     cdataBlockCallback,
955     externalSubsetCallback,
956     XML_SAX2_MAGIC,
957     NULL,
958     startElementNsCallback,
959     endElementNsCallback,
960     NULL
961 };
962 
963 static xmlSAXHandlerPtr callbackSAX2Handler = &callbackSAX2HandlerStruct;
964 
965 /************************************************************************
966  *									*
967  *		The tests front-ends                                     *
968  *									*
969  ************************************************************************/
970 
971 /**
972  * readerTest:
973  * @filename: the file to parse
974  * @max_size: size of the limit to test
975  * @options: parsing options
976  * @fail: should a failure be reported
977  *
978  * Parse a memory generated file using SAX
979  *
980  * Returns 0 in case of success, an error code otherwise
981  */
982 static int
saxTest(const char * filename,size_t limit,int options,int fail)983 saxTest(const char *filename, size_t limit, int options, int fail) {
984     int res = 0;
985     xmlParserCtxtPtr ctxt;
986     xmlDocPtr doc;
987 
988     nb_tests++;
989 
990     maxlen = limit;
991     ctxt = xmlNewSAXParserCtxt(callbackSAX2Handler, NULL);
992     if (ctxt == NULL) {
993         fprintf(stderr, "Failed to create parser context\n");
994 	return(1);
995     }
996     doc = xmlCtxtReadFile(ctxt, filename, NULL, options | XML_PARSE_NOERROR);
997 
998     if (doc != NULL) {
999         fprintf(stderr, "SAX parsing generated a document !\n");
1000         xmlFreeDoc(doc);
1001         res = 0;
1002     } else if (ctxt->wellFormed == 0) {
1003         if (fail)
1004             res = 0;
1005         else {
1006             fprintf(stderr, "Failed to parse '%s' %lu\n", filename,
1007                     (unsigned long) limit);
1008             res = 1;
1009         }
1010     } else {
1011         if (fail) {
1012             fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1013                     filename, (unsigned long) limit);
1014             res = 1;
1015         } else
1016             res = 0;
1017     }
1018     xmlFreeParserCtxt(ctxt);
1019 
1020     return(res);
1021 }
1022 #ifdef LIBXML_READER_ENABLED
1023 /**
1024  * readerTest:
1025  * @filename: the file to parse
1026  * @max_size: size of the limit to test
1027  * @options: parsing options
1028  * @fail: should a failure be reported
1029  *
1030  * Parse a memory generated file using the xmlReader
1031  *
1032  * Returns 0 in case of success, an error code otherwise
1033  */
1034 static int
readerTest(const char * filename,size_t limit,int options,int fail)1035 readerTest(const char *filename, size_t limit, int options, int fail) {
1036     xmlTextReaderPtr reader;
1037     int res = 0;
1038     int ret;
1039 
1040     nb_tests++;
1041 
1042     maxlen = limit;
1043     reader = xmlReaderForFile(filename , NULL, options | XML_PARSE_NOERROR);
1044     if (reader == NULL) {
1045         fprintf(stderr, "Failed to open '%s' test\n", filename);
1046 	return(1);
1047     }
1048     ret = xmlTextReaderRead(reader);
1049     while (ret == 1) {
1050         ret = xmlTextReaderRead(reader);
1051     }
1052     if (ret != 0) {
1053         if (fail)
1054             res = 0;
1055         else {
1056             if (strncmp(filename, "crazy:", 6) == 0)
1057                 fprintf(stderr, "Failed to parse '%s' %u\n",
1058                         filename, crazy_indx);
1059             else
1060                 fprintf(stderr, "Failed to parse '%s' %lu\n",
1061                         filename, (unsigned long) limit);
1062             res = 1;
1063         }
1064     } else {
1065         if (fail) {
1066             if (strncmp(filename, "crazy:", 6) == 0)
1067                 fprintf(stderr, "Failed to get failure for '%s' %u\n",
1068                         filename, crazy_indx);
1069             else
1070                 fprintf(stderr, "Failed to get failure for '%s' %lu\n",
1071                         filename, (unsigned long) limit);
1072             res = 1;
1073         } else
1074             res = 0;
1075     }
1076     if (timeout)
1077         res = 1;
1078     xmlFreeTextReader(reader);
1079 
1080     return(res);
1081 }
1082 #endif
1083 
1084 /************************************************************************
1085  *									*
1086  *			Tests descriptions				*
1087  *									*
1088  ************************************************************************/
1089 
1090 typedef int (*functest) (const char *filename, size_t limit, int options,
1091                          int fail);
1092 
1093 typedef struct limitDesc limitDesc;
1094 typedef limitDesc *limitDescPtr;
1095 struct limitDesc {
1096     const char *name; /* the huge generator name */
1097     size_t limit;     /* the limit to test */
1098     int options;      /* extra parser options */
1099     int fail;         /* whether the test should fail */
1100 };
1101 
1102 static limitDesc limitDescriptions[] = {
1103     /* max length of a text node in content */
1104     {"huge:textNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1105     {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1106     {"huge:textNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1107     /* max length of a text node in content */
1108     {"huge:attrNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1109     {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1110     {"huge:attrNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1111     /* max length of a comment node */
1112     {"huge:commentNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1113     {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1114     {"huge:commentNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1115     /* max length of a PI node */
1116     {"huge:piNode", XML_MAX_TEXT_LENGTH - CHUNK, 0, 0},
1117     {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, 0, 1},
1118     {"huge:piNode", XML_MAX_TEXT_LENGTH + CHUNK, XML_PARSE_HUGE, 0},
1119 };
1120 
1121 typedef struct testDesc testDesc;
1122 typedef testDesc *testDescPtr;
1123 struct testDesc {
1124     const char *desc; /* description of the test */
1125     functest    func; /* function implementing the test */
1126 };
1127 
1128 static
1129 testDesc testDescriptions[] = {
1130     { "Parsing of huge files with the sax parser", saxTest},
1131 /*    { "Parsing of huge files with the tree parser", treeTest}, */
1132 #ifdef LIBXML_READER_ENABLED
1133     { "Parsing of huge files with the reader", readerTest},
1134 #endif
1135     {NULL, NULL}
1136 };
1137 
1138 typedef struct testException testException;
1139 typedef testException *testExceptionPtr;
1140 struct testException {
1141     unsigned int test;  /* the parser test number */
1142     unsigned int limit; /* the limit test number */
1143     int fail;           /* new fail value or -1*/
1144     size_t size;        /* new limit value or 0 */
1145 };
1146 
1147 static
1148 testException testExceptions[] = {
1149     /* the SAX parser doesn't hit a limit of XML_MAX_TEXT_LENGTH text nodes */
1150     { 0, 1, 0, 0},
1151 };
1152 
1153 static int
launchTests(testDescPtr tst,unsigned int test)1154 launchTests(testDescPtr tst, unsigned int test) {
1155     int res = 0, err = 0;
1156     unsigned int i, j;
1157     size_t limit;
1158     int fail;
1159 
1160     if (tst == NULL) return(-1);
1161 
1162     for (i = 0;i < sizeof(limitDescriptions)/sizeof(limitDescriptions[0]);i++) {
1163         limit = limitDescriptions[i].limit;
1164         fail = limitDescriptions[i].fail;
1165         /*
1166          * Handle exceptions if any
1167          */
1168         for (j = 0;j < sizeof(testExceptions)/sizeof(testExceptions[0]);j++) {
1169             if ((testExceptions[j].test == test) &&
1170                 (testExceptions[j].limit == i)) {
1171                 if (testExceptions[j].fail != -1)
1172                     fail = testExceptions[j].fail;
1173                 if (testExceptions[j].size != 0)
1174                     limit = testExceptions[j].size;
1175                 break;
1176             }
1177         }
1178         res = tst->func(limitDescriptions[i].name, limit,
1179                         limitDescriptions[i].options, fail);
1180         if (res != 0) {
1181             nb_errors++;
1182             err++;
1183         }
1184     }
1185     return(err);
1186 }
1187 
1188 
1189 static int
runtest(unsigned int i)1190 runtest(unsigned int i) {
1191     int ret = 0, res;
1192     int old_errors, old_tests, old_leaks;
1193 
1194     old_errors = nb_errors;
1195     old_tests = nb_tests;
1196     old_leaks = nb_leaks;
1197     if ((tests_quiet == 0) && (testDescriptions[i].desc != NULL))
1198 	printf("## %s\n", testDescriptions[i].desc);
1199     res = launchTests(&testDescriptions[i], i);
1200     if (res != 0)
1201 	ret++;
1202     if (verbose) {
1203 	if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1204 	    printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1205 	else
1206 	    printf("Ran %d tests, %d errors, %d leaks\n",
1207 		   nb_tests - old_tests,
1208 		   nb_errors - old_errors,
1209 		   nb_leaks - old_leaks);
1210     }
1211     return(ret);
1212 }
1213 
1214 static int
launchCrazySAX(unsigned int test,int fail)1215 launchCrazySAX(unsigned int test, int fail) {
1216     int res = 0, err = 0;
1217 
1218     crazy_indx = test;
1219 
1220     res = saxTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail);
1221     if (res != 0) {
1222         nb_errors++;
1223         err++;
1224     }
1225     if (tests_quiet == 0)
1226         fprintf(stderr, "%c", crazy[test]);
1227 
1228     return(err);
1229 }
1230 
1231 #ifdef LIBXML_READER_ENABLED
1232 static int
launchCrazy(unsigned int test,int fail)1233 launchCrazy(unsigned int test, int fail) {
1234     int res = 0, err = 0;
1235 
1236     crazy_indx = test;
1237 
1238     res = readerTest("crazy::test", XML_MAX_LOOKUP_LIMIT - CHUNK, 0, fail);
1239     if (res != 0) {
1240         nb_errors++;
1241         err++;
1242     }
1243     if (tests_quiet == 0)
1244         fprintf(stderr, "%c", crazy[test]);
1245 
1246     return(err);
1247 }
1248 #endif
1249 
get_crazy_fail(int test)1250 static int get_crazy_fail(int test) {
1251     /*
1252      * adding 1000000 of character 'a' leads to parser failure mostly
1253      * everywhere except in those special spots. Need to be updated
1254      * each time crazy is updated
1255      */
1256     int fail = 1;
1257     if ((test == 44) || /* PI in Misc */
1258         ((test >= 50) && (test <= 55)) || /* Comment in Misc */
1259         (test == 79) || /* PI in DTD */
1260         ((test >= 85) && (test <= 90)) || /* Comment in DTD */
1261         (test == 154) || /* PI in Misc */
1262         ((test >= 160) && (test <= 165)) || /* Comment in Misc */
1263         ((test >= 178) && (test <= 181)) || /* attribute value */
1264         (test == 183) || /* Text */
1265         (test == 189) || /* PI in Content */
1266         (test == 191) || /* Text */
1267         ((test >= 195) && (test <= 200)) || /* Comment in Content */
1268         ((test >= 203) && (test <= 206)) || /* Text */
1269         (test == 215) || (test == 216) || /* in CDATA */
1270         (test == 219) || /* Text */
1271         (test == 231) || /* PI in Misc */
1272         ((test >= 237) && (test <= 242))) /* Comment in Misc */
1273         fail = 0;
1274     return(fail);
1275 }
1276 
1277 static int
runcrazy(void)1278 runcrazy(void) {
1279     int ret = 0, res = 0;
1280     int old_errors, old_tests, old_leaks;
1281     unsigned int i;
1282 
1283     old_errors = nb_errors;
1284     old_tests = nb_tests;
1285     old_leaks = nb_leaks;
1286 
1287 #ifdef LIBXML_READER_ENABLED
1288     if (tests_quiet == 0) {
1289 	printf("## Crazy tests on reader\n");
1290     }
1291     for (i = 0;i < strlen(crazy);i++) {
1292         res += launchCrazy(i, get_crazy_fail(i));
1293         if (res != 0)
1294             ret++;
1295     }
1296 #endif
1297 
1298     if (tests_quiet == 0) {
1299 	printf("\n## Crazy tests on SAX\n");
1300     }
1301     for (i = 0;i < strlen(crazy);i++) {
1302         res += launchCrazySAX(i, get_crazy_fail(i));
1303         if (res != 0)
1304             ret++;
1305     }
1306     if (tests_quiet == 0)
1307         fprintf(stderr, "\n");
1308     if (verbose) {
1309 	if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
1310 	    printf("Ran %d tests, no errors\n", nb_tests - old_tests);
1311 	else
1312 	    printf("Ran %d tests, %d errors, %d leaks\n",
1313 		   nb_tests - old_tests,
1314 		   nb_errors - old_errors,
1315 		   nb_leaks - old_leaks);
1316     }
1317     return(ret);
1318 }
1319 
1320 
1321 int
main(int argc ATTRIBUTE_UNUSED,char ** argv ATTRIBUTE_UNUSED)1322 main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
1323     int i, a, ret = 0;
1324     int subset = 0;
1325 
1326     fillFilling();
1327     initializeLibxml2();
1328 
1329     for (a = 1; a < argc;a++) {
1330         if (!strcmp(argv[a], "-v"))
1331 	    verbose = 1;
1332         else if (!strcmp(argv[a], "-quiet"))
1333 	    tests_quiet = 1;
1334         else if (!strcmp(argv[a], "-crazy"))
1335 	    subset = 1;
1336     }
1337     if (subset == 0) {
1338 	for (i = 0; testDescriptions[i].func != NULL; i++) {
1339 	    ret += runtest(i);
1340 	}
1341     }
1342     ret += runcrazy();
1343     if ((nb_errors == 0) && (nb_leaks == 0)) {
1344         ret = 0;
1345 	printf("Total %d tests, no errors\n",
1346 	       nb_tests);
1347     } else {
1348         ret = 1;
1349 	printf("Total %d tests, %d errors, %d leaks\n",
1350 	       nb_tests, nb_errors, nb_leaks);
1351     }
1352     xmlCleanupParser();
1353 
1354     return(ret);
1355 }
1356