2020-06-05 13:49:25 +03:00
/*
* fuzz . c : Common functions for fuzzing .
*
* See Copyright for the status of this software .
*/
2020-08-24 04:16:25 +03:00
# include <stdio.h>
2020-06-05 13:49:25 +03:00
# include <stdlib.h>
# include <string.h>
2020-08-24 04:16:25 +03:00
# include <sys/stat.h>
2020-06-05 13:49:25 +03:00
# 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 ) ;
2020-08-24 17:28:54 +03:00
xmlHashFree ( fuzzData . entities , xmlHashDefaultDeallocator ) ;
2020-06-05 13:49:25 +03:00
}
/**
* xmlFuzzReadInt :
* @ size : size of string in bytes
*
* Read an integer from the fuzz data .
*/
int
2022-10-25 20:23:07 +03:00
xmlFuzzReadInt ( void ) {
2020-06-05 13:49:25 +03:00
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 ;
}
2020-06-15 13:20:54 +03:00
/**
* 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 ) ;
}
2020-06-21 13:14:19 +03:00
/*
2020-08-06 14:20:01 +03:00
* xmlFuzzWriteString :
* @ out : output file
* @ str : string to write
*
* Write a random - length string to file in a format similar to
2020-06-21 13:14:19 +03:00
* FuzzedDataProvider . Backslash followed by newline marks the end of the
* string . Two backslashes are used to escape a backslash .
*/
2020-08-06 14:20:01 +03:00
void
xmlFuzzWriteString ( FILE * out , const char * str ) {
2020-06-21 13:14:19 +03:00
for ( ; * str ; str + + ) {
int c = ( unsigned char ) * str ;
2020-08-06 14:20:01 +03:00
putc ( c , out ) ;
2020-06-21 13:14:19 +03:00
if ( c = = ' \\ ' )
2020-08-06 14:20:01 +03:00
putc ( c , out ) ;
2020-06-21 13:14:19 +03:00
}
2020-08-06 14:20:01 +03:00
putc ( ' \\ ' , out ) ;
putc ( ' \n ' , out ) ;
2020-06-21 13:14:19 +03:00
}
2020-06-05 13:49:25 +03:00
/**
* 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 .
*/
2020-08-06 14:20:01 +03:00
const char *
2020-06-05 13:49:25 +03:00
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 ) ;
}
2022-01-26 18:35:18 +03:00
* size = 0 ;
2020-06-05 13:49:25 +03:00
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 ) ) ;
2020-12-18 02:50:34 +03:00
if ( entityInfo = = NULL )
break ;
2020-06-05 13:49:25 +03:00
entityInfo - > data = entity ;
entityInfo - > size = entitySize ;
xmlHashAddEntry ( fuzzData . entities , ( xmlChar * ) url , entityInfo ) ;
if ( num = = 0 ) {
fuzzData . mainUrl = url ;
fuzzData . mainEntity = entityInfo ;
}
num + + ;
}
}
}
2020-06-21 15:42:00 +03:00
/**
* xmlFuzzMainUrl :
*
* Returns the main URL .
*/
const char *
xmlFuzzMainUrl ( void ) {
return ( fuzzData . mainUrl ) ;
}
2020-06-05 13:49:25 +03:00
/**
* 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 ) ;
2020-12-18 02:50:34 +03:00
if ( input - > buf = = NULL ) {
xmlFreeInputStream ( input ) ;
return ( NULL ) ;
}
2020-06-05 13:49:25 +03:00
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 ) ;
}
2020-08-24 04:16:25 +03:00
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 ) ;
}