(root)/
libxml2-2.12.3/
fuzz/
fuzz.c
       1  /*
       2   * fuzz.c: Common functions for fuzzing.
       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  #include <sys/stat.h>
      11  
      12  #include <libxml/hash.h>
      13  #include <libxml/parser.h>
      14  #include <libxml/parserInternals.h>
      15  #include <libxml/tree.h>
      16  #include <libxml/xmlIO.h>
      17  #include "fuzz.h"
      18  
      19  typedef struct {
      20      const char *data;
      21      size_t size;
      22  } xmlFuzzEntityInfo;
      23  
      24  /* Single static instance for now */
      25  static struct {
      26      /* Original data */
      27      const char *data;
      28      size_t size;
      29  
      30      /* Remaining data */
      31      const char *ptr;
      32      size_t remaining;
      33  
      34      /* Buffer for unescaped strings */
      35      char *outBuf;
      36      char *outPtr; /* Free space at end of buffer */
      37  
      38      xmlHashTablePtr entities; /* Maps URLs to xmlFuzzEntityInfos */
      39  
      40      /* The first entity is the main entity. */
      41      const char *mainUrl;
      42      xmlFuzzEntityInfo *mainEntity;
      43  } fuzzData;
      44  
      45  size_t fuzzNumAllocs;
      46  size_t fuzzMaxAllocs;
      47  int fuzzAllocFailed;
      48  
      49  /**
      50   * xmlFuzzErrorFunc:
      51   *
      52   * An error function that simply discards all errors.
      53   */
      54  void
      55  xmlFuzzErrorFunc(void *ctx ATTRIBUTE_UNUSED, const char *msg ATTRIBUTE_UNUSED,
      56                   ...) {
      57  }
      58  
      59  /*
      60   * Malloc failure injection.
      61   *
      62   * Quick tip to debug complicated issues: Increase MALLOC_OFFSET until
      63   * the crash disappears (or a different issue is triggered). Then set
      64   * the offset to the highest value that produces a crash and set
      65   * MALLOC_ABORT to 1 to see which failed memory allocation causes the
      66   * issue.
      67   */
      68  
      69  #define XML_FUZZ_MALLOC_OFFSET  0
      70  #define XML_FUZZ_MALLOC_ABORT   0
      71  
      72  static void *
      73  xmlFuzzMalloc(size_t size) {
      74      if (fuzzMaxAllocs > 0) {
      75          if (fuzzNumAllocs >= fuzzMaxAllocs - 1) {
      76  #if XML_FUZZ_MALLOC_ABORT
      77              abort();
      78  #endif
      79              fuzzAllocFailed = 1;
      80              return(NULL);
      81          }
      82          fuzzNumAllocs += 1;
      83      }
      84      return malloc(size);
      85  }
      86  
      87  static void *
      88  xmlFuzzRealloc(void *ptr, size_t size) {
      89      if (fuzzMaxAllocs > 0) {
      90          if (fuzzNumAllocs >= fuzzMaxAllocs - 1) {
      91  #if XML_FUZZ_MALLOC_ABORT
      92              abort();
      93  #endif
      94              fuzzAllocFailed = 1;
      95              return(NULL);
      96          }
      97          fuzzNumAllocs += 1;
      98      }
      99      return realloc(ptr, size);
     100  }
     101  
     102  void
     103  xmlFuzzMemSetup(void) {
     104      xmlMemSetup(free, xmlFuzzMalloc, xmlFuzzRealloc, xmlMemStrdup);
     105  }
     106  
     107  void
     108  xmlFuzzMemSetLimit(size_t limit) {
     109      fuzzNumAllocs = 0;
     110      fuzzMaxAllocs = limit ? limit + XML_FUZZ_MALLOC_OFFSET : 0;
     111      fuzzAllocFailed = 0;
     112  }
     113  
     114  int
     115  xmlFuzzMallocFailed(void) {
     116      return fuzzAllocFailed;
     117  }
     118  
     119  /**
     120   * xmlFuzzDataInit:
     121   *
     122   * Initialize fuzz data provider.
     123   */
     124  void
     125  xmlFuzzDataInit(const char *data, size_t size) {
     126      fuzzData.data = data;
     127      fuzzData.size = size;
     128      fuzzData.ptr = data;
     129      fuzzData.remaining = size;
     130  
     131      fuzzData.outBuf = xmlMalloc(size + 1);
     132      fuzzData.outPtr = fuzzData.outBuf;
     133  
     134      fuzzData.entities = xmlHashCreate(8);
     135      fuzzData.mainUrl = NULL;
     136      fuzzData.mainEntity = NULL;
     137  }
     138  
     139  /**
     140   * xmlFuzzDataFree:
     141   *
     142   * Cleanup fuzz data provider.
     143   */
     144  void
     145  xmlFuzzDataCleanup(void) {
     146      xmlFree(fuzzData.outBuf);
     147      xmlHashFree(fuzzData.entities, xmlHashDefaultDeallocator);
     148  }
     149  
     150  /**
     151   * xmlFuzzWriteInt:
     152   * @out:  output file
     153   * @v:  integer to write
     154   * @size:  size of integer in bytes
     155   *
     156   * Write an integer to the fuzz data.
     157   */
     158  void
     159  xmlFuzzWriteInt(FILE *out, size_t v, int size) {
     160      int shift;
     161  
     162      while (size > (int) sizeof(size_t)) {
     163          putc(0, out);
     164          size--;
     165      }
     166  
     167      shift = size * 8;
     168      while (shift > 0) {
     169          shift -= 8;
     170          putc((v >> shift) & 255, out);
     171      }
     172  }
     173  
     174  /**
     175   * xmlFuzzReadInt:
     176   * @size:  size of integer in bytes
     177   *
     178   * Read an integer from the fuzz data.
     179   */
     180  size_t
     181  xmlFuzzReadInt(int size) {
     182      size_t ret = 0;
     183  
     184      while ((size > 0) && (fuzzData.remaining > 0)) {
     185          unsigned char c = (unsigned char) *fuzzData.ptr++;
     186          fuzzData.remaining--;
     187          ret = (ret << 8) | c;
     188          size--;
     189      }
     190  
     191      return ret;
     192  }
     193  
     194  /**
     195   * xmlFuzzReadRemaining:
     196   * @size:  size of string in bytes
     197   *
     198   * Read remaining bytes from fuzz data.
     199   */
     200  const char *
     201  xmlFuzzReadRemaining(size_t *size) {
     202      const char *ret = fuzzData.ptr;
     203  
     204      *size = fuzzData.remaining;
     205      fuzzData.ptr += fuzzData.remaining;
     206      fuzzData.remaining = 0;
     207  
     208      return(ret);
     209  }
     210  
     211  /*
     212   * xmlFuzzWriteString:
     213   * @out:  output file
     214   * @str:  string to write
     215   *
     216   * Write a random-length string to file in a format similar to
     217   * FuzzedDataProvider. Backslash followed by newline marks the end of the
     218   * string. Two backslashes are used to escape a backslash.
     219   */
     220  void
     221  xmlFuzzWriteString(FILE *out, const char *str) {
     222      for (; *str; str++) {
     223          int c = (unsigned char) *str;
     224          putc(c, out);
     225          if (c == '\\')
     226              putc(c, out);
     227      }
     228      putc('\\', out);
     229      putc('\n', out);
     230  }
     231  
     232  /**
     233   * xmlFuzzReadString:
     234   * @size:  size of string in bytes
     235   *
     236   * Read a random-length string from the fuzz data.
     237   *
     238   * The format is similar to libFuzzer's FuzzedDataProvider but treats
     239   * backslash followed by newline as end of string. This makes the fuzz data
     240   * more readable. A backslash character is escaped with another backslash.
     241   *
     242   * Returns a zero-terminated string or NULL if the fuzz data is exhausted.
     243   */
     244  const char *
     245  xmlFuzzReadString(size_t *size) {
     246      const char *out = fuzzData.outPtr;
     247  
     248      while (fuzzData.remaining > 0) {
     249          int c = *fuzzData.ptr++;
     250          fuzzData.remaining--;
     251  
     252          if ((c == '\\') && (fuzzData.remaining > 0)) {
     253              int c2 = *fuzzData.ptr;
     254  
     255              if (c2 == '\n') {
     256                  fuzzData.ptr++;
     257                  fuzzData.remaining--;
     258                  if (size != NULL)
     259                      *size = fuzzData.outPtr - out;
     260                  *fuzzData.outPtr++ = '\0';
     261                  return(out);
     262              }
     263              if (c2 == '\\') {
     264                  fuzzData.ptr++;
     265                  fuzzData.remaining--;
     266              }
     267          }
     268  
     269          *fuzzData.outPtr++ = c;
     270      }
     271  
     272      if (fuzzData.outPtr > out) {
     273          if (size != NULL)
     274              *size = fuzzData.outPtr - out;
     275          *fuzzData.outPtr++ = '\0';
     276          return(out);
     277      }
     278  
     279      if (size != NULL)
     280          *size = 0;
     281      return(NULL);
     282  }
     283  
     284  /**
     285   * xmlFuzzReadEntities:
     286   *
     287   * Read entities like the main XML file, external DTDs, external parsed
     288   * entities from fuzz data.
     289   */
     290  void
     291  xmlFuzzReadEntities(void) {
     292      size_t num = 0;
     293  
     294      while (1) {
     295          const char *url, *entity;
     296          size_t entitySize;
     297          xmlFuzzEntityInfo *entityInfo;
     298  
     299          url = xmlFuzzReadString(NULL);
     300          if (url == NULL) break;
     301  
     302          entity = xmlFuzzReadString(&entitySize);
     303          if (entity == NULL) break;
     304  
     305          if (xmlHashLookup(fuzzData.entities, (xmlChar *)url) == NULL) {
     306              entityInfo = xmlMalloc(sizeof(xmlFuzzEntityInfo));
     307              if (entityInfo == NULL)
     308                  break;
     309              entityInfo->data = entity;
     310              entityInfo->size = entitySize;
     311  
     312              xmlHashAddEntry(fuzzData.entities, (xmlChar *)url, entityInfo);
     313  
     314              if (num == 0) {
     315                  fuzzData.mainUrl = url;
     316                  fuzzData.mainEntity = entityInfo;
     317              }
     318  
     319              num++;
     320          }
     321      }
     322  }
     323  
     324  /**
     325   * xmlFuzzMainUrl:
     326   *
     327   * Returns the main URL.
     328   */
     329  const char *
     330  xmlFuzzMainUrl(void) {
     331      return(fuzzData.mainUrl);
     332  }
     333  
     334  /**
     335   * xmlFuzzMainEntity:
     336   * @size:  size of the main entity in bytes
     337   *
     338   * Returns the main entity.
     339   */
     340  const char *
     341  xmlFuzzMainEntity(size_t *size) {
     342      if (fuzzData.mainEntity == NULL)
     343          return(NULL);
     344      *size = fuzzData.mainEntity->size;
     345      return(fuzzData.mainEntity->data);
     346  }
     347  
     348  /**
     349   * xmlFuzzEntityLoader:
     350   *
     351   * The entity loader for fuzz data.
     352   */
     353  xmlParserInputPtr
     354  xmlFuzzEntityLoader(const char *URL, const char *ID ATTRIBUTE_UNUSED,
     355                      xmlParserCtxtPtr ctxt) {
     356      xmlParserInputPtr input;
     357      xmlFuzzEntityInfo *entity;
     358  
     359      if (URL == NULL)
     360          return(NULL);
     361      entity = xmlHashLookup(fuzzData.entities, (xmlChar *) URL);
     362      if (entity == NULL)
     363          return(NULL);
     364  
     365      input = xmlNewInputStream(ctxt);
     366      if (input == NULL)
     367          return(NULL);
     368      input->filename = (char *) xmlCharStrdup(URL);
     369      input->buf = xmlParserInputBufferCreateMem(entity->data, entity->size,
     370                                                 XML_CHAR_ENCODING_NONE);
     371      if (input->buf == NULL) {
     372          xmlFreeInputStream(input);
     373          return(NULL);
     374      }
     375      input->base = input->cur = xmlBufContent(input->buf->buffer);
     376      input->end = input->base + xmlBufUse(input->buf->buffer);
     377  
     378      return input;
     379  }
     380  
     381  char *
     382  xmlSlurpFile(const char *path, size_t *sizeRet) {
     383      FILE *file;
     384      struct stat statbuf;
     385      char *data;
     386      size_t size;
     387  
     388      if ((stat(path, &statbuf) != 0) || (!S_ISREG(statbuf.st_mode)))
     389          return(NULL);
     390      size = statbuf.st_size;
     391      file = fopen(path, "rb");
     392      if (file == NULL)
     393          return(NULL);
     394      data = xmlMalloc(size + 1);
     395      if (data != NULL) {
     396          if (fread(data, 1, size, file) != size) {
     397              xmlFree(data);
     398              data = NULL;
     399          } else {
     400              data[size] = 0;
     401              if (sizeRet != NULL)
     402                  *sizeRet = size;
     403          }
     404      }
     405      fclose(file);
     406  
     407      return(data);
     408  }
     409