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