• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 /**
46  * xmlFuzzErrorFunc:
47  *
48  * An error function that simply discards all errors.
49  */
50 void
xmlFuzzErrorFunc(void * ctx ATTRIBUTE_UNUSED,const char * msg ATTRIBUTE_UNUSED,...)51 xmlFuzzErrorFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
52                  ...) {
53 }
54 
55 /**
56  * xmlFuzzDataInit:
57  *
58  * Initialize fuzz data provider.
59  */
60 void
xmlFuzzDataInit(const char * data,size_t size)61 xmlFuzzDataInit(const char *data, size_t size) {
62     fuzzData.data = data;
63     fuzzData.size = size;
64     fuzzData.ptr = data;
65     fuzzData.remaining = size;
66 
67     fuzzData.outBuf = xmlMalloc(size + 1);
68     fuzzData.outPtr = fuzzData.outBuf;
69 
70     fuzzData.entities = xmlHashCreate(8);
71     fuzzData.mainUrl = NULL;
72     fuzzData.mainEntity = NULL;
73 }
74 
75 /**
76  * xmlFuzzDataFree:
77  *
78  * Cleanup fuzz data provider.
79  */
80 void
xmlFuzzDataCleanup(void)81 xmlFuzzDataCleanup(void) {
82     xmlFree(fuzzData.outBuf);
83     xmlHashFree(fuzzData.entities, xmlHashDefaultDeallocator);
84 }
85 
86 /**
87  * xmlFuzzReadInt:
88  * @size:  size of string in bytes
89  *
90  * Read an integer from the fuzz data.
91  */
92 int
xmlFuzzReadInt()93 xmlFuzzReadInt() {
94     int ret;
95 
96     if (fuzzData.remaining < sizeof(int))
97         return(0);
98     memcpy(&ret, fuzzData.ptr, sizeof(int));
99     fuzzData.ptr += sizeof(int);
100     fuzzData.remaining -= sizeof(int);
101 
102     return ret;
103 }
104 
105 /**
106  * xmlFuzzReadRemaining:
107  * @size:  size of string in bytes
108  *
109  * Read remaining bytes from fuzz data.
110  */
111 const char *
xmlFuzzReadRemaining(size_t * size)112 xmlFuzzReadRemaining(size_t *size) {
113     const char *ret = fuzzData.ptr;
114 
115     *size = fuzzData.remaining;
116     fuzzData.ptr += fuzzData.remaining;
117     fuzzData.remaining = 0;
118 
119     return(ret);
120 }
121 
122 /*
123  * xmlFuzzWriteString:
124  * @out:  output file
125  * @str:  string to write
126  *
127  * Write a random-length string to file in a format similar to
128  * FuzzedDataProvider. Backslash followed by newline marks the end of the
129  * string. Two backslashes are used to escape a backslash.
130  */
131 void
xmlFuzzWriteString(FILE * out,const char * str)132 xmlFuzzWriteString(FILE *out, const char *str) {
133     for (; *str; str++) {
134         int c = (unsigned char) *str;
135         putc(c, out);
136         if (c == '\\')
137             putc(c, out);
138     }
139     putc('\\', out);
140     putc('\n', out);
141 }
142 
143 /**
144  * xmlFuzzReadString:
145  * @size:  size of string in bytes
146  *
147  * Read a random-length string from the fuzz data.
148  *
149  * The format is similar to libFuzzer's FuzzedDataProvider but treats
150  * backslash followed by newline as end of string. This makes the fuzz data
151  * more readable. A backslash character is escaped with another backslash.
152  *
153  * Returns a zero-terminated string or NULL if the fuzz data is exhausted.
154  */
155 const char *
xmlFuzzReadString(size_t * size)156 xmlFuzzReadString(size_t *size) {
157     const char *out = fuzzData.outPtr;
158 
159     while (fuzzData.remaining > 0) {
160         int c = *fuzzData.ptr++;
161         fuzzData.remaining--;
162 
163         if ((c == '\\') && (fuzzData.remaining > 0)) {
164             int c2 = *fuzzData.ptr;
165 
166             if (c2 == '\n') {
167                 fuzzData.ptr++;
168                 fuzzData.remaining--;
169                 *size = fuzzData.outPtr - out;
170                 *fuzzData.outPtr++ = '\0';
171                 return(out);
172             }
173             if (c2 == '\\') {
174                 fuzzData.ptr++;
175                 fuzzData.remaining--;
176             }
177         }
178 
179         *fuzzData.outPtr++ = c;
180     }
181 
182     if (fuzzData.outPtr > out) {
183         *size = fuzzData.outPtr - out;
184         *fuzzData.outPtr++ = '\0';
185         return(out);
186     }
187 
188     *size = 0;
189     return(NULL);
190 }
191 
192 /**
193  * xmlFuzzReadEntities:
194  *
195  * Read entities like the main XML file, external DTDs, external parsed
196  * entities from fuzz data.
197  */
198 void
xmlFuzzReadEntities(void)199 xmlFuzzReadEntities(void) {
200     size_t num = 0;
201 
202     while (1) {
203         const char *url, *entity;
204         size_t urlSize, entitySize;
205         xmlFuzzEntityInfo *entityInfo;
206 
207         url = xmlFuzzReadString(&urlSize);
208         if (url == NULL) break;
209 
210         entity = xmlFuzzReadString(&entitySize);
211         if (entity == NULL) break;
212 
213         if (xmlHashLookup(fuzzData.entities, (xmlChar *)url) == NULL) {
214             entityInfo = xmlMalloc(sizeof(xmlFuzzEntityInfo));
215             if (entityInfo == NULL)
216                 break;
217             entityInfo->data = entity;
218             entityInfo->size = entitySize;
219 
220             xmlHashAddEntry(fuzzData.entities, (xmlChar *)url, entityInfo);
221 
222             if (num == 0) {
223                 fuzzData.mainUrl = url;
224                 fuzzData.mainEntity = entityInfo;
225             }
226 
227             num++;
228         }
229     }
230 }
231 
232 /**
233  * xmlFuzzMainUrl:
234  *
235  * Returns the main URL.
236  */
237 const char *
xmlFuzzMainUrl(void)238 xmlFuzzMainUrl(void) {
239     return(fuzzData.mainUrl);
240 }
241 
242 /**
243  * xmlFuzzMainEntity:
244  * @size:  size of the main entity in bytes
245  *
246  * Returns the main entity.
247  */
248 const char *
xmlFuzzMainEntity(size_t * size)249 xmlFuzzMainEntity(size_t *size) {
250     if (fuzzData.mainEntity == NULL)
251         return(NULL);
252     *size = fuzzData.mainEntity->size;
253     return(fuzzData.mainEntity->data);
254 }
255 
256 /**
257  * xmlFuzzEntityLoader:
258  *
259  * The entity loader for fuzz data.
260  */
261 xmlParserInputPtr
xmlFuzzEntityLoader(const char * URL,const char * ID ATTRIBUTE_UNUSED,xmlParserCtxtPtr ctxt)262 xmlFuzzEntityLoader(const char *URL, const char *ID ATTRIBUTE_UNUSED,
263                     xmlParserCtxtPtr ctxt) {
264     xmlParserInputPtr input;
265     xmlFuzzEntityInfo *entity;
266 
267     if (URL == NULL)
268         return(NULL);
269     entity = xmlHashLookup(fuzzData.entities, (xmlChar *) URL);
270     if (entity == NULL)
271         return(NULL);
272 
273     input = xmlNewInputStream(ctxt);
274     input->filename = NULL;
275     input->buf = xmlParserInputBufferCreateMem(entity->data, entity->size,
276                                                XML_CHAR_ENCODING_NONE);
277     if (input->buf == NULL) {
278         xmlFreeInputStream(input);
279         return(NULL);
280     }
281     input->base = input->cur = xmlBufContent(input->buf->buffer);
282     input->end = input->base + entity->size;
283 
284     return input;
285 }
286 
287 /**
288  * xmlFuzzExtractStrings:
289  *
290  * Extract C strings from input data. Use exact-size allocations to detect
291  * potential memory errors.
292  */
293 size_t
xmlFuzzExtractStrings(const char * data,size_t size,char ** strings,size_t numStrings)294 xmlFuzzExtractStrings(const char *data, size_t size, char **strings,
295                       size_t numStrings) {
296     const char *start = data;
297     const char *end = data + size;
298     size_t i = 0, ret;
299 
300     while (i < numStrings) {
301         size_t strSize = end - start;
302         const char *zero = memchr(start, 0, strSize);
303 
304         if (zero != NULL)
305             strSize = zero - start;
306 
307         strings[i] = xmlMalloc(strSize + 1);
308         memcpy(strings[i], start, strSize);
309         strings[i][strSize] = '\0';
310 
311         i++;
312         if (zero != NULL)
313             start = zero + 1;
314         else
315             break;
316     }
317 
318     ret = i;
319 
320     while (i < numStrings) {
321         strings[i] = NULL;
322         i++;
323     }
324 
325     return(ret);
326 }
327 
328 char *
xmlSlurpFile(const char * path,size_t * sizeRet)329 xmlSlurpFile(const char *path, size_t *sizeRet) {
330     FILE *file;
331     struct stat statbuf;
332     char *data;
333     size_t size;
334 
335     if ((stat(path, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode)))
336         return(NULL);
337     size = statbuf.st_size;
338     file = fopen(path, "rb");
339     if (file == NULL)
340         return(NULL);
341     data = xmlMalloc(size + 1);
342     if (data != NULL) {
343         if (fread(data, 1, size, file) != size) {
344             xmlFree(data);
345             data = NULL;
346         } else {
347             data[size] = 0;
348             if (sizeRet != NULL)
349                 *sizeRet = size;
350         }
351     }
352     fclose(file);
353 
354     return(data);
355 }
356 
357