xref: /aosp_15_r20/external/libxml2/fuzz/fuzz.c (revision 7c5688314b92172186c154356a6374bf7684c3ca)
1 /*
2  * fuzz.c: Common functions for fuzzing.
3  *
4  * See Copyright for the status of this software.
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <sys/stat.h>
11 
12 #include <libxml/hash.h>
13 #include <libxml/parser.h>
14 #include <libxml/parserInternals.h>
15 #include <libxml/tree.h>
16 #include <libxml/xmlIO.h>
17 #include "fuzz.h"
18 
19 typedef struct {
20     const char *data;
21     size_t size;
22 } xmlFuzzEntityInfo;
23 
24 /* Single static instance for now */
25 static struct {
26     /* Original data */
27     const char *data;
28     size_t size;
29 
30     /* Remaining data */
31     const char *ptr;
32     size_t remaining;
33 
34     /* Buffer for unescaped strings */
35     char *outBuf;
36     char *outPtr; /* Free space at end of buffer */
37 
38     xmlHashTablePtr entities; /* Maps URLs to xmlFuzzEntityInfos */
39 
40     /* The first entity is the main entity. */
41     const char *mainUrl;
42     xmlFuzzEntityInfo *mainEntity;
43 } fuzzData;
44 
45 size_t fuzzNumAllocs;
46 size_t fuzzMaxAllocs;
47 int fuzzAllocFailed;
48 
49 /**
50  * xmlFuzzErrorFunc:
51  *
52  * An error function that simply discards all errors.
53  */
54 void
xmlFuzzErrorFunc(void * ctx ATTRIBUTE_UNUSED,const char * msg ATTRIBUTE_UNUSED,...)55 xmlFuzzErrorFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
56                  ...) {
57 }
58 
59 /**
60  * xmlFuzzSErrorFunc:
61  *
62  * A structured error function that simply discards all errors.
63  */
64 void
xmlFuzzSErrorFunc(void * ctx ATTRIBUTE_UNUSED,const xmlError * error ATTRIBUTE_UNUSED)65 xmlFuzzSErrorFunc(void *ctx ATTRIBUTE_UNUSED,
66                   const xmlError *error ATTRIBUTE_UNUSED) {
67 }
68 
69 /*
70  * Malloc failure injection.
71  *
72  * To debug issues involving malloc failures, it's often helpful to set
73  * MALLOC_ABORT to 1. This should provide a backtrace of the failed
74  * allocation.
75  */
76 
77 #define XML_FUZZ_MALLOC_ABORT   0
78 
79 static void *
xmlFuzzMalloc(size_t size)80 xmlFuzzMalloc(size_t size) {
81     void *ret;
82 
83     if (fuzzMaxAllocs > 0) {
84         fuzzNumAllocs += 1;
85         if (fuzzNumAllocs == fuzzMaxAllocs) {
86 #if XML_FUZZ_MALLOC_ABORT
87             abort();
88 #endif
89             fuzzAllocFailed = 1;
90             return NULL;
91         }
92     }
93 
94     ret = malloc(size);
95     if (ret == NULL)
96         fuzzAllocFailed = 1;
97 
98     return ret;
99 }
100 
101 static void *
xmlFuzzRealloc(void * ptr,size_t size)102 xmlFuzzRealloc(void *ptr, size_t size) {
103     void *ret;
104 
105     if (fuzzMaxAllocs > 0) {
106         fuzzNumAllocs += 1;
107         if (fuzzNumAllocs == fuzzMaxAllocs) {
108 #if XML_FUZZ_MALLOC_ABORT
109             abort();
110 #endif
111             fuzzAllocFailed = 1;
112             return NULL;
113         }
114     }
115 
116     ret = realloc(ptr, size);
117     if (ret == NULL)
118         fuzzAllocFailed = 1;
119 
120     return ret;
121 }
122 
123 void
xmlFuzzMemSetup(void)124 xmlFuzzMemSetup(void) {
125     xmlMemSetup(free, xmlFuzzMalloc, xmlFuzzRealloc, xmlMemStrdup);
126 }
127 
128 void
xmlFuzzMemSetLimit(size_t limit)129 xmlFuzzMemSetLimit(size_t limit) {
130     fuzzNumAllocs = 0;
131     fuzzMaxAllocs = limit;
132     fuzzAllocFailed = 0;
133 }
134 
135 int
xmlFuzzMallocFailed(void)136 xmlFuzzMallocFailed(void) {
137     return fuzzAllocFailed;
138 }
139 
140 void
xmlFuzzResetMallocFailed(void)141 xmlFuzzResetMallocFailed(void) {
142     fuzzAllocFailed = 0;
143 }
144 
145 void
xmlFuzzCheckMallocFailure(const char * func,int error)146 xmlFuzzCheckMallocFailure(const char *func, int error) {
147     if (error >= 0 && fuzzAllocFailed != error) {
148         fprintf(stderr, "%s: malloc failure %s reported\n",
149                 func, fuzzAllocFailed ? "not" : "erroneously");
150         abort();
151     }
152     fuzzAllocFailed = 0;
153 }
154 
155 /**
156  * xmlFuzzDataInit:
157  *
158  * Initialize fuzz data provider.
159  */
160 void
xmlFuzzDataInit(const char * data,size_t size)161 xmlFuzzDataInit(const char *data, size_t size) {
162     fuzzData.data = data;
163     fuzzData.size = size;
164     fuzzData.ptr = data;
165     fuzzData.remaining = size;
166 
167     fuzzData.outBuf = xmlMalloc(size + 1);
168     fuzzData.outPtr = fuzzData.outBuf;
169 
170     fuzzData.entities = xmlHashCreate(8);
171     fuzzData.mainUrl = NULL;
172     fuzzData.mainEntity = NULL;
173 }
174 
175 /**
176  * xmlFuzzDataFree:
177  *
178  * Cleanup fuzz data provider.
179  */
180 void
xmlFuzzDataCleanup(void)181 xmlFuzzDataCleanup(void) {
182     xmlFree(fuzzData.outBuf);
183     xmlHashFree(fuzzData.entities, xmlHashDefaultDeallocator);
184 }
185 
186 /**
187  * xmlFuzzWriteInt:
188  * @out:  output file
189  * @v:  integer to write
190  * @size:  size of integer in bytes
191  *
192  * Write an integer to the fuzz data.
193  */
194 void
xmlFuzzWriteInt(FILE * out,size_t v,int size)195 xmlFuzzWriteInt(FILE *out, size_t v, int size) {
196     int shift;
197 
198     while (size > (int) sizeof(size_t)) {
199         putc(0, out);
200         size--;
201     }
202 
203     shift = size * 8;
204     while (shift > 0) {
205         shift -= 8;
206         putc((v >> shift) & 255, out);
207     }
208 }
209 
210 /**
211  * xmlFuzzReadInt:
212  * @size:  size of integer in bytes
213  *
214  * Read an integer from the fuzz data.
215  */
216 size_t
xmlFuzzReadInt(int size)217 xmlFuzzReadInt(int size) {
218     size_t ret = 0;
219 
220     while ((size > 0) && (fuzzData.remaining > 0)) {
221         unsigned char c = (unsigned char) *fuzzData.ptr++;
222         fuzzData.remaining--;
223         ret = (ret << 8) | c;
224         size--;
225     }
226 
227     return ret;
228 }
229 
230 /**
231  * xmlFuzzBytesRemaining:
232  *
233  * Return number of remaining bytes in fuzz data.
234  */
235 size_t
xmlFuzzBytesRemaining(void)236 xmlFuzzBytesRemaining(void) {
237     return(fuzzData.remaining);
238 }
239 
240 /**
241  * xmlFuzzReadRemaining:
242  * @size:  size of string in bytes
243  *
244  * Read remaining bytes from fuzz data.
245  */
246 const char *
xmlFuzzReadRemaining(size_t * size)247 xmlFuzzReadRemaining(size_t *size) {
248     const char *ret = fuzzData.ptr;
249 
250     *size = fuzzData.remaining;
251     fuzzData.ptr += fuzzData.remaining;
252     fuzzData.remaining = 0;
253 
254     return(ret);
255 }
256 
257 /*
258  * xmlFuzzWriteString:
259  * @out:  output file
260  * @str:  string to write
261  *
262  * Write a random-length string to file in a format similar to
263  * FuzzedDataProvider. Backslash followed by newline marks the end of the
264  * string. Two backslashes are used to escape a backslash.
265  */
266 void
xmlFuzzWriteString(FILE * out,const char * str)267 xmlFuzzWriteString(FILE *out, const char *str) {
268     for (; *str; str++) {
269         int c = (unsigned char) *str;
270         putc(c, out);
271         if (c == '\\')
272             putc(c, out);
273     }
274     putc('\\', out);
275     putc('\n', out);
276 }
277 
278 /**
279  * xmlFuzzReadString:
280  * @size:  size of string in bytes
281  *
282  * Read a random-length string from the fuzz data.
283  *
284  * The format is similar to libFuzzer's FuzzedDataProvider but treats
285  * backslash followed by newline as end of string. This makes the fuzz data
286  * more readable. A backslash character is escaped with another backslash.
287  *
288  * Returns a zero-terminated string or NULL if the fuzz data is exhausted.
289  */
290 const char *
xmlFuzzReadString(size_t * size)291 xmlFuzzReadString(size_t *size) {
292     const char *out = fuzzData.outPtr;
293 
294     while (fuzzData.remaining > 0) {
295         int c = *fuzzData.ptr++;
296         fuzzData.remaining--;
297 
298         if ((c == '\\') && (fuzzData.remaining > 0)) {
299             int c2 = *fuzzData.ptr;
300 
301             if (c2 == '\n') {
302                 fuzzData.ptr++;
303                 fuzzData.remaining--;
304                 if (size != NULL)
305                     *size = fuzzData.outPtr - out;
306                 *fuzzData.outPtr++ = '\0';
307                 return(out);
308             }
309             if (c2 == '\\') {
310                 fuzzData.ptr++;
311                 fuzzData.remaining--;
312             }
313         }
314 
315         *fuzzData.outPtr++ = c;
316     }
317 
318     if (fuzzData.outPtr > out) {
319         if (size != NULL)
320             *size = fuzzData.outPtr - out;
321         *fuzzData.outPtr++ = '\0';
322         return(out);
323     }
324 
325     if (size != NULL)
326         *size = 0;
327     return(NULL);
328 }
329 
330 /**
331  * xmlFuzzReadEntities:
332  *
333  * Read entities like the main XML file, external DTDs, external parsed
334  * entities from fuzz data.
335  */
336 void
xmlFuzzReadEntities(void)337 xmlFuzzReadEntities(void) {
338     size_t num = 0;
339 
340     while (1) {
341         const char *url, *entity;
342         size_t urlSize, entitySize;
343         xmlFuzzEntityInfo *entityInfo;
344 
345         url = xmlFuzzReadString(&urlSize);
346         if (url == NULL) break;
347 
348         entity = xmlFuzzReadString(&entitySize);
349         if (entity == NULL) break;
350 
351         /*
352          * Cap URL size to avoid quadratic behavior when generating
353          * error messages or looking up entities.
354          */
355         if (urlSize < 50 &&
356             xmlHashLookup(fuzzData.entities, (xmlChar *)url) == NULL) {
357             entityInfo = xmlMalloc(sizeof(xmlFuzzEntityInfo));
358             if (entityInfo == NULL)
359                 break;
360             entityInfo->data = entity;
361             entityInfo->size = entitySize;
362 
363             xmlHashAddEntry(fuzzData.entities, (xmlChar *)url, entityInfo);
364 
365             if (num == 0) {
366                 fuzzData.mainUrl = url;
367                 fuzzData.mainEntity = entityInfo;
368             }
369 
370             num++;
371         }
372     }
373 }
374 
375 /**
376  * xmlFuzzMainUrl:
377  *
378  * Returns the main URL.
379  */
380 const char *
xmlFuzzMainUrl(void)381 xmlFuzzMainUrl(void) {
382     return(fuzzData.mainUrl);
383 }
384 
385 /**
386  * xmlFuzzMainEntity:
387  * @size:  size of the main entity in bytes
388  *
389  * Returns the main entity.
390  */
391 const char *
xmlFuzzMainEntity(size_t * size)392 xmlFuzzMainEntity(size_t *size) {
393     if (fuzzData.mainEntity == NULL)
394         return(NULL);
395     *size = fuzzData.mainEntity->size;
396     return(fuzzData.mainEntity->data);
397 }
398 
399 /**
400  * xmlFuzzResourceLoader:
401  *
402  * The resource loader for fuzz data.
403  */
404 int
xmlFuzzResourceLoader(void * data ATTRIBUTE_UNUSED,const char * URL,const char * ID ATTRIBUTE_UNUSED,xmlResourceType type ATTRIBUTE_UNUSED,int flags ATTRIBUTE_UNUSED,xmlParserInputPtr * out)405 xmlFuzzResourceLoader(void *data ATTRIBUTE_UNUSED, const char *URL,
406                       const char *ID ATTRIBUTE_UNUSED,
407                       xmlResourceType type ATTRIBUTE_UNUSED,
408                       int flags ATTRIBUTE_UNUSED, xmlParserInputPtr *out) {
409     xmlParserInputPtr input;
410     xmlFuzzEntityInfo *entity;
411 
412     entity = xmlHashLookup(fuzzData.entities, (xmlChar *) URL);
413     if (entity == NULL)
414         return(XML_IO_ENOENT);
415 
416     input = xmlNewInputFromMemory(URL, entity->data, entity->size,
417                                   XML_INPUT_BUF_STATIC |
418                                   XML_INPUT_BUF_ZERO_TERMINATED);
419     if (input == NULL)
420         return(XML_ERR_NO_MEMORY);
421 
422     *out = input;
423     return(XML_ERR_OK);
424 }
425 
426 /**
427  * xmlFuzzEntityLoader:
428  *
429  * The entity loader for fuzz data.
430  */
431 xmlParserInputPtr
xmlFuzzEntityLoader(const char * URL,const char * ID ATTRIBUTE_UNUSED,xmlParserCtxtPtr ctxt)432 xmlFuzzEntityLoader(const char *URL, const char *ID ATTRIBUTE_UNUSED,
433                     xmlParserCtxtPtr ctxt) {
434     xmlParserInputBufferPtr buf;
435     xmlFuzzEntityInfo *entity;
436 
437     if (URL == NULL)
438         return(NULL);
439     entity = xmlHashLookup(fuzzData.entities, (xmlChar *) URL);
440     if (entity == NULL)
441         return(NULL);
442 
443     buf = xmlParserInputBufferCreateMem(entity->data, entity->size,
444                                         XML_CHAR_ENCODING_NONE);
445     if (buf == NULL) {
446         xmlCtxtErrMemory(ctxt);
447         return(NULL);
448     }
449 
450     return(xmlNewIOInputStream(ctxt, buf, XML_CHAR_ENCODING_NONE));
451 }
452 
453 char *
xmlSlurpFile(const char * path,size_t * sizeRet)454 xmlSlurpFile(const char *path, size_t *sizeRet) {
455     FILE *file;
456     struct stat statbuf;
457     char *data;
458     size_t size;
459 
460     if ((stat(path, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode)))
461         return(NULL);
462     size = statbuf.st_size;
463     file = fopen(path, "rb");
464     if (file == NULL)
465         return(NULL);
466     data = xmlMalloc(size + 1);
467     if (data != NULL) {
468         if (fread(data, 1, size, file) != size) {
469             xmlFree(data);
470             data = NULL;
471         } else {
472             data[size] = 0;
473             if (sizeRet != NULL)
474                 *sizeRet = size;
475         }
476     }
477     fclose(file);
478 
479     return(data);
480 }
481 
482