(root)/
libredwg-0.13/
examples/
llvmfuzz.c
       1  /*****************************************************************************/
       2  /*  LibreDWG - free implementation of the DWG file format                    */
       3  /*                                                                           */
       4  /*  Copyright (C) 2021, 2023 Free Software Foundation, Inc.                  */
       5  /*                                                                           */
       6  /*  This library is free software, licensed under the terms of the GNU       */
       7  /*  General Public License as published by the Free Software Foundation,     */
       8  /*  either version 3 of the License, or (at your option) any later version.  */
       9  /*  You should have received a copy of the GNU General Public License        */
      10  /*  along with this program.  If not, see <http://www.gnu.org/licenses/>.    */
      11  /*****************************************************************************/
      12  
      13  /*
      14   * llvmfuzz.c: libfuzzer testing, esp. for oss-fuzz. with libfuzzer or
      15   * standalone written by Reini Urban
      16   */
      17  
      18  #include <stdio.h>
      19  #include <stdlib.h>
      20  #include <assert.h>
      21  //#include <unistd.h>
      22  #include <sys/stat.h>
      23  
      24  #include "common.h"
      25  #include <dwg.h>
      26  #ifdef HAVE_SYS_TIME_H
      27  #  include <sys/time.h>
      28  #endif
      29  #include "decode.h"
      30  #include "encode.h"
      31  #include "bits.h"
      32  #ifndef DISABLE_DXF
      33  #  include "out_dxf.h"
      34  #  ifndef DISABLE_JSON
      35  #    include "in_json.h"
      36  #    include "out_json.h"
      37  #  endif
      38  #  include "in_dxf.h"
      39  #endif
      40  
      41  int out;
      42  int ver;
      43  
      44  extern int LLVMFuzzerTestOneInput (const unsigned char *data, size_t size);
      45  
      46  // libfuzzer limitation:
      47  // Enforce NULL-termination of the input buffer, to avoid bogus reports. copy
      48  // it. Problematic is mostly strtol(3) which also works with \n termination.
      49  static int
      50  enforce_null_termination (Bit_Chain *dat, bool enforce)
      51  {
      52    unsigned char *copy;
      53    unsigned char c;
      54    if (!dat->size)
      55      return 0;
      56    c = dat->chain[dat->size - 1];
      57    // Allow \n termination without \0 in DXF? No, still crashes
      58    if (!enforce && ((c == '\n' && c + 1 == '\0') || c == '\0'))
      59      return 0;
      60  #ifdef STANDALONE
      61    fprintf (stderr,
      62             "llvmfuzz_standalone: enforce libfuzzer buffer NULL termination\n");
      63  #endif
      64    copy = malloc (dat->size + 1);
      65    memcpy (copy, dat->chain, dat->size);
      66    copy[dat->size] = '\0';
      67    dat->chain = copy;
      68    return 1;
      69  }
      70  
      71  int
      72  LLVMFuzzerTestOneInput (const unsigned char *data, size_t size)
      73  {
      74    Dwg_Data dwg;
      75    Bit_Chain dat = { NULL, 0, 0, 0, 0, 0, 0, NULL, 0 };
      76    Bit_Chain out_dat = { NULL, 0, 0, 0, 0, 0, 0, NULL, 0 };
      77    int copied = 0;
      78    struct ly_ctx *ctx = NULL;
      79  
      80    static char tmp_file[256];
      81    dat.chain = (unsigned char *)data;
      82    dat.size = size;
      83    memset (&dwg, 0, sizeof (dwg));
      84  
      85    // Detect the input format: DWG, DXF or JSON
      86    if (dat.size > 2 && dat.chain[0] == 'A' && dat.chain[1] == 'C')
      87      {
      88        if (dwg_decode (&dat, &dwg) >= DWG_ERR_CRITICAL)
      89          {
      90            dwg_free (&dwg);
      91            return 0;
      92          }
      93      }
      94  #ifndef DISABLE_JSON
      95    else if (dat.size > 1 && dat.chain[0] == '{')
      96      {
      97        copied = enforce_null_termination (&dat, true);
      98        if (dwg_read_json (&dat, &dwg) >= DWG_ERR_CRITICAL)
      99          {
     100            if (copied)
     101              bit_chain_free (&dat);
     102            dwg_free (&dwg);
     103            return 0;
     104          }
     105        dat.opts |= DWG_OPTS_INJSON;
     106        dwg.opts |= DWG_OPTS_INJSON;
     107      }
     108  #endif
     109  #ifndef DISABLE_DXF
     110    else
     111      {
     112        copied = enforce_null_termination (&dat, false);
     113        if (dwg_read_dxf (&dat, &dwg) >= DWG_ERR_CRITICAL)
     114          {
     115            if (copied)
     116              bit_chain_free (&dat);
     117            dwg_free (&dwg);
     118            return 0;
     119          }
     120      }
     121  #else
     122    else
     123      return 0;
     124  #endif
     125  
     126    memset (&out_dat, 0, sizeof (out_dat));
     127    bit_chain_set_version (&out_dat, &dat);
     128    if (copied)
     129      bit_chain_free (&dat);
     130  
     131  #if 0
     132      snprintf (tmp_file, 255, "/tmp/llvmfuzzer%d.out", getpid());
     133      tmp_file[255] = '\0';
     134  #elif defined _WIN32
     135    strcpy (tmp_file, "NUL");
     136  #else
     137    strcpy (tmp_file, "/dev/null");
     138  #endif
     139    out_dat.fh = fopen (tmp_file, "w");
     140  
     141    switch (out)
     142      {
     143      case 0:
     144        {
     145          switch (ver)
     146            {
     147            // TODO support preR13, many downconverters still missing
     148            case 0:
     149              out_dat.version = dwg.header.version = R_1_4;
     150              break;
     151            case 1:
     152              out_dat.version = dwg.header.version = R_2_0;
     153              break;
     154            case 2:
     155              out_dat.version = dwg.header.version = R_2_10;
     156              break;
     157            case 3:
     158              out_dat.version = dwg.header.version = R_2_21;
     159              break;
     160            case 4:
     161              out_dat.version = dwg.header.version = R_2_4;
     162              break;
     163            case 5:
     164              out_dat.version = dwg.header.version = R_2_6;
     165              break;
     166            case 6:
     167              out_dat.version = dwg.header.version = R_9;
     168              break;
     169            case 7:
     170              out_dat.version = dwg.header.version = R_10;
     171              break;
     172            case 8:
     173              out_dat.version = dwg.header.version = R_11;
     174              break;
     175            case 9:
     176              out_dat.version = dwg.header.version = R_12;
     177              break;
     178            case 10:
     179              out_dat.version = dwg.header.version = R_13;
     180              break;
     181            case 11:
     182              out_dat.version = dwg.header.version = R_13c3;
     183              break;
     184            case 12:
     185              out_dat.version = dwg.header.version = R_14;
     186              break;
     187            case 13:
     188              out_dat.version = dwg.header.version = R_2004;
     189              break;
     190            default: // favor this one
     191              out_dat.version = dwg.header.version = R_2000;
     192              break;
     193            }
     194          dwg_encode (&dwg, &out_dat);
     195          break;
     196        }
     197  #ifndef DISABLE_DXF
     198      case 1:
     199        dwg_write_dxf (&out_dat, &dwg);
     200        break;
     201      case 2: // experimental
     202        dwg_write_dxfb (&out_dat, &dwg);
     203        break;
     204  #  ifndef DISABLE_JSON
     205      case 3:
     206        dwg_write_json (&out_dat, &dwg);
     207        break;
     208      case 4:
     209        dwg_write_geojson (&out_dat, &dwg);
     210        break;
     211  #  endif
     212  #endif
     213      default:
     214        break;
     215      }
     216    dwg_free (&dwg);
     217    free (out_dat.chain);
     218    fclose (out_dat.fh);
     219    // unlink (tmp_file);
     220    return 0;
     221  }
     222  
     223  #ifdef STANDALONE
     224  /*
     225  # ifdef __GNUC__
     226  __attribute__((weak))
     227  # endif
     228  extern int LLVMFuzzerInitialize(int *argc, char ***argv);
     229  */
     230  
     231  static int
     232  usage (void)
     233  {
     234    printf ("\nUsage: OUT=0 VER=3 llvmfuzz_standalone INPUT...");
     235    return 1;
     236  }
     237  // llvmfuzz_standalone reproducer, see OUT and VER env vars
     238  int
     239  main (int argc, char *argv[])
     240  {
     241    unsigned seed;
     242    const unsigned int possible_outputformats =
     243  #ifdef DISABLE_DXF
     244  #  ifdef DISABLE_JSON
     245        1;
     246  #  else
     247        3;
     248  #  endif
     249  #else
     250        5;
     251  #endif
     252  
     253    if (argc <= 1 || !*argv[1])
     254      return usage ();
     255    if (getenv ("SEED"))
     256      seed = (unsigned)strtol (getenv ("SEED"), NULL, 10) % 9999;
     257    else
     258      {
     259  #ifdef HAVE_GETTIMEOFDAY
     260        struct timeval tval;
     261        gettimeofday (&tval, NULL);
     262        seed = (unsigned)(tval.tv_sec * 1000 + tval.tv_usec) % 9999;
     263  #else
     264        seed = (unsigned)time (NULL) % 9999;
     265  #endif
     266      }
     267    srand (seed);
     268    /* works only on linux
     269    if (LLVMFuzzerInitialize)
     270      LLVMFuzzerInitialize (&argc, &argv);
     271    */
     272    for (int i = 1; i < argc; i++)
     273      {
     274        unsigned char *buf;
     275        FILE *f = fopen (argv[i], "rb");
     276        struct stat attrib;
     277        long len;
     278        size_t n_read;
     279        int fd;
     280        if (!f)
     281          {
     282            fprintf (stderr, "Illegal file argument %s\n", argv[i]);
     283            continue;
     284          }
     285        fd = fileno (f);
     286        if (fd < 0 || fstat (fd, &attrib)
     287            || !(S_ISREG (attrib.st_mode)
     288  #  ifndef _WIN32
     289                 || S_ISLNK (attrib.st_mode)
     290  #  endif
     291                     ))
     292          {
     293            fprintf (stderr, "Illegal input file \"%s\"\n", argv[i]);
     294            continue;
     295          }
     296        // libFuzzer design bug, not zero-terminating its text buffer
     297        fseek (f, 0, SEEK_END);
     298        len = ftell (f);
     299        fseek (f, 0, SEEK_SET);
     300        if (len <= 0)
     301          continue;
     302        buf = (unsigned char *)malloc (len);
     303        n_read = fread (buf, 1, len, f);
     304        fclose (f);
     305        assert ((long)n_read == len);
     306  
     307        out = rand () % possible_outputformats;
     308  #ifdef STANDALONE
     309        if (getenv ("OUT"))
     310          out = strtol (getenv ("OUT"), NULL, 10);
     311        // print SEED onlyu when needed (no env vars given)
     312        if (!(out || getenv ("VER")))
     313          fprintf (stderr, "SEED=%04u ", seed);
     314        fprintf (stderr, "OUT=%d ", out);
     315  #endif
     316        if (out == 0)
     317          {
     318            ver = rand () % 20;
     319  #ifdef STANDALONE
     320            if (getenv ("VER"))
     321              ver = strtol (getenv ("VER"), NULL, 10);
     322            fprintf (stderr, "VER=%d ", ver);
     323  #endif
     324          }
     325        fprintf (stderr, "examples/llvmfuzz_standalone %s [%" PRIuSIZE "]\n",
     326                 argv[i], len);
     327        LLVMFuzzerTestOneInput (buf, len);
     328        free (buf);
     329        // Bit_Chain dat = { 0 };
     330        // dat_read_file (&dat, fp, argv[i]);
     331        // LLVMFuzzerTestOneInput (dat.chain, dat.size);
     332        // bit_free_chain (&dat);
     333      }
     334  }
     335  #endif