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