mirror of
				https://gitlab.gnome.org/GNOME/libxml2.git
				synced 2025-10-30 10:45:36 +03:00 
			
		
		
		
	
		
			
				
	
	
		
			357 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			357 lines
		
	
	
		
			7.8 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * fuzz.c: Common functions for fuzzing.
 | |
|  *
 | |
|  * See Copyright for the status of this software.
 | |
|  */
 | |
| 
 | |
| #include <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <sys/stat.h>
 | |
| 
 | |
| #include <libxml/hash.h>
 | |
| #include <libxml/parser.h>
 | |
| #include <libxml/parserInternals.h>
 | |
| #include <libxml/tree.h>
 | |
| #include <libxml/xmlIO.h>
 | |
| #include "fuzz.h"
 | |
| 
 | |
| typedef struct {
 | |
|     const char *data;
 | |
|     size_t size;
 | |
| } xmlFuzzEntityInfo;
 | |
| 
 | |
| /* Single static instance for now */
 | |
| static struct {
 | |
|     /* Original data */
 | |
|     const char *data;
 | |
|     size_t size;
 | |
| 
 | |
|     /* Remaining data */
 | |
|     const char *ptr;
 | |
|     size_t remaining;
 | |
| 
 | |
|     /* Buffer for unescaped strings */
 | |
|     char *outBuf;
 | |
|     char *outPtr; /* Free space at end of buffer */
 | |
| 
 | |
|     xmlHashTablePtr entities; /* Maps URLs to xmlFuzzEntityInfos */
 | |
| 
 | |
|     /* The first entity is the main entity. */
 | |
|     const char *mainUrl;
 | |
|     xmlFuzzEntityInfo *mainEntity;
 | |
| } fuzzData;
 | |
| 
 | |
| /**
 | |
|  * xmlFuzzErrorFunc:
 | |
|  *
 | |
|  * An error function that simply discards all errors.
 | |
|  */
 | |
| void
 | |
| xmlFuzzErrorFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
 | |
|                  ...) {
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFuzzDataInit:
 | |
|  *
 | |
|  * Initialize fuzz data provider.
 | |
|  */
 | |
| void
 | |
| xmlFuzzDataInit(const char *data, size_t size) {
 | |
|     fuzzData.data = data;
 | |
|     fuzzData.size = size;
 | |
|     fuzzData.ptr = data;
 | |
|     fuzzData.remaining = size;
 | |
| 
 | |
|     fuzzData.outBuf = xmlMalloc(size + 1);
 | |
|     fuzzData.outPtr = fuzzData.outBuf;
 | |
| 
 | |
|     fuzzData.entities = xmlHashCreate(8);
 | |
|     fuzzData.mainUrl = NULL;
 | |
|     fuzzData.mainEntity = NULL;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFuzzDataFree:
 | |
|  *
 | |
|  * Cleanup fuzz data provider.
 | |
|  */
 | |
| void
 | |
| xmlFuzzDataCleanup(void) {
 | |
|     xmlFree(fuzzData.outBuf);
 | |
|     xmlHashFree(fuzzData.entities, xmlHashDefaultDeallocator);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFuzzReadInt:
 | |
|  * @size:  size of string in bytes
 | |
|  *
 | |
|  * Read an integer from the fuzz data.
 | |
|  */
 | |
| int
 | |
| xmlFuzzReadInt() {
 | |
|     int ret;
 | |
| 
 | |
|     if (fuzzData.remaining < sizeof(int))
 | |
|         return(0);
 | |
|     memcpy(&ret, fuzzData.ptr, sizeof(int));
 | |
|     fuzzData.ptr += sizeof(int);
 | |
|     fuzzData.remaining -= sizeof(int);
 | |
| 
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFuzzReadRemaining:
 | |
|  * @size:  size of string in bytes
 | |
|  *
 | |
|  * Read remaining bytes from fuzz data.
 | |
|  */
 | |
| const char *
 | |
| xmlFuzzReadRemaining(size_t *size) {
 | |
|     const char *ret = fuzzData.ptr;
 | |
| 
 | |
|     *size = fuzzData.remaining;
 | |
|     fuzzData.ptr += fuzzData.remaining;
 | |
|     fuzzData.remaining = 0;
 | |
| 
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * xmlFuzzWriteString:
 | |
|  * @out:  output file
 | |
|  * @str:  string to write
 | |
|  *
 | |
|  * Write a random-length string to file in a format similar to
 | |
|  * FuzzedDataProvider. Backslash followed by newline marks the end of the
 | |
|  * string. Two backslashes are used to escape a backslash.
 | |
|  */
 | |
| void
 | |
| xmlFuzzWriteString(FILE *out, const char *str) {
 | |
|     for (; *str; str++) {
 | |
|         int c = (unsigned char) *str;
 | |
|         putc(c, out);
 | |
|         if (c == '\\')
 | |
|             putc(c, out);
 | |
|     }
 | |
|     putc('\\', out);
 | |
|     putc('\n', out);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFuzzReadString:
 | |
|  * @size:  size of string in bytes
 | |
|  *
 | |
|  * Read a random-length string from the fuzz data.
 | |
|  *
 | |
|  * The format is similar to libFuzzer's FuzzedDataProvider but treats
 | |
|  * backslash followed by newline as end of string. This makes the fuzz data
 | |
|  * more readable. A backslash character is escaped with another backslash.
 | |
|  *
 | |
|  * Returns a zero-terminated string or NULL if the fuzz data is exhausted.
 | |
|  */
 | |
| const char *
 | |
| xmlFuzzReadString(size_t *size) {
 | |
|     const char *out = fuzzData.outPtr;
 | |
| 
 | |
|     while (fuzzData.remaining > 0) {
 | |
|         int c = *fuzzData.ptr++;
 | |
|         fuzzData.remaining--;
 | |
| 
 | |
|         if ((c == '\\') && (fuzzData.remaining > 0)) {
 | |
|             int c2 = *fuzzData.ptr;
 | |
| 
 | |
|             if (c2 == '\n') {
 | |
|                 fuzzData.ptr++;
 | |
|                 fuzzData.remaining--;
 | |
|                 *size = fuzzData.outPtr - out;
 | |
|                 *fuzzData.outPtr++ = '\0';
 | |
|                 return(out);
 | |
|             }
 | |
|             if (c2 == '\\') {
 | |
|                 fuzzData.ptr++;
 | |
|                 fuzzData.remaining--;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         *fuzzData.outPtr++ = c;
 | |
|     }
 | |
| 
 | |
|     if (fuzzData.outPtr > out) {
 | |
|         *size = fuzzData.outPtr - out;
 | |
|         *fuzzData.outPtr++ = '\0';
 | |
|         return(out);
 | |
|     }
 | |
| 
 | |
|     *size = 0;
 | |
|     return(NULL);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFuzzReadEntities:
 | |
|  *
 | |
|  * Read entities like the main XML file, external DTDs, external parsed
 | |
|  * entities from fuzz data.
 | |
|  */
 | |
| void
 | |
| xmlFuzzReadEntities(void) {
 | |
|     size_t num = 0;
 | |
| 
 | |
|     while (1) {
 | |
|         const char *url, *entity;
 | |
|         size_t urlSize, entitySize;
 | |
|         xmlFuzzEntityInfo *entityInfo;
 | |
|         
 | |
|         url = xmlFuzzReadString(&urlSize);
 | |
|         if (url == NULL) break;
 | |
| 
 | |
|         entity = xmlFuzzReadString(&entitySize);
 | |
|         if (entity == NULL) break;
 | |
| 
 | |
|         if (xmlHashLookup(fuzzData.entities, (xmlChar *)url) == NULL) {
 | |
|             entityInfo = xmlMalloc(sizeof(xmlFuzzEntityInfo));
 | |
|             if (entityInfo == NULL)
 | |
|                 break;
 | |
|             entityInfo->data = entity;
 | |
|             entityInfo->size = entitySize;
 | |
| 
 | |
|             xmlHashAddEntry(fuzzData.entities, (xmlChar *)url, entityInfo);
 | |
| 
 | |
|             if (num == 0) {
 | |
|                 fuzzData.mainUrl = url;
 | |
|                 fuzzData.mainEntity = entityInfo;
 | |
|             }
 | |
| 
 | |
|             num++;
 | |
|         }
 | |
|     }
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFuzzMainUrl:
 | |
|  *
 | |
|  * Returns the main URL.
 | |
|  */
 | |
| const char *
 | |
| xmlFuzzMainUrl(void) {
 | |
|     return(fuzzData.mainUrl);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFuzzMainEntity:
 | |
|  * @size:  size of the main entity in bytes
 | |
|  *
 | |
|  * Returns the main entity.
 | |
|  */
 | |
| const char *
 | |
| xmlFuzzMainEntity(size_t *size) {
 | |
|     if (fuzzData.mainEntity == NULL)
 | |
|         return(NULL);
 | |
|     *size = fuzzData.mainEntity->size;
 | |
|     return(fuzzData.mainEntity->data);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFuzzEntityLoader:
 | |
|  *
 | |
|  * The entity loader for fuzz data.
 | |
|  */
 | |
| xmlParserInputPtr
 | |
| xmlFuzzEntityLoader(const char *URL, const char *ID ATTRIBUTE_UNUSED,
 | |
|                     xmlParserCtxtPtr ctxt) {
 | |
|     xmlParserInputPtr input;
 | |
|     xmlFuzzEntityInfo *entity;
 | |
| 
 | |
|     if (URL == NULL)
 | |
|         return(NULL);
 | |
|     entity = xmlHashLookup(fuzzData.entities, (xmlChar *) URL);
 | |
|     if (entity == NULL)
 | |
|         return(NULL);
 | |
| 
 | |
|     input = xmlNewInputStream(ctxt);
 | |
|     input->filename = NULL;
 | |
|     input->buf = xmlParserInputBufferCreateMem(entity->data, entity->size,
 | |
|                                                XML_CHAR_ENCODING_NONE);
 | |
|     if (input->buf == NULL) {
 | |
|         xmlFreeInputStream(input);
 | |
|         return(NULL);
 | |
|     }
 | |
|     input->base = input->cur = xmlBufContent(input->buf->buffer);
 | |
|     input->end = input->base + entity->size;
 | |
| 
 | |
|     return input;
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * xmlFuzzExtractStrings:
 | |
|  *
 | |
|  * Extract C strings from input data. Use exact-size allocations to detect
 | |
|  * potential memory errors.
 | |
|  */
 | |
| size_t
 | |
| xmlFuzzExtractStrings(const char *data, size_t size, char **strings,
 | |
|                       size_t numStrings) {
 | |
|     const char *start = data;
 | |
|     const char *end = data + size;
 | |
|     size_t i = 0, ret;
 | |
| 
 | |
|     while (i < numStrings) {
 | |
|         size_t strSize = end - start;
 | |
|         const char *zero = memchr(start, 0, strSize);
 | |
| 
 | |
|         if (zero != NULL)
 | |
|             strSize = zero - start;
 | |
| 
 | |
|         strings[i] = xmlMalloc(strSize + 1);
 | |
|         memcpy(strings[i], start, strSize);
 | |
|         strings[i][strSize] = '\0';
 | |
| 
 | |
|         i++;
 | |
|         if (zero != NULL)
 | |
|             start = zero + 1;
 | |
|         else
 | |
|             break;
 | |
|     }
 | |
| 
 | |
|     ret = i;
 | |
| 
 | |
|     while (i < numStrings) {
 | |
|         strings[i] = NULL;
 | |
|         i++;
 | |
|     }
 | |
| 
 | |
|     return(ret);
 | |
| }
 | |
| 
 | |
| char *
 | |
| xmlSlurpFile(const char *path, size_t *sizeRet) {
 | |
|     FILE *file;
 | |
|     struct stat statbuf;
 | |
|     char *data;
 | |
|     size_t size;
 | |
| 
 | |
|     if ((stat(path, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode)))
 | |
|         return(NULL);
 | |
|     size = statbuf.st_size;
 | |
|     file = fopen(path, "rb");
 | |
|     if (file == NULL)
 | |
|         return(NULL);
 | |
|     data = xmlMalloc(size + 1);
 | |
|     if (data != NULL) {
 | |
|         if (fread(data, 1, size, file) != size) {
 | |
|             xmlFree(data);
 | |
|             data = NULL;
 | |
|         } else {
 | |
|             data[size] = 0;
 | |
|             if (sizeRet != NULL)
 | |
|                 *sizeRet = size;
 | |
|         }
 | |
|     }
 | |
|     fclose(file);
 | |
| 
 | |
|     return(data);
 | |
| }
 | |
| 
 |