Coverage Report

Created: 2023-06-07 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
10.4M
xsltFuzzXmlErrorFunc(void *vctxt, const char *msg ATTRIBUTE_UNUSED, ...) {
38
10.4M
    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
10.4M
    xmlStopParser(ctxt);
44
10.4M
}
45
46
static void
47
xsltFuzzXsltErrorFunc(void *vctxt ATTRIBUTE_UNUSED,
48
5.33M
                      const char *msg ATTRIBUTE_UNUSED, ...) {
49
5.33M
}
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
410
xsltFuzzXPath(const char *data, size_t size) {
122
410
    xmlXPathContextPtr xpctxt = NULL;
123
410
    xmlXPathObjectPtr xpathObj = NULL;
124
410
    xmlDocPtr doc;
125
410
    xmlNodePtr root;
126
410
    const char *xpathExpr, *xml;
127
410
    size_t maxAllocs, xmlSize;
128
129
410
    xmlFuzzDataInit(data, size);
130
131
410
    maxAllocs = xmlFuzzReadInt(4) % (size + 1);
132
410
    xpathExpr = xmlFuzzReadString(NULL);
133
410
    xml = xmlFuzzReadString(&xmlSize);
134
135
    /* Recovery mode allows more input to be fuzzed. */
136
410
    doc = xmlReadMemory(xml, xmlSize, NULL, NULL, XML_PARSE_RECOVER);
137
410
    if (doc == NULL)
138
11
        goto error;
139
399
    root = xmlDocGetRootElement(doc);
140
399
    if (root != NULL) {
141
342
        xmlNewNs(root, BAD_CAST "a", BAD_CAST "a");
142
342
        xmlNewNs(root, BAD_CAST "b", BAD_CAST "b");
143
342
        xmlNewNs(root, BAD_CAST "c", BAD_CAST "c");
144
342
    }
145
146
399
    tctxt = xsltNewTransformContext(globalStyle, doc);
147
399
    if (tctxt == NULL) {
148
0
        xmlFreeDoc(doc);
149
0
        goto error;
150
0
    }
151
399
    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
399
    tctxt->inst = xmlDocGetRootElement(doc);
163
164
    /* Set up XPath context */
165
399
    xpctxt = tctxt->xpathCtxt;
166
167
    /* Resource limits to avoid timeouts and call stack overflows */
168
399
    xpctxt->opLimit = 500000;
169
170
    /* Test namespaces */
171
399
    xmlXPathRegisterNs(xpctxt, BAD_CAST "a", BAD_CAST "a");
172
399
    xmlXPathRegisterNs(xpctxt, BAD_CAST "b", BAD_CAST "b");
173
399
    xmlXPathRegisterNs(xpctxt, BAD_CAST "c", BAD_CAST "c");
174
175
    /* EXSLT namespaces */
176
399
    xmlXPathRegisterNs(xpctxt, BAD_CAST "crypto", EXSLT_CRYPTO_NAMESPACE);
177
399
    xmlXPathRegisterNs(xpctxt, BAD_CAST "date", EXSLT_DATE_NAMESPACE);
178
399
    xmlXPathRegisterNs(xpctxt, BAD_CAST "dyn", EXSLT_DYNAMIC_NAMESPACE);
179
399
    xmlXPathRegisterNs(xpctxt, BAD_CAST "exsl", EXSLT_COMMON_NAMESPACE);
180
399
    xmlXPathRegisterNs(xpctxt, BAD_CAST "math", EXSLT_MATH_NAMESPACE);
181
399
    xmlXPathRegisterNs(xpctxt, BAD_CAST "saxon", SAXON_NAMESPACE);
182
399
    xmlXPathRegisterNs(xpctxt, BAD_CAST "set", EXSLT_SETS_NAMESPACE);
183
399
    xmlXPathRegisterNs(xpctxt, BAD_CAST "str", EXSLT_STRINGS_NAMESPACE);
184
185
    /* Register variables */
186
399
    xmlXPathRegisterVariable(xpctxt, BAD_CAST "f", xmlXPathNewFloat(-1.5));
187
399
    xmlXPathRegisterVariable(xpctxt, BAD_CAST "b", xmlXPathNewBoolean(1));
188
399
    xmlXPathRegisterVariable(xpctxt, BAD_CAST "s",
189
399
                             xmlXPathNewString(BAD_CAST "var"));
190
399
    xmlXPathRegisterVariable(
191
399
            xpctxt, BAD_CAST "n",
192
399
            xmlXPathEval(BAD_CAST "//node() | /*/*/namespace::*", xpctxt));
193
194
    /* Compile and return early if the expression is invalid */
195
399
    xmlXPathCompExprPtr compExpr = xmlXPathCtxtCompile(xpctxt,
196
399
            (const xmlChar *) xpathExpr);
197
399
    if (compExpr == NULL)
198
177
        goto error;
199
200
    /* Initialize XPath evaluation context and evaluate */
201
222
    xmlFuzzMemSetLimit(maxAllocs);
202
    /* Maybe test different context nodes? */
203
222
    xpctxt->node = (xmlNodePtr) doc;
204
222
    xpctxt->contextSize = 1;
205
222
    xpctxt->proximityPosition = 1;
206
222
    xpctxt->opCount = 0;
207
222
    xpathObj = xmlXPathCompiledEval(compExpr, xpctxt);
208
222
    xmlXPathFreeCompExpr(compExpr);
209
210
410
error:
211
410
    xmlFuzzMemSetLimit(0);
212
410
    xmlXPathRegisteredNsCleanup(xpctxt);
213
410
    xmlFuzzDataCleanup();
214
215
410
    return xpathObj;
216
222
}
217
218
void
219
410
xsltFuzzXPathFreeObject(xmlXPathObjectPtr obj) {
220
410
    xmlXPathFreeObject(obj);
221
222
410
    if (tctxt != NULL) {
223
399
        xmlDocPtr doc = tctxt->document->doc;
224
225
399
        xsltFreeTransformContext(tctxt);
226
399
        tctxt = NULL;
227
399
        xmlFreeDoc(doc);
228
399
    }
229
410
}
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
35.8k
xsltFuzzXslt(const char *data, size_t size) {
263
35.8k
    const char *xsltBuffer, *xsltUrl, *docBuffer, *docUrl;
264
35.8k
    xmlDocPtr xsltDoc = NULL, doc = NULL;
265
35.8k
    xmlDocPtr result = NULL;
266
35.8k
    xmlNodePtr root;
267
35.8k
    xsltStylesheetPtr sheet = NULL;
268
35.8k
    xsltTransformContextPtr ctxt = NULL;
269
35.8k
    xmlChar *ret = NULL;
270
35.8k
    size_t xsltSize, docSize, maxAllocs;
271
35.8k
    int retLen;
272
273
35.8k
    xmlFuzzDataInit(data, size);
274
35.8k
    maxAllocs = xmlFuzzReadInt(4) % (size + 1);
275
276
35.8k
    xmlFuzzReadEntities();
277
35.8k
    xsltBuffer = xmlFuzzMainEntity(&xsltSize);
278
35.8k
    xsltUrl = xmlFuzzMainUrl();
279
35.8k
    docBuffer = xmlFuzzSecondaryEntity(&docSize);
280
35.8k
    docUrl = xmlFuzzSecondaryUrl();
281
35.8k
    if ((xsltBuffer == NULL) || (docBuffer == NULL))
282
143
        goto exit;
283
284
35.7k
    doc = xmlReadMemory(docBuffer, docSize, docUrl, NULL, XSLT_PARSE_OPTIONS);
285
35.7k
    if (doc == NULL)
286
12.2k
        goto exit;
287
288
23.4k
    xsltDoc = xmlReadMemory(xsltBuffer, xsltSize, xsltUrl, NULL,
289
23.4k
                            XSLT_PARSE_OPTIONS);
290
23.4k
    if (xsltDoc == NULL)
291
293
        goto exit;
292
23.1k
    root = xmlDocGetRootElement(xsltDoc);
293
23.1k
    if (root != NULL) {
294
23.1k
        xmlNewNs(root, XSLT_NAMESPACE, BAD_CAST "x");
295
23.1k
        xmlNewNs(root, EXSLT_COMMON_NAMESPACE, BAD_CAST "exsl");
296
23.1k
        xmlNewNs(root, EXSLT_COMMON_NAMESPACE, BAD_CAST "exslt");
297
23.1k
        xmlNewNs(root, EXSLT_CRYPTO_NAMESPACE, BAD_CAST "crypto");
298
23.1k
        xmlNewNs(root, EXSLT_DATE_NAMESPACE, BAD_CAST "date");
299
23.1k
        xmlNewNs(root, EXSLT_DYNAMIC_NAMESPACE, BAD_CAST "dyn");
300
23.1k
        xmlNewNs(root, EXSLT_MATH_NAMESPACE, BAD_CAST "math");
301
23.1k
        xmlNewNs(root, EXSLT_SETS_NAMESPACE, BAD_CAST "set");
302
23.1k
        xmlNewNs(root, EXSLT_STRINGS_NAMESPACE, BAD_CAST "str");
303
23.1k
        xmlNewNs(root, SAXON_NAMESPACE, BAD_CAST "saxon");
304
23.1k
    }
305
306
23.1k
    xmlFuzzMemSetLimit(maxAllocs);
307
23.1k
    sheet = xsltNewStylesheet();
308
23.1k
    if (sheet == NULL)
309
21
        goto exit;
310
23.1k
    sheet->xpathCtxt->opLimit = 100000;
311
23.1k
    sheet->xpathCtxt->opCount = 0;
312
23.1k
    if (xsltParseStylesheetUser(sheet, xsltDoc) != 0)
313
8.96k
        goto exit;
314
14.1k
    xsltDoc = NULL;
315
316
14.1k
    root = xmlDocGetRootElement(doc);
317
14.1k
    if (root != NULL) {
318
14.1k
        xmlNewNs(root, BAD_CAST "a", BAD_CAST "a");
319
14.1k
        xmlNewNs(root, BAD_CAST "b", BAD_CAST "b");
320
14.1k
        xmlNewNs(root, BAD_CAST "c", BAD_CAST "c");
321
14.1k
    }
322
323
14.1k
    ctxt = xsltNewTransformContext(sheet, doc);
324
14.1k
    if (ctxt == NULL)
325
120
        goto exit;
326
14.0k
    xsltSetCtxtSecurityPrefs(globalSec, ctxt);
327
14.0k
    ctxt->maxTemplateDepth = 100;
328
14.0k
    ctxt->opLimit = 20000;
329
14.0k
    ctxt->xpathCtxt->opLimit = 100000;
330
14.0k
    ctxt->xpathCtxt->opCount = sheet->xpathCtxt->opCount;
331
332
14.0k
    result = xsltApplyStylesheetUser(sheet, doc, NULL, NULL, NULL, ctxt);
333
14.0k
    if (result != NULL)
334
5.88k
        xsltSaveResultToString(&ret, &retLen, result, sheet);
335
336
35.8k
exit:
337
35.8k
    xmlFuzzMemSetLimit(0);
338
35.8k
    xmlFreeDoc(result);
339
35.8k
    xsltFreeTransformContext(ctxt);
340
35.8k
    xsltFreeStylesheet(sheet);
341
35.8k
    xmlFreeDoc(xsltDoc);
342
35.8k
    xmlFreeDoc(doc);
343
35.8k
    xmlFuzzDataCleanup();
344
345
35.8k
    return ret;
346
14.0k
}
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
18.7k
#define XML_FUZZ_MALLOC_OFFSET  0
410
#define XML_FUZZ_MALLOC_ABORT   0
411
412
static void *
413
161M
xmlFuzzMalloc(size_t size) {
414
161M
    if (fuzzMaxAllocs > 0) {
415
45.8M
        if (fuzzNumAllocs >= fuzzMaxAllocs - 1)
416
#if XML_FUZZ_MALLOC_ABORT
417
            abort();
418
#else
419
1.07M
            return(NULL);
420
44.7M
#endif
421
44.7M
        fuzzNumAllocs += 1;
422
44.7M
    }
423
160M
    return malloc(size);
424
161M
}
425
426
static void *
427
9.13M
xmlFuzzRealloc(void *ptr, size_t size) {
428
9.13M
    if (fuzzMaxAllocs > 0) {
429
3.70M
        if (fuzzNumAllocs >= fuzzMaxAllocs - 1)
430
#if XML_FUZZ_MALLOC_ABORT
431
            abort();
432
#else
433
78.8k
            return(NULL);
434
3.62M
#endif
435
3.62M
        fuzzNumAllocs += 1;
436
3.62M
    }
437
9.05M
    return realloc(ptr, size);
438
9.13M
}
439
440
void
441
4
xmlFuzzMemSetup(void) {
442
4
    xmlMemSetup(free, xmlFuzzMalloc, xmlFuzzRealloc, xmlMemStrdup);
443
4
}
444
445
void
446
59.6k
xmlFuzzMemSetLimit(size_t limit) {
447
59.6k
    fuzzNumAllocs = 0;
448
59.6k
    fuzzMaxAllocs = limit ? limit + XML_FUZZ_MALLOC_OFFSET : 0;
449
59.6k
}
450
451
/**
452
 * xmlFuzzDataInit:
453
 *
454
 * Initialize fuzz data provider.
455
 */
456
void
457
36.3k
xmlFuzzDataInit(const char *data, size_t size) {
458
36.3k
    fuzzData.data = data;
459
36.3k
    fuzzData.size = size;
460
36.3k
    fuzzData.ptr = data;
461
36.3k
    fuzzData.remaining = size;
462
463
36.3k
    fuzzData.outBuf = xmlMalloc(size + 1);
464
36.3k
    fuzzData.outPtr = fuzzData.outBuf;
465
466
36.3k
    fuzzData.entities = xmlHashCreate(8);
467
36.3k
    fuzzData.mainUrl = NULL;
468
36.3k
    fuzzData.mainEntity = NULL;
469
36.3k
    fuzzData.secondaryUrl = NULL;
470
36.3k
    fuzzData.secondaryEntity = NULL;
471
36.3k
}
472
473
/**
474
 * xmlFuzzDataFree:
475
 *
476
 * Cleanup fuzz data provider.
477
 */
478
void
479
36.3k
xmlFuzzDataCleanup(void) {
480
36.3k
    xmlFree(fuzzData.outBuf);
481
36.3k
    xmlHashFree(fuzzData.entities, xmlHashDefaultDeallocator);
482
36.3k
}
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
36.3k
xmlFuzzReadInt(int size) {
516
36.3k
    size_t ret = 0;
517
518
181k
    while ((size > 0) && (fuzzData.remaining > 0)) {
519
145k
        unsigned char c = (unsigned char) *fuzzData.ptr++;
520
145k
        fuzzData.remaining--;
521
145k
        ret = (ret << 8) | c;
522
145k
        size--;
523
145k
    }
524
525
36.3k
    return ret;
526
36.3k
}
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
284k
xmlFuzzReadString(size_t *size) {
580
284k
    const char *out = fuzzData.outPtr;
581
582
393M
    while (fuzzData.remaining > 0) {
583
393M
        int c = *fuzzData.ptr++;
584
393M
        fuzzData.remaining--;
585
586
393M
        if ((c == '\\') && (fuzzData.remaining > 0)) {
587
332k
            int c2 = *fuzzData.ptr;
588
589
332k
            if (c2 == '\n') {
590
219k
                fuzzData.ptr++;
591
219k
                fuzzData.remaining--;
592
219k
                if (size != NULL)
593
99.3k
                    *size = fuzzData.outPtr - out;
594
219k
                *fuzzData.outPtr++ = '\0';
595
219k
                return(out);
596
219k
            }
597
113k
            if (c2 == '\\') {
598
56.4k
                fuzzData.ptr++;
599
56.4k
                fuzzData.remaining--;
600
56.4k
            }
601
113k
        }
602
603
393M
        *fuzzData.outPtr++ = c;
604
393M
    }
605
606
65.3k
    if (fuzzData.outPtr > out) {
607
29.4k
        if (size != NULL)
608
20.5k
            *size = fuzzData.outPtr - out;
609
29.4k
        *fuzzData.outPtr++ = '\0';
610
29.4k
        return(out);
611
29.4k
    }
612
613
35.8k
    if (size != NULL)
614
9.14k
        *size = 0;
615
35.8k
    return(NULL);
616
65.3k
}
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
35.8k
xmlFuzzReadEntities(void) {
626
35.8k
    size_t num = 0;
627
628
155k
    while (1) {
629
155k
        const char *url, *entity;
630
155k
        size_t entitySize;
631
155k
        xmlFuzzEntityInfo *entityInfo;
632
633
155k
        url = xmlFuzzReadString(NULL);
634
155k
        if (url == NULL) break;
635
636
128k
        entity = xmlFuzzReadString(&entitySize);
637
128k
        if (entity == NULL) break;
638
639
119k
        if (xmlHashLookup(fuzzData.entities, (xmlChar *)url) == NULL) {
640
108k
            entityInfo = xmlMalloc(sizeof(xmlFuzzEntityInfo));
641
108k
            if (entityInfo == NULL)
642
0
                break;
643
108k
            entityInfo->data = entity;
644
108k
            entityInfo->size = entitySize;
645
646
108k
            xmlHashAddEntry(fuzzData.entities, (xmlChar *)url, entityInfo);
647
648
108k
            if (num == 0) {
649
35.8k
                fuzzData.mainUrl = url;
650
35.8k
                fuzzData.mainEntity = entityInfo;
651
72.8k
            } else if (num == 1) {
652
35.7k
                fuzzData.secondaryUrl = url;
653
35.7k
                fuzzData.secondaryEntity = entityInfo;
654
35.7k
            }
655
656
108k
            num++;
657
108k
        }
658
119k
    }
659
35.8k
}
660
661
/**
662
 * xmlFuzzMainUrl:
663
 *
664
 * Returns the main URL.
665
 */
666
const char *
667
35.8k
xmlFuzzMainUrl(void) {
668
35.8k
    return(fuzzData.mainUrl);
669
35.8k
}
670
671
/**
672
 * xmlFuzzMainEntity:
673
 * @size:  size of the main entity in bytes
674
 *
675
 * Returns the main entity.
676
 */
677
const char *
678
35.8k
xmlFuzzMainEntity(size_t *size) {
679
35.8k
    if (fuzzData.mainEntity == NULL)
680
74
        return(NULL);
681
35.8k
    *size = fuzzData.mainEntity->size;
682
35.8k
    return(fuzzData.mainEntity->data);
683
35.8k
}
684
685
/**
686
 * xmlFuzzSecondaryUrl:
687
 *
688
 * Returns the secondary URL.
689
 */
690
const char *
691
35.8k
xmlFuzzSecondaryUrl(void) {
692
35.8k
    return(fuzzData.secondaryUrl);
693
35.8k
}
694
695
/**
696
 * xmlFuzzSecondaryEntity:
697
 * @size:  size of the secondary entity in bytes
698
 *
699
 * Returns the secondary entity.
700
 */
701
const char *
702
35.8k
xmlFuzzSecondaryEntity(size_t *size) {
703
35.8k
    if (fuzzData.secondaryEntity == NULL)
704
143
        return(NULL);
705
35.7k
    *size = fuzzData.secondaryEntity->size;
706
35.7k
    return(fuzzData.secondaryEntity->data);
707
35.8k
}
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
393k
                    xmlParserCtxtPtr ctxt) {
717
393k
    xmlParserInputPtr input;
718
393k
    xmlFuzzEntityInfo *entity;
719
720
393k
    if (URL == NULL)
721
4.63k
        return(NULL);
722
389k
    entity = xmlHashLookup(fuzzData.entities, (xmlChar *) URL);
723
389k
    if (entity == NULL)
724
85.7k
        return(NULL);
725
726
303k
    input = xmlNewInputStream(ctxt);
727
303k
    if (input == NULL)
728
63
        return(NULL);
729
303k
    input->filename = (char *) xmlCharStrdup(URL);
730
303k
    input->buf = xmlParserInputBufferCreateMem(entity->data, entity->size,
731
303k
                                               XML_CHAR_ENCODING_NONE);
732
303k
    if (input->buf == NULL) {
733
108
        xmlFreeInputStream(input);
734
108
        return(NULL);
735
108
    }
736
303k
    input->base = input->cur = xmlBufContent(input->buf->buffer);
737
303k
    input->end = input->base + entity->size;
738
739
303k
    return input;
740
303k
}