1
0
mirror of https://gitlab.gnome.org/GNOME/libxml2.git synced 2024-12-25 23:21:26 +03:00
libxml2/fuzz/fuzz.c
Nick Wellnhofer f19a95108a parser: Report malloc failures
Fix many places where malloc failures aren't reported.

Make xmlErrMemory public. This is useful for custom external entity
loaders.

Introduce new API function xmlSwitchEncodingName.

Change the way how we store whether the the parser is stopped. This used
to be signaled by setting ctxt->instate to XML_PARSER_EOF which was
misdesigned and error-prone. Set ctxt->disableSAX to 2 instead and
introduce a macro PARSER_STOPPED. Also stop to remove parser inputs in
xmlHaltParser. This allows to remove many checks of ctxt->instate.

Introduce xmlErrParser to handle errors if a parser context is
available.
2023-12-11 22:13:05 +01:00

427 lines
9.2 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;
size_t fuzzNumAllocs;
size_t fuzzMaxAllocs;
int fuzzAllocFailed;
/**
* xmlFuzzErrorFunc:
*
* An error function that simply discards all errors.
*/
void
xmlFuzzErrorFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
...) {
}
/*
* Malloc failure injection.
*
* To debug issues involving malloc failures, it's often helpful to set
* MALLOC_ABORT to 1. This should provide a backtrace of the failed
* allocation.
*/
#define XML_FUZZ_MALLOC_ABORT 0
static void *
xmlFuzzMalloc(size_t size) {
if (fuzzMaxAllocs > 0) {
fuzzNumAllocs += 1;
if (fuzzNumAllocs == fuzzMaxAllocs) {
#if XML_FUZZ_MALLOC_ABORT
abort();
#endif
fuzzAllocFailed = 1;
return(NULL);
}
}
return malloc(size);
}
static void *
xmlFuzzRealloc(void *ptr, size_t size) {
if (fuzzMaxAllocs > 0) {
fuzzNumAllocs += 1;
if (fuzzNumAllocs == fuzzMaxAllocs) {
#if XML_FUZZ_MALLOC_ABORT
abort();
#endif
fuzzAllocFailed = 1;
return(NULL);
}
}
return realloc(ptr, size);
}
void
xmlFuzzMemSetup(void) {
xmlMemSetup(free, xmlFuzzMalloc, xmlFuzzRealloc, xmlMemStrdup);
}
void
xmlFuzzMemSetLimit(size_t limit) {
fuzzNumAllocs = 0;
fuzzMaxAllocs = limit;
fuzzAllocFailed = 0;
}
int
xmlFuzzMallocFailed(void) {
return fuzzAllocFailed;
}
void
xmlFuzzResetMallocFailed(void) {
fuzzAllocFailed = 0;
}
void
xmlFuzzCheckMallocFailure(const char *func, int expect) {
if (fuzzAllocFailed != expect) {
fprintf(stderr, "%s: malloc failure %s reported\n",
func, fuzzAllocFailed ? "not" : "erroneously");
abort();
}
}
/**
* 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);
}
/**
* xmlFuzzWriteInt:
* @out: output file
* @v: integer to write
* @size: size of integer in bytes
*
* Write an integer to the fuzz data.
*/
void
xmlFuzzWriteInt(FILE *out, size_t v, int size) {
int shift;
while (size > (int) sizeof(size_t)) {
putc(0, out);
size--;
}
shift = size * 8;
while (shift > 0) {
shift -= 8;
putc((v >> shift) & 255, out);
}
}
/**
* xmlFuzzReadInt:
* @size: size of integer in bytes
*
* Read an integer from the fuzz data.
*/
size_t
xmlFuzzReadInt(int size) {
size_t ret = 0;
while ((size > 0) && (fuzzData.remaining > 0)) {
unsigned char c = (unsigned char) *fuzzData.ptr++;
fuzzData.remaining--;
ret = (ret << 8) | c;
size--;
}
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--;
if (size != NULL)
*size = fuzzData.outPtr - out;
*fuzzData.outPtr++ = '\0';
return(out);
}
if (c2 == '\\') {
fuzzData.ptr++;
fuzzData.remaining--;
}
}
*fuzzData.outPtr++ = c;
}
if (fuzzData.outPtr > out) {
if (size != NULL)
*size = fuzzData.outPtr - out;
*fuzzData.outPtr++ = '\0';
return(out);
}
if (size != NULL)
*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 entitySize;
xmlFuzzEntityInfo *entityInfo;
url = xmlFuzzReadString(NULL);
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);
if (input == NULL)
return(NULL);
input->filename = (char *) xmlCharStrdup(URL);
if (input->filename == NULL) {
xmlErrMemory(ctxt, NULL);
xmlFreeInputStream(input);
return(NULL);
}
input->buf = xmlParserInputBufferCreateMem(entity->data, entity->size,
XML_CHAR_ENCODING_NONE);
if (input->buf == NULL) {
xmlErrMemory(ctxt, NULL);
xmlFreeInputStream(input);
return(NULL);
}
input->base = input->cur = xmlBufContent(input->buf->buffer);
input->end = input->base + xmlBufUse(input->buf->buffer);
return input;
}
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);
}