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