1 /*
2 ** Copyright (c) 2019 D. Richard Hipp
3 **
4 ** This program is free software; you can redistribute it and/or
5 ** modify it under the terms of the Simplified BSD License (also
6 ** known as the "2-Clause License" or "FreeBSD License".)
7 *
8 ** This program is distributed in the hope that it will be useful,
9 ** but without any warranty; without even the implied warranty of
10 ** merchantability or fitness for a particular purpose.
11 **
12 ** Author contact information:
13 ** drh@hwaci.com
14 ** http://www.hwaci.com/drh/
15 **
16 *******************************************************************************
17 **
18 ** This file contains code to connect Fossil to libFuzzer. Do a web search
19 ** for "libfuzzer" for details about that fuzzing platform.
20 **
21 ** To build on linux (the only platform for which this works at
22 ** present) first do
23 **
24 ** ./configure
25 **
26 ** Then edit the Makefile as follows:
27 **
28 ** (1) Change CC to be "clang-6.0" or some other compiler that
29 ** supports libFuzzer
30 **
31 ** (2) Change APPNAME to "fossil-fuzz"
32 **
33 ** (3) Add "-fsanitize=fuzzer" and "-DFOSSIL_FUZZ" to TCCFLAGS. Perhaps
34 ** make the first change "-fsanitize=fuzzer,undefined,address" for
35 ** extra, but slower, testing.
36 **
37 ** Then build the fuzzer using:
38 **
39 ** make clean fossil-fuzz
40 **
41 ** To run the fuzzer, create a working directory ("cases"):
42 **
43 ** mkdir cases
44 **
45 ** Then seed the working directory with example input files. For example,
46 ** if fuzzing the wiki formatter, perhaps copy *.wiki into cases. Then
47 ** run the fuzzer thusly:
48 **
49 ** fossil-fuzz cases
50 **
51 ** The default is to fuzz the Fossil-wiki translator. Use the --fuzztype TYPE
52 ** option to fuzz different aspects of the system.
53 */
54 #include "config.h"
55 #include "fuzz.h"
56
57 #if LOCAL_INTERFACE
58 /*
59 ** Type of fuzzing:
60 */
61 #define FUZZ_WIKI 0 /* The Fossil-Wiki formatter */
62 #define FUZZ_MARKDOWN 1 /* The Markdown formatter */
63 #define FUZZ_ARTIFACT 2 /* Fuzz the artifact parser */
64 #endif
65
66 /* The type of fuzzing to do */
67 static int eFuzzType = FUZZ_WIKI;
68
69 /* The fuzzer invokes this routine once for each fuzzer input
70 */
LLVMFuzzerTestOneInput(const uint8_t * aData,size_t nByte)71 int LLVMFuzzerTestOneInput(const uint8_t *aData, size_t nByte){
72 Blob in, out;
73 blob_init(&in, 0, 0);
74 blob_append(&in, (char*)aData, (int)nByte);
75 blob_zero(&out);
76 switch( eFuzzType ){
77 case FUZZ_WIKI: {
78 Blob title = BLOB_INITIALIZER;
79 wiki_convert(&in, &out, 0);
80 blob_reset(&out);
81 markdown_to_html(&in, &title, &out);
82 blob_reset(&title);
83 break;
84 }
85 }
86 blob_reset(&in);
87 blob_reset(&out);
88 return 0;
89 }
90
91 /*
92 ** Check fuzzer command-line options.
93 */
fuzzer_options(void)94 static void fuzzer_options(void){
95 const char *zType;
96 db_find_and_open_repository(OPEN_OK_NOT_FOUND|OPEN_SUBSTITUTE,0);
97 db_multi_exec("PRAGMA query_only=1;");
98 zType = find_option("fuzztype",0,1);
99 if( zType==0 || fossil_strcmp(zType,"wiki")==0 ){
100 eFuzzType = FUZZ_WIKI;
101 }else if( fossil_strcmp(zType,"markdown")==0 ){
102 eFuzzType = FUZZ_MARKDOWN;
103 }else{
104 fossil_fatal("unknown fuzz type: \"%s\"", zType);
105 }
106 }
107
108 /* Libfuzzer invokes this routine once prior to start-up to
109 ** process command-line options.
110 */
LLVMFuzzerInitialize(int * pArgc,char *** pArgv)111 int LLVMFuzzerInitialize(int *pArgc, char ***pArgv){
112 expand_args_option(*pArgc, *pArgv);
113 fuzzer_options();
114 *pArgc = g.argc;
115 *pArgv = g.argv;
116 return 0;
117 }
118
119 /*
120 ** COMMAND: test-fuzz
121 **
122 ** Usage: %fossil test-fuzz [-type TYPE] INPUTFILE...
123 **
124 ** Run a fuzz test using INPUTFILE as the test data. TYPE can be one of:
125 **
126 ** wiki Fuzz the Fossil-wiki translator
127 ** markdown Fuzz the markdown translator
128 ** artifact Fuzz the artifact parser
129 */
fuzz_command(void)130 void fuzz_command(void){
131 Blob in;
132 int i;
133 fuzzer_options();
134 verify_all_options();
135 for(i=2; i<g.argc; i++){
136 blob_read_from_file(&in, g.argv[i], ExtFILE);
137 LLVMFuzzerTestOneInput((const uint8_t*)in.aData, (size_t)in.nUsed);
138 fossil_print("%s\n", g.argv[i]);
139 blob_reset(&in);
140 }
141 }
142