Coverage Report

Created: 2023-03-26 06:14

/src/libxslt/tests/fuzz/fuzz.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * fuzz.c: Fuzz targets for libxslt
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
11
#include <libxml/parser.h>
12
#include <libxml/parserInternals.h>
13
#include <libxml/tree.h>
14
#include <libxml/xpath.h>
15
#include <libxml/xpathInternals.h>
16
#include <libxslt/extensions.h>
17
#include <libxslt/functions.h>
18
#include <libxslt/security.h>
19
#include <libxslt/transform.h>
20
#include <libxslt/xslt.h>
21
#include <libxslt/xsltInternals.h>
22
#include <libxslt/xsltutils.h>
23
#include <libexslt/exslt.h>
24
#include "fuzz.h"
25
26
#if defined(_WIN32)
27
  #define DIR_SEP '\\'
28
#else
29
  #define DIR_SEP '/'
30
#endif
31
32
static xsltSecurityPrefsPtr globalSec;
33
static xsltStylesheetPtr globalStyle;
34
static xsltTransformContextPtr tctxt;
35
36
static void
37
8.58M
xsltFuzzXmlErrorFunc(void *vctxt, const char *msg ATTRIBUTE_UNUSED, ...) {
38
8.58M
    xmlParserCtxtPtr ctxt = (xmlParserCtxtPtr) vctxt;
39
    /*
40
     * Stopping the parser should be slightly faster and might catch some
41
     * issues related to recent libxml2 changes.
42
     */
43
8.58M
    xmlStopParser(ctxt);
44
8.58M
}
45
46
static void
47
xsltFuzzXsltErrorFunc(void *vctxt ATTRIBUTE_UNUSED,
48
4.27M
                      const char *msg ATTRIBUTE_UNUSED, ...) {
49
4.27M
}
50
51
static void
52
4
xsltFuzzInit(void) {
53
4
    xmlFuzzMemSetup();
54
55
    /* Init libxml2, libxslt and libexslt */
56
4
    xmlInitParser();
57
4
    xsltInit();
58
4
    exsltRegisterAll();
59
60
    /* Suppress error messages */
61
4
    xmlSetGenericErrorFunc(NULL, xsltFuzzXmlErrorFunc);
62
4
    xsltSetGenericErrorFunc(NULL, xsltFuzzXsltErrorFunc);
63
64
    /* Disallow I/O */
65
4
    globalSec = xsltNewSecurityPrefs();
66
4
    xsltSetSecurityPrefs(globalSec, XSLT_SECPREF_READ_FILE,
67
4
                         xsltSecurityForbid);
68
4
    xsltSetSecurityPrefs(globalSec, XSLT_SECPREF_WRITE_FILE,
69
4
                         xsltSecurityForbid);
70
4
    xsltSetSecurityPrefs(globalSec, XSLT_SECPREF_CREATE_DIRECTORY,
71
4
                         xsltSecurityForbid);
72
4
    xsltSetSecurityPrefs(globalSec, XSLT_SECPREF_READ_NETWORK,
73
4
                         xsltSecurityForbid);
74
4
    xsltSetSecurityPrefs(globalSec, XSLT_SECPREF_WRITE_NETWORK,
75
4
                         xsltSecurityForbid);
76
4
}
77
78
/* XPath fuzzer
79
 *
80
 * This fuzz target parses and evaluates XPath expressions in an (E)XSLT
81
 * context using a static XML document. It heavily exercises the libxml2
82
 * XPath engine (xpath.c), a few other parts of libxml2, and most of
83
 * libexslt.
84
 *
85
 * Some EXSLT functions need the transform context to create RVTs for
86
 * node-sets. A couple of functions also access the stylesheet. The
87
 * XPath context from the transform context is used to parse and
88
 * evaluate expressions.
89
 *
90
 * All these objects are created once at startup. After fuzzing each input,
91
 * they're reset as cheaply as possible.
92
 *
93
 * TODO
94
 *
95
 * - Some expressions can create lots of temporary node sets (RVTs) which
96
 *   aren't freed until the whole expression was evaluated, leading to
97
 *   extensive memory usage. Cleaning them up earlier would require
98
 *   callbacks from the XPath engine, for example after evaluating a
99
 *   predicate expression, which doesn't seem feasible. Terminating the
100
 *   evaluation after creating a certain number of RVTs is a simple
101
 *   workaround.
102
 * - Register a custom xsl:decimal-format declaration for format-number().
103
 * - Some functions add strings to the stylesheet or transform context
104
 *   dictionary, for example via xsltGetQName, requiring a clean up of the
105
 *   dicts after fuzzing each input. This behavior seems questionable.
106
 *   Extension functions shouldn't needlessly modify the transform context
107
 *   or stylesheet.
108
 * - Register xsl:keys and fuzz the key() function.
109
 * - Add a few custom func:functions.
110
 * - Fuzz the document() function with external documents.
111
 */
112
113
int
114
2
xsltFuzzXPathInit(void) {
115
2
    xsltFuzzInit();
116
2
    globalStyle = xsltNewStylesheet();
117
2
    return(0);
118
2
}
119
120
xmlXPathObjectPtr
121
160
xsltFuzzXPath(const char *data, size_t size) {
122
160
    xmlXPathContextPtr xpctxt = NULL;
123
160
    xmlXPathObjectPtr xpathObj = NULL;
124
160
    xmlDocPtr doc;
125
160
    xmlNodePtr root;
126
160
    const char *xpathExpr, *xml;
127
160
    size_t maxAllocs, xmlSize;
128
129
160
    xmlFuzzDataInit(data, size);
130
131
160
    maxAllocs = xmlFuzzReadInt(4) % (size + 1);
132
160
    xpathExpr = xmlFuzzReadString(NULL);
133
160
    xml = xmlFuzzReadString(&xmlSize);
134
135
    /* Recovery mode allows more input to be fuzzed. */
136
160
    doc = xmlReadMemory(xml, xmlSize, NULL, NULL, XML_PARSE_RECOVER);
137
160
    if (doc == NULL)
138
8
        goto error;
139
152
    root = xmlDocGetRootElement(doc);
140
152
    if (root != NULL) {
141
119
        xmlNewNs(root, BAD_CAST "a", BAD_CAST "a");
142
119
        xmlNewNs(root, BAD_CAST "b", BAD_CAST "b");
143
119
        xmlNewNs(root, BAD_CAST "c", BAD_CAST "c");
144
119
    }
145
146
152
    tctxt = xsltNewTransformContext(globalStyle, doc);
147
152
    if (tctxt == NULL) {
148
0
        xmlFreeDoc(doc);
149
0
        goto error;
150
0
    }
151
152
    xsltSetCtxtSecurityPrefs(globalSec, tctxt);
152
153
    /*
154
     * Some extension functions need the current instruction.
155
     *
156
     * - format-number() for namespaces.
157
     * - document() for the base URL.
158
     * - maybe others?
159
     *
160
     * For fuzzing, it's enough to use the source document's root element.
161
     */
162
152
    tctxt->inst = xmlDocGetRootElement(doc);
163
164
    /* Set up XPath context */
165
152
    xpctxt = tctxt->xpathCtxt;
166
167
    /* Resource limits to avoid timeouts and call stack overflows */
168
152
    xpctxt->opLimit = 500000;
169
170
    /* Test namespaces */
171
152
    xmlXPathRegisterNs(xpctxt, BAD_CAST "a", BAD_CAST "a");
172
152
    xmlXPathRegisterNs(xpctxt, BAD_CAST "b", BAD_CAST "b");
173
152
    xmlXPathRegisterNs(xpctxt, BAD_CAST "c", BAD_CAST "c");
174
175
    /* EXSLT namespaces */
176
152
    xmlXPathRegisterNs(xpctxt, BAD_CAST "crypto", EXSLT_CRYPTO_NAMESPACE);
177
152
    xmlXPathRegisterNs(xpctxt, BAD_CAST "date", EXSLT_DATE_NAMESPACE);
178
152
    xmlXPathRegisterNs(xpctxt, BAD_CAST "dyn", EXSLT_DYNAMIC_NAMESPACE);
179
152
    xmlXPathRegisterNs(xpctxt, BAD_CAST "exsl", EXSLT_COMMON_NAMESPACE);
180
152
    xmlXPathRegisterNs(xpctxt, BAD_CAST "math", EXSLT_MATH_NAMESPACE);
181
152
    xmlXPathRegisterNs(xpctxt, BAD_CAST "saxon", SAXON_NAMESPACE);
182
152
    xmlXPathRegisterNs(xpctxt, BAD_CAST "set", EXSLT_SETS_NAMESPACE);
183
152
    xmlXPathRegisterNs(xpctxt, BAD_CAST "str", EXSLT_STRINGS_NAMESPACE);
184
185
    /* Register variables */
186
152
    xmlXPathRegisterVariable(xpctxt, BAD_CAST "f", xmlXPathNewFloat(-1.5));
187
152
    xmlXPathRegisterVariable(xpctxt, BAD_CAST "b", xmlXPathNewBoolean(1));
188
152
    xmlXPathRegisterVariable(xpctxt, BAD_CAST "s",
189
152
                             xmlXPathNewString(BAD_CAST "var"));
190
152
    xmlXPathRegisterVariable(
191
152
            xpctxt, BAD_CAST "n",
192
152
            xmlXPathEval(BAD_CAST "//node() | /*/*/namespace::*", xpctxt));
193
194
    /* Compile and return early if the expression is invalid */
195
152
    xmlXPathCompExprPtr compExpr = xmlXPathCtxtCompile(xpctxt,
196
152
            (const xmlChar *) xpathExpr);
197
152
    if (compExpr == NULL)
198
58
        goto error;
199
200
    /* Initialize XPath evaluation context and evaluate */
201
94
    xmlFuzzMemSetLimit(maxAllocs);
202
    /* Maybe test different context nodes? */
203
94
    xpctxt->node = (xmlNodePtr) doc;
204
94
    xpctxt->contextSize = 1;
205
94
    xpctxt->proximityPosition = 1;
206
94
    xpctxt->opCount = 0;
207
94
    xpathObj = xmlXPathCompiledEval(compExpr, xpctxt);
208
94
    xmlXPathFreeCompExpr(compExpr);
209
210
160
error:
211
160
    xmlFuzzMemSetLimit(0);
212
160
    xmlXPathRegisteredNsCleanup(xpctxt);
213
160
    xmlFuzzDataCleanup();
214
215
160
    return xpathObj;
216
94
}
217
218
void
219
160
xsltFuzzXPathFreeObject(xmlXPathObjectPtr obj) {
220
160
    xmlXPathFreeObject(obj);
221
222
160
    if (tctxt != NULL) {
223
152
        xmlDocPtr doc = tctxt->document->doc;
224
225
152
        xsltFreeTransformContext(tctxt);
226
152
        tctxt = NULL;
227
152
        xmlFreeDoc(doc);
228
152
    }
229
160
}
230
231
void
232
0
xsltFuzzXPathCleanup(void) {
233
0
    xsltFreeSecurityPrefs(globalSec);
234
0
    globalSec = NULL;
235
0
    xsltFreeStylesheet(globalStyle);
236
0
    globalStyle = NULL;
237
0
}
238
239
/*
240
 * XSLT fuzzer
241
 *
242
 * This is a rather naive fuzz target using a static XML document.
243
 *
244
 * TODO
245
 *
246
 * - Improve seed corpus
247
 * - Mutate multiple input documents: source, xsl:import, xsl:include
248
 * - format-number() with xsl:decimal-format
249
 * - Better coverage for xsl:key and key() function
250
 * - EXSLT func:function
251
 * - xsl:document
252
 */
253
254
int
255
2
xsltFuzzXsltInit(void) {
256
2
    xsltFuzzInit();
257
2
    xmlSetExternalEntityLoader(xmlFuzzEntityLoader);
258
2
    return(0);
259
2
}
260
261
xmlChar *
262
34.0k
xsltFuzzXslt(const char *data, size_t size) {
263
34.0k
    const char *xsltBuffer, *xsltUrl, *docBuffer, *docUrl;
264
34.0k
    xmlDocPtr xsltDoc = NULL, doc = NULL;
265
34.0k
    xmlDocPtr result = NULL;
266
34.0k
    xmlNodePtr root;
267
34.0k
    xsltStylesheetPtr sheet = NULL;
268
34.0k
    xsltTransformContextPtr ctxt = NULL;
269
34.0k
    xmlChar *ret = NULL;
270
34.0k
    size_t xsltSize, docSize, maxAllocs;
271
34.0k
    int retLen;
272
273
34.0k
    xmlFuzzDataInit(data, size);
274
34.0k
    maxAllocs = xmlFuzzReadInt(4) % (size + 1);
275
276
34.0k
    xmlFuzzReadEntities();
277
34.0k
    xsltBuffer = xmlFuzzMainEntity(&xsltSize);
278
34.0k
    xsltUrl = xmlFuzzMainUrl();
279
34.0k
    docBuffer = xmlFuzzSecondaryEntity(&docSize);
280
34.0k
    docUrl = xmlFuzzSecondaryUrl();
281
34.0k
    if ((xsltBuffer == NULL) || (docBuffer == NULL))
282
135
        goto exit;
283
284
33.8k
    doc = xmlReadMemory(docBuffer, docSize, docUrl, NULL, XSLT_PARSE_OPTIONS);
285
33.8k
    if (doc == NULL)
286
12.7k
        goto exit;
287
288
21.1k
    xsltDoc = xmlReadMemory(xsltBuffer, xsltSize, xsltUrl, NULL,
289
21.1k
                            XSLT_PARSE_OPTIONS);
290
21.1k
    if (xsltDoc == NULL)
291
221
        goto exit;
292
20.9k
    root = xmlDocGetRootElement(xsltDoc);
293
20.9k
    if (root != NULL) {
294
20.9k
        xmlNewNs(root, XSLT_NAMESPACE, BAD_CAST "x");
295
20.9k
        xmlNewNs(root, EXSLT_COMMON_NAMESPACE, BAD_CAST "exsl");
296
20.9k
        xmlNewNs(root, EXSLT_COMMON_NAMESPACE, BAD_CAST "exslt");
297
20.9k
        xmlNewNs(root, EXSLT_CRYPTO_NAMESPACE, BAD_CAST "crypto");
298
20.9k
        xmlNewNs(root, EXSLT_DATE_NAMESPACE, BAD_CAST "date");
299
20.9k
        xmlNewNs(root, EXSLT_DYNAMIC_NAMESPACE, BAD_CAST "dyn");
300
20.9k
        xmlNewNs(root, EXSLT_MATH_NAMESPACE, BAD_CAST "math");
301
20.9k
        xmlNewNs(root, EXSLT_SETS_NAMESPACE, BAD_CAST "set");
302
20.9k
        xmlNewNs(root, EXSLT_STRINGS_NAMESPACE, BAD_CAST "str");
303
20.9k
        xmlNewNs(root, SAXON_NAMESPACE, BAD_CAST "saxon");
304
20.9k
    }
305
306
20.9k
    xmlFuzzMemSetLimit(maxAllocs);
307
20.9k
    sheet = xsltNewStylesheet();
308
20.9k
    if (sheet == NULL)
309
19
        goto exit;
310
20.8k
    sheet->xpathCtxt->opLimit = 100000;
311
20.8k
    sheet->xpathCtxt->opCount = 0;
312
20.8k
    if (xsltParseStylesheetUser(sheet, xsltDoc) != 0)
313
8.63k
        goto exit;
314
12.2k
    xsltDoc = NULL;
315
316
12.2k
    root = xmlDocGetRootElement(doc);
317
12.2k
    if (root != NULL) {
318
12.2k
        xmlNewNs(root, BAD_CAST "a", BAD_CAST "a");
319
12.2k
        xmlNewNs(root, BAD_CAST "b", BAD_CAST "b");
320
12.2k
        xmlNewNs(root, BAD_CAST "c", BAD_CAST "c");
321
12.2k
    }
322
323
12.2k
    ctxt = xsltNewTransformContext(sheet, doc);
324
12.2k
    if (ctxt == NULL)
325
120
        goto exit;
326
12.1k
    xsltSetCtxtSecurityPrefs(globalSec, ctxt);
327
12.1k
    ctxt->maxTemplateDepth = 100;
328
12.1k
    ctxt->opLimit = 20000;
329
12.1k
    ctxt->xpathCtxt->opLimit = 100000;
330
12.1k
    ctxt->xpathCtxt->opCount = sheet->xpathCtxt->opCount;
331
332
12.1k
    result = xsltApplyStylesheetUser(sheet, doc, NULL, NULL, NULL, ctxt);
333
12.1k
    if (result != NULL)
334
5.44k
        xsltSaveResultToString(&ret, &retLen, result, sheet);
335
336
34.0k
exit:
337
34.0k
    xmlFuzzMemSetLimit(0);
338
34.0k
    xmlFreeDoc(result);
339
34.0k
    xsltFreeTransformContext(ctxt);
340
34.0k
    xsltFreeStylesheet(sheet);
341
34.0k
    xmlFreeDoc(xsltDoc);
342
34.0k
    xmlFreeDoc(doc);
343
34.0k
    xmlFuzzDataCleanup();
344
345
34.0k
    return ret;
346
12.1k
}
347
348
void
349
0
xsltFuzzXsltCleanup(void) {
350
0
    xsltFreeSecurityPrefs(globalSec);
351
0
    globalSec = NULL;
352
0
}
353
354
/*
355
 * Utility functions, copied from libxml2
356
 */
357
358
typedef struct {
359
    const char *data;
360
    size_t size;
361
} xmlFuzzEntityInfo;
362
363
/* Single static instance for now */
364
static struct {
365
    /* Original data */
366
    const char *data;
367
    size_t size;
368
369
    /* Remaining data */
370
    const char *ptr;
371
    size_t remaining;
372
373
    /* Buffer for unescaped strings */
374
    char *outBuf;
375
    char *outPtr; /* Free space at end of buffer */
376
377
    xmlHashTablePtr entities; /* Maps URLs to xmlFuzzEntityInfos */
378
379
    /* The first entity is the main entity. */
380
    const char *mainUrl;
381
    xmlFuzzEntityInfo *mainEntity;
382
    const char *secondaryUrl;
383
    xmlFuzzEntityInfo *secondaryEntity;
384
} fuzzData;
385
386
size_t fuzzNumAllocs;
387
size_t fuzzMaxAllocs;
388
389
/**
390
 * xmlFuzzErrorFunc:
391
 *
392
 * An error function that simply discards all errors.
393
 */
394
void
395
xmlFuzzErrorFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
396
0
                 ...) {
397
0
}
398
399
/*
400
 * Malloc failure injection.
401
 *
402
 * Quick tip to debug complicated issues: Increase MALLOC_OFFSET until
403
 * the crash disappears (or a different issue is triggered). Then set
404
 * the offset to the highest value that produces a crash and set
405
 * MALLOC_ABORT to 1 to see which failed memory allocation causes the
406
 * issue.
407
 */
408
409
17.1k
#define XML_FUZZ_MALLOC_OFFSET  0
410
#define XML_FUZZ_MALLOC_ABORT   0
411
412
static void *
413
116M
xmlFuzzMalloc(size_t size) {
414
116M
    if (fuzzMaxAllocs > 0) {
415
50.5M
        if (fuzzNumAllocs >= fuzzMaxAllocs - 1)
416
#if XML_FUZZ_MALLOC_ABORT
417
            abort();
418
#else
419
675k
            return(NULL);
420
49.8M
#endif
421
49.8M
        fuzzNumAllocs += 1;
422
49.8M
    }
423
116M
    return malloc(size);
424
116M
}
425
426
static void *
427
6.18M
xmlFuzzRealloc(void *ptr, size_t size) {
428
6.18M
    if (fuzzMaxAllocs > 0) {
429
3.70M
        if (fuzzNumAllocs >= fuzzMaxAllocs - 1)
430
#if XML_FUZZ_MALLOC_ABORT
431
            abort();
432
#else
433
22.6k
            return(NULL);
434
3.67M
#endif
435
3.67M
        fuzzNumAllocs += 1;
436
3.67M
    }
437
6.15M
    return realloc(ptr, size);
438
6.18M
}
439
440
void
441
4
xmlFuzzMemSetup(void) {
442
4
    xmlMemSetup(free, xmlFuzzMalloc, xmlFuzzRealloc, xmlMemStrdup);
443
4
}
444
445
void
446
55.1k
xmlFuzzMemSetLimit(size_t limit) {
447
55.1k
    fuzzNumAllocs = 0;
448
55.1k
    fuzzMaxAllocs = limit ? limit + XML_FUZZ_MALLOC_OFFSET : 0;
449
55.1k
}
450
451
/**
452
 * xmlFuzzDataInit:
453
 *
454
 * Initialize fuzz data provider.
455
 */
456
void
457
34.1k
xmlFuzzDataInit(const char *data, size_t size) {
458
34.1k
    fuzzData.data = data;
459
34.1k
    fuzzData.size = size;
460
34.1k
    fuzzData.ptr = data;
461
34.1k
    fuzzData.remaining = size;
462
463
34.1k
    fuzzData.outBuf = xmlMalloc(size + 1);
464
34.1k
    fuzzData.outPtr = fuzzData.outBuf;
465
466
34.1k
    fuzzData.entities = xmlHashCreate(8);
467
34.1k
    fuzzData.mainUrl = NULL;
468
34.1k
    fuzzData.mainEntity = NULL;
469
34.1k
    fuzzData.secondaryUrl = NULL;
470
34.1k
    fuzzData.secondaryEntity = NULL;
471
34.1k
}
472
473
/**
474
 * xmlFuzzDataFree:
475
 *
476
 * Cleanup fuzz data provider.
477
 */
478
void
479
34.1k
xmlFuzzDataCleanup(void) {
480
34.1k
    xmlFree(fuzzData.outBuf);
481
34.1k
    xmlHashFree(fuzzData.entities, xmlHashDefaultDeallocator);
482
34.1k
}
483
484
/**
485
 * xmlFuzzWriteInt:
486
 * @out:  output file
487
 * @v:  integer to write
488
 * @size:  size of integer in bytes
489
 *
490
 * Write an integer to the fuzz data.
491
 */
492
void
493
0
xmlFuzzWriteInt(FILE *out, size_t v, int size) {
494
0
    int shift;
495
496
0
    while (size > (int) sizeof(size_t)) {
497
0
        putc(0, out);
498
0
        size--;
499
0
    }
500
501
0
    shift = size * 8;
502
0
    while (shift > 0) {
503
0
        shift -= 8;
504
0
        putc((v >> shift) & 255, out);
505
0
    }
506
0
}
507
508
/**
509
 * xmlFuzzReadInt:
510
 * @size:  size of integer in bytes
511
 *
512
 * Read an integer from the fuzz data.
513
 */
514
size_t
515
34.1k
xmlFuzzReadInt(int size) {
516
34.1k
    size_t ret = 0;
517
518
170k
    while ((size > 0) && (fuzzData.remaining > 0)) {
519
136k
        unsigned char c = (unsigned char) *fuzzData.ptr++;
520
136k
        fuzzData.remaining--;
521
136k
        ret = (ret << 8) | c;
522
136k
        size--;
523
136k
    }
524
525
34.1k
    return ret;
526
34.1k
}
527
528
/**
529
 * xmlFuzzReadRemaining:
530
 * @size:  size of string in bytes
531
 *
532
 * Read remaining bytes from fuzz data.
533
 */
534
const char *
535
0
xmlFuzzReadRemaining(size_t *size) {
536
0
    const char *ret = fuzzData.ptr;
537
538
0
    *size = fuzzData.remaining;
539
0
    fuzzData.ptr += fuzzData.remaining;
540
0
    fuzzData.remaining = 0;
541
542
0
    return(ret);
543
0
}
544
545
/*
546
 * xmlFuzzWriteString:
547
 * @out:  output file
548
 * @str:  string to write
549
 *
550
 * Write a random-length string to file in a format similar to
551
 * FuzzedDataProvider. Backslash followed by newline marks the end of the
552
 * string. Two backslashes are used to escape a backslash.
553
 */
554
void
555
0
xmlFuzzWriteString(FILE *out, const char *str) {
556
0
    for (; *str; str++) {
557
0
        int c = (unsigned char) *str;
558
0
        putc(c, out);
559
0
        if (c == '\\')
560
0
            putc(c, out);
561
0
    }
562
0
    putc('\\', out);
563
0
    putc('\n', out);
564
0
}
565
566
/**
567
 * xmlFuzzReadString:
568
 * @size:  size of string in bytes
569
 *
570
 * Read a random-length string from the fuzz data.
571
 *
572
 * The format is similar to libFuzzer's FuzzedDataProvider but treats
573
 * backslash followed by newline as end of string. This makes the fuzz data
574
 * more readable. A backslash character is escaped with another backslash.
575
 *
576
 * Returns a zero-terminated string or NULL if the fuzz data is exhausted.
577
 */
578
const char *
579
267k
xmlFuzzReadString(size_t *size) {
580
267k
    const char *out = fuzzData.outPtr;
581
582
384M
    while (fuzzData.remaining > 0) {
583
384M
        int c = *fuzzData.ptr++;
584
384M
        fuzzData.remaining--;
585
586
384M
        if ((c == '\\') && (fuzzData.remaining > 0)) {
587
403k
            int c2 = *fuzzData.ptr;
588
589
403k
            if (c2 == '\n') {
590
207k
                fuzzData.ptr++;
591
207k
                fuzzData.remaining--;
592
207k
                if (size != NULL)
593
94.5k
                    *size = fuzzData.outPtr - out;
594
207k
                *fuzzData.outPtr++ = '\0';
595
207k
                return(out);
596
207k
            }
597
196k
            if (c2 == '\\') {
598
98.3k
                fuzzData.ptr++;
599
98.3k
                fuzzData.remaining--;
600
98.3k
            }
601
196k
        }
602
603
384M
        *fuzzData.outPtr++ = c;
604
384M
    }
605
606
60.0k
    if (fuzzData.outPtr > out) {
607
26.0k
        if (size != NULL)
608
18.4k
            *size = fuzzData.outPtr - out;
609
26.0k
        *fuzzData.outPtr++ = '\0';
610
26.0k
        return(out);
611
26.0k
    }
612
613
34.0k
    if (size != NULL)
614
7.82k
        *size = 0;
615
34.0k
    return(NULL);
616
60.0k
}
617
618
/**
619
 * xmlFuzzReadEntities:
620
 *
621
 * Read entities like the main XML file, external DTDs, external parsed
622
 * entities from fuzz data.
623
 */
624
void
625
34.0k
xmlFuzzReadEntities(void) {
626
34.0k
    size_t num = 0;
627
628
146k
    while (1) {
629
146k
        const char *url, *entity;
630
146k
        size_t entitySize;
631
146k
        xmlFuzzEntityInfo *entityInfo;
632
633
146k
        url = xmlFuzzReadString(NULL);
634
146k
        if (url == NULL) break;
635
636
120k
        entity = xmlFuzzReadString(&entitySize);
637
120k
        if (entity == NULL) break;
638
639
112k
        if (xmlHashLookup(fuzzData.entities, (xmlChar *)url) == NULL) {
640
105k
            entityInfo = xmlMalloc(sizeof(xmlFuzzEntityInfo));
641
105k
            if (entityInfo == NULL)
642
0
                break;
643
105k
            entityInfo->data = entity;
644
105k
            entityInfo->size = entitySize;
645
646
105k
            xmlHashAddEntry(fuzzData.entities, (xmlChar *)url, entityInfo);
647
648
105k
            if (num == 0) {
649
33.9k
                fuzzData.mainUrl = url;
650
33.9k
                fuzzData.mainEntity = entityInfo;
651
71.6k
            } else if (num == 1) {
652
33.8k
                fuzzData.secondaryUrl = url;
653
33.8k
                fuzzData.secondaryEntity = entityInfo;
654
33.8k
            }
655
656
105k
            num++;
657
105k
        }
658
112k
    }
659
34.0k
}
660
661
/**
662
 * xmlFuzzMainUrl:
663
 *
664
 * Returns the main URL.
665
 */
666
const char *
667
34.0k
xmlFuzzMainUrl(void) {
668
34.0k
    return(fuzzData.mainUrl);
669
34.0k
}
670
671
/**
672
 * xmlFuzzMainEntity:
673
 * @size:  size of the main entity in bytes
674
 *
675
 * Returns the main entity.
676
 */
677
const char *
678
34.0k
xmlFuzzMainEntity(size_t *size) {
679
34.0k
    if (fuzzData.mainEntity == NULL)
680
71
        return(NULL);
681
33.9k
    *size = fuzzData.mainEntity->size;
682
33.9k
    return(fuzzData.mainEntity->data);
683
34.0k
}
684
685
/**
686
 * xmlFuzzSecondaryUrl:
687
 *
688
 * Returns the secondary URL.
689
 */
690
const char *
691
34.0k
xmlFuzzSecondaryUrl(void) {
692
34.0k
    return(fuzzData.secondaryUrl);
693
34.0k
}
694
695
/**
696
 * xmlFuzzSecondaryEntity:
697
 * @size:  size of the secondary entity in bytes
698
 *
699
 * Returns the secondary entity.
700
 */
701
const char *
702
34.0k
xmlFuzzSecondaryEntity(size_t *size) {
703
34.0k
    if (fuzzData.secondaryEntity == NULL)
704
135
        return(NULL);
705
33.8k
    *size = fuzzData.secondaryEntity->size;
706
33.8k
    return(fuzzData.secondaryEntity->data);
707
34.0k
}
708
709
/**
710
 * xmlFuzzEntityLoader:
711
 *
712
 * The entity loader for fuzz data.
713
 */
714
xmlParserInputPtr
715
xmlFuzzEntityLoader(const char *URL, const char *ID ATTRIBUTE_UNUSED,
716
340k
                    xmlParserCtxtPtr ctxt) {
717
340k
    xmlParserInputPtr input;
718
340k
    xmlFuzzEntityInfo *entity;
719
720
340k
    if (URL == NULL)
721
3.48k
        return(NULL);
722
336k
    entity = xmlHashLookup(fuzzData.entities, (xmlChar *) URL);
723
336k
    if (entity == NULL)
724
178k
        return(NULL);
725
726
157k
    input = xmlNewInputStream(ctxt);
727
157k
    if (input == NULL)
728
12
        return(NULL);
729
157k
    input->filename = (char *) xmlCharStrdup(URL);
730
157k
    input->buf = xmlParserInputBufferCreateMem(entity->data, entity->size,
731
157k
                                               XML_CHAR_ENCODING_NONE);
732
157k
    if (input->buf == NULL) {
733
46
        xmlFreeInputStream(input);
734
46
        return(NULL);
735
46
    }
736
157k
    input->base = input->cur = xmlBufContent(input->buf->buffer);
737
157k
    input->end = input->base + entity->size;
738
739
157k
    return input;
740
157k
}