1 /*
2  * testOOM.c: Test out-of-memory handling
3  *
4  * See Copyright for the status of this software.
5  *
6  * hp@redhat.com
7  */
8 
9 #include "libxml.h"
10 
11 #include <string.h>
12 #include <stdarg.h>
13 
14 #ifdef HAVE_SYS_TYPES_H
15 #include <sys/types.h>
16 #endif
17 #ifdef HAVE_UNISTD_H
18 #include <unistd.h>
19 #endif
20 #ifdef HAVE_STDLIB_H
21 #include <stdlib.h>
22 #endif
23 #ifdef HAVE_STRING_H
24 #include <string.h>
25 #endif
26 
27 #include <libxml/xmlreader.h>
28 
29 #include "testOOMlib.h"
30 
31 #ifndef TRUE
32 #define TRUE (1)
33 #endif
34 #ifndef FALSE
35 #define FALSE (0)
36 #endif
37 
38 #define EXIT_OOM 2
39 
40 int error = FALSE;
41 int errcount = 0;
42 int noent = 0;
43 int count = 0;
44 int valid = 0;
45 int showErrs = 0;
46 
47 /*
48  * Since we are using the xmlTextReader functions, we set up
49  * strings for the element types to help in debugging any error
50  * output
51  */
52 const char *elementNames[] = {
53     "XML_READER_TYPE_NONE",
54     "XML_READER_TYPE_ELEMENT",
55     "XML_READER_TYPE_ATTRIBUTE",
56     "XML_READER_TYPE_TEXT",
57     "XML_READER_TYPE_CDATA",
58     "XML_READER_TYPE_ENTITY_REFERENCE",
59     "XML_READER_TYPE_ENTITY",
60     "XML_READER_TYPE_PROCESSING_INSTRUCTION",
61     "XML_READER_TYPE_COMMENT",
62     "XML_READER_TYPE_DOCUMENT",
63     "XML_READER_TYPE_DOCUMENT_TYPE",
64     "XML_READER_TYPE_DOCUMENT_FRAGMENT",
65     "XML_READER_TYPE_NOTATION",
66     "XML_READER_TYPE_WHITESPACE",
67     "XML_READER_TYPE_SIGNIFICANT_WHITESPACE",
68     "XML_READER_TYPE_END_ELEMENT",
69     "XML_READER_TYPE_END_ENTITY",
70     "XML_READER_TYPE_XML_DECLARATION"};
71 
72 /* not using xmlBuff here because I don't want those
73  * mallocs to interfere */
74 struct buffer {
75     char *str;
76     size_t len;
77     size_t max;
78 };
79 
buffer_create(size_t init_len)80 static struct buffer *buffer_create (size_t init_len)
81 {
82     struct buffer *b;
83     b = malloc (sizeof *b);
84     if (b == NULL)
85 	exit (EXIT_OOM);
86     if (init_len) {
87 	b->str = malloc (init_len);
88 	if (b->str == NULL)
89 	    exit (EXIT_OOM);
90     }
91     else
92 	b->str = NULL;
93     b->len = 0;
94     b->max = init_len;
95     return b;
96 }
97 
buffer_free(struct buffer * b)98 static void buffer_free (struct buffer *b)
99 {
100     free (b->str);
101     free (b);
102 }
103 
buffer_get_length(struct buffer * b)104 static size_t buffer_get_length (struct buffer *b)
105 {
106     return b->len;
107 }
108 
buffer_expand(struct buffer * b,size_t min)109 static void buffer_expand (struct buffer *b, size_t min)
110 {
111     void *new_str;
112     size_t new_size = b->max ? b->max : 512;
113     while (new_size < b->len + min)
114 	new_size *= 2;
115     if (new_size > b->max) {
116 	new_str = realloc (b->str, new_size);
117 	if (new_str == NULL)
118 	    exit (EXIT_OOM);
119 	b->str = new_str;
120 	b->max = new_size;
121     }
122 }
123 
buffer_add_char(struct buffer * b,char c)124 static void buffer_add_char (struct buffer *b, char c)
125 {
126     buffer_expand (b, 1);
127     b->str[b->len] = c;
128     b->len += 1;
129 }
130 
buffer_add_string(struct buffer * b,const char * s)131 static void buffer_add_string (struct buffer *b, const char *s)
132 {
133     size_t size = strlen(s) + 1;
134     unsigned int ix;
135     for (ix=0; ix<size-1; ix++) {
136         if (s[ix] < 0x20)
137 	    printf ("binary data [0x%02x]?\n", (unsigned char)s[ix]);
138     }
139     buffer_expand (b, size);
140     strcpy (b->str + b->len, s);
141     b->str[b->len+size-1] = '\n';	/* replace string term with newline */
142     b->len += size;
143 }
144 
buffer_equal(struct buffer * b1,struct buffer * b2)145 static int buffer_equal (struct buffer *b1, struct buffer *b2)
146 {
147     return (b1->len == b2->len &&
148 	    (b1->len == 0 || (memcmp (b1->str, b2->str, b1->len) == 0)));
149 }
150 
buffer_dump(struct buffer * b,const char * fname)151 static void buffer_dump (struct buffer *b, const char *fname)
152 {
153     FILE *f = fopen (fname, "wb");
154     if (f != NULL) {
155 	fwrite (b->str, 1, b->len, f);
156 	fclose (f);
157     }
158 }
159 
160 
usage(const char * progname)161 static void usage(const char *progname) {
162     printf("Usage : %s [options] XMLfiles ...\n", progname);
163     printf("\tParse the XML files using the xmlTextReader API\n");
164     printf("\t --count: count the number of attribute and elements\n");
165     printf("\t --valid: validate the document\n");
166     printf("\t --show:  display the error messages encountered\n");
167     exit(1);
168 }
169 static unsigned int elem, attrs, chars;
170 
processNode(xmlTextReaderPtr reader,void * data)171 static int processNode (xmlTextReaderPtr reader, void *data)
172 {
173     struct buffer *buff = data;
174     int type;
175 
176     type = xmlTextReaderNodeType(reader);
177     if (count) {
178 	if (type == 1) {
179 	    elem++;
180 	    attrs += xmlTextReaderAttributeCount(reader);
181 	} else if (type == 3) {
182 	  const xmlChar *txt;
183 	  txt = xmlTextReaderConstValue(reader);
184 	  if (txt != NULL)
185 	    chars += xmlStrlen (txt);
186 	  else
187 	    return FALSE;
188 	}
189     }
190 
191     if (buff != NULL) {
192 	int ret;
193 	const char *s;
194 
195 	buffer_add_string (buff, elementNames[type]);
196 
197 	if (type == 1) {
198 	    s = (const char *)xmlTextReaderConstName (reader);
199 	    if (s == NULL) return FALSE;
200 	    buffer_add_string (buff, s);
201 	    while ((ret = xmlTextReaderMoveToNextAttribute (reader)) == 1) {
202 		s = (const char *)xmlTextReaderConstName (reader);
203 		if (s == NULL) return FALSE;
204 		buffer_add_string (buff, s);
205 		buffer_add_char (buff, '=');
206 		s = (const char *)xmlTextReaderConstValue (reader);
207 		if (s == NULL) return FALSE;
208 		buffer_add_string (buff, s);
209 	    }
210 	    if (ret == -1) return FALSE;
211 	}
212 	else if (type == 3) {
213 	    s = (const char *)xmlTextReaderConstValue (reader);
214 	    if (s == NULL) return FALSE;
215 	    buffer_add_string (buff, s);
216 	}
217     }
218 
219     return TRUE;
220 }
221 
222 
223 struct file_params {
224     const char *filename;
225     struct buffer *verif_buff;
226 };
227 
228 static void
error_func(void * data ATTRIBUTE_UNUSED,xmlErrorPtr err)229 error_func (void *data ATTRIBUTE_UNUSED, xmlErrorPtr err)
230 {
231 
232     errcount++;
233     if (err->level == XML_ERR_ERROR ||
234         err->level == XML_ERR_FATAL)
235         error = TRUE;
236     if (showErrs) {
237         printf("%3d line %d: %s\n", error, err->line, err->message);
238     }
239 }
240 
241 static int
check_load_file_memory_func(void * data)242 check_load_file_memory_func (void *data)
243 {
244      struct file_params *p = data;
245      struct buffer *b;
246      xmlTextReaderPtr reader;
247      int ret, status, first_run;
248 
249      if (count) {
250          elem = 0;
251          attrs = 0;
252          chars = 0;
253      }
254 
255      first_run = p->verif_buff == NULL;
256      status = TRUE;
257      error = FALSE;
258      if (first_run)
259 	 b = buffer_create (0);
260      else
261 	 b = buffer_create (buffer_get_length (p->verif_buff));
262 
263      reader = xmlNewTextReaderFilename (p->filename);
264      if (reader == NULL)
265        goto out;
266 
267      xmlTextReaderSetStructuredErrorHandler (reader, error_func, NULL);
268      xmlSetStructuredErrorFunc(NULL, error_func);
269 
270      if (valid) {
271        if (xmlTextReaderSetParserProp(reader, XML_PARSER_VALIDATE, 1) == -1)
272 	 goto out;
273      }
274 
275      /*
276       * Process all nodes in sequence
277       */
278      while ((ret = xmlTextReaderRead(reader)) == 1) {
279 	 if (!processNode(reader, b))
280 	 goto out;
281      }
282      if (ret == -1)
283        goto out;
284 
285      if (error) {
286 	 fprintf (stdout, "error handler was called but parse completed successfully (last error #%d)\n", errcount);
287 	 return FALSE;
288      }
289 
290      /*
291       * Done, cleanup and status
292       */
293      if (! first_run) {
294 	 status = buffer_equal (p->verif_buff, b);
295 	 if (! status) {
296 	     buffer_dump (p->verif_buff, ".OOM.verif_buff");
297 	     buffer_dump (b, ".OOM.buff");
298 	 }
299      }
300 
301      if (count)
302        {
303 	   fprintf (stdout, "# %s: %u elems, %u attrs, %u chars %s\n",
304 		    p->filename, elem, attrs, chars,
305 		    status ? "ok" : "wrong");
306        }
307 
308  out:
309      if (first_run)
310 	 p->verif_buff = b;
311      else
312 	 buffer_free (b);
313      if (reader)
314 	 xmlFreeTextReader (reader);
315      return status;
316 }
317 
main(int argc,char ** argv)318 int main(int argc, char **argv) {
319     int i;
320     int files = 0;
321 
322     if (argc <= 1) {
323 	usage(argv[0]);
324 	return(1);
325     }
326     LIBXML_TEST_VERSION;
327 
328     xmlMemSetup (test_free,
329                  test_malloc,
330                  test_realloc,
331                  test_strdup);
332 
333     xmlInitParser();
334 
335     for (i = 1; i < argc ; i++) {
336         if ((!strcmp(argv[i], "-count")) || (!strcmp(argv[i], "--count")))
337 	    count++;
338 	else if ((!strcmp(argv[i], "-valid")) || (!strcmp(argv[i], "--valid")))
339 	    valid++;
340 	else if ((!strcmp(argv[i], "-noent")) ||
341 	         (!strcmp(argv[i], "--noent")))
342 	    noent++;
343 	else if ((!strcmp(argv[i], "-show")) ||
344 		 (!strcmp(argv[i], "--show")))
345 	    showErrs++;
346     }
347     if (noent != 0)
348       xmlSubstituteEntitiesDefault(1);
349     for (i = 1; i < argc ; i++) {
350 	if (argv[i][0] != '-') {
351              struct file_params p;
352 	     p.filename = argv[i];
353 	     p.verif_buff = NULL;
354 
355              if (!test_oom_handling (check_load_file_memory_func,
356                                      &p)) {
357                   fprintf (stdout, "Failed!\n");
358                   return 1;
359              }
360 
361 	     buffer_free (p.verif_buff);
362              xmlCleanupParser();
363 
364              if (test_get_malloc_blocks_outstanding () > 0) {
365                   fprintf (stdout, "%d blocks leaked\n",
366                            test_get_malloc_blocks_outstanding ());
367 		  xmlMemoryDump();
368                   return 1;
369              }
370 
371 	    files ++;
372 	}
373     }
374     xmlMemoryDump();
375 
376     return 0;
377 }
378