blob: d51c3f03168aeaf36b58ca48ae8862106d5be0ac [file] [log] [blame]
kccdbf20a02018-05-10 19:59:01 +00001/*===- DataFlow.cpp - a standalone DataFlow tracer -------===//
2//
chandlerc40284492019-01-19 08:50:56 +00003// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
kccdbf20a02018-05-10 19:59:01 +00006//
7//===----------------------------------------------------------------------===//
8// An experimental data-flow tracer for fuzz targets.
9// It is based on DFSan and SanitizerCoverage.
10// https://clang.llvm.org/docs/DataFlowSanitizer.html
11// https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-data-flow
12//
13// It executes the fuzz target on the given input while monitoring the
14// data flow for every instrumented comparison instruction.
15//
kccce6392a2019-05-08 00:51:15 +000016// The output shows which functions depend on which bytes of the input,
17// and also provides basic-block coverage for every input.
kccdbf20a02018-05-10 19:59:01 +000018//
19// Build:
20// 1. Compile this file with -fsanitize=dataflow
21// 2. Build the fuzz target with -g -fsanitize=dataflow
kccce6392a2019-05-08 00:51:15 +000022// -fsanitize-coverage=trace-pc-guard,pc-table,bb,trace-cmp
kccdbf20a02018-05-10 19:59:01 +000023// 3. Link those together with -fsanitize=dataflow
24//
25// -fsanitize-coverage=trace-cmp inserts callbacks around every comparison
26// instruction, DFSan modifies the calls to pass the data flow labels.
27// The callbacks update the data flow label for the current function.
28// See e.g. __dfsw___sanitizer_cov_trace_cmp1 below.
29//
kccce6392a2019-05-08 00:51:15 +000030// -fsanitize-coverage=trace-pc-guard,pc-table,bb instruments function
kccdbf20a02018-05-10 19:59:01 +000031// entries so that the comparison callback knows that current function.
kccce6392a2019-05-08 00:51:15 +000032// -fsanitize-coverage=...,bb also allows to collect basic block coverage.
kccdbf20a02018-05-10 19:59:01 +000033//
34//
35// Run:
kccce6392a2019-05-08 00:51:15 +000036// # Collect data flow and coverage for INPUT_FILE
37// # write to OUTPUT_FILE (default: stdout)
38// ./a.out FIRST_LABEL LAST_LABEL INPUT_FILE [OUTPUT_FILE]
kccdbf20a02018-05-10 19:59:01 +000039//
40// # Print all instrumented functions. llvm-symbolizer must be present in PATH
41// ./a.out
42//
43// Example output:
44// ===============
kcc54dce2c2018-05-23 20:57:11 +000045// F0 11111111111111
46// F1 10000000000000
kccce6392a2019-05-08 00:51:15 +000047// C0 1 2 3 4
48// C1
kccdbf20a02018-05-10 19:59:01 +000049// ===============
kcc54dce2c2018-05-23 20:57:11 +000050// "FN xxxxxxxxxx": tells what bytes of the input does the function N depend on.
51// The byte string is LEN+1 bytes. The last byte is set if the function
52// depends on the input length.
kccce6392a2019-05-08 00:51:15 +000053// "CN X Y Z": tells that a function N has basic blocks X, Y, and Z covered
54// in addition to the function's entry block.
55//
kccdbf20a02018-05-10 19:59:01 +000056//===----------------------------------------------------------------------===*/
57
58#include <assert.h>
59#include <stdio.h>
60#include <stdlib.h>
61#include <stdint.h>
62#include <string.h>
63
64#include <execinfo.h> // backtrace_symbols_fd
65
66#include <sanitizer/dfsan_interface.h>
67
68extern "C" {
69extern int LLVMFuzzerTestOneInput(const unsigned char *Data, size_t Size);
70__attribute__((weak)) extern int LLVMFuzzerInitialize(int *argc, char ***argv);
71} // extern "C"
72
73static size_t InputLen;
dor1sd7a96a22019-04-12 21:00:12 +000074static size_t InputLabelBeg;
75static size_t InputLabelEnd;
76static size_t InputSizeLabel;
kccce6392a2019-05-08 00:51:15 +000077static size_t NumFuncs, NumGuards;
78static uint32_t *GuardsBeg, *GuardsEnd;
79static const uintptr_t *PCsBeg, *PCsEnd;
kccdbf20a02018-05-10 19:59:01 +000080static __thread size_t CurrentFunc;
81static dfsan_label *FuncLabels; // Array of NumFuncs elements.
kccce6392a2019-05-08 00:51:15 +000082static bool *BBExecuted; // Array of NumGuards elements.
kcc54dce2c2018-05-23 20:57:11 +000083static char *PrintableStringForLabel; // InputLen + 2 bytes.
kcc86e43882018-06-06 01:23:29 +000084static bool LabelSeen[1 << 8 * sizeof(dfsan_label)];
kccdbf20a02018-05-10 19:59:01 +000085
kccce6392a2019-05-08 00:51:15 +000086enum {
87 PCFLAG_FUNC_ENTRY = 1,
88};
89
kccdbf20a02018-05-10 19:59:01 +000090// Prints all instrumented functions.
kcc54dce2c2018-05-23 20:57:11 +000091static int PrintFunctions() {
kccdbf20a02018-05-10 19:59:01 +000092 // We don't have the symbolizer integrated with dfsan yet.
93 // So use backtrace_symbols_fd and pipe it through llvm-symbolizer.
94 // TODO(kcc): this is pretty ugly and may break in lots of ways.
95 // We'll need to make a proper in-process symbolizer work with DFSan.
96 FILE *Pipe = popen("sed 's/(+/ /g; s/).*//g' "
97 "| llvm-symbolizer "
98 "| grep 'dfs\\$' "
99 "| sed 's/dfs\\$//g'", "w");
kccce6392a2019-05-08 00:51:15 +0000100 for (size_t I = 0; I < NumGuards; I++) {
101 uintptr_t PC = PCsBeg[I * 2];
102 uintptr_t PCFlags = PCsBeg[I * 2 + 1];
103 if (!(PCFlags & PCFLAG_FUNC_ENTRY)) continue;
kccdbf20a02018-05-10 19:59:01 +0000104 void *const Buf[1] = {(void*)PC};
105 backtrace_symbols_fd(Buf, 1, fileno(Pipe));
106 }
107 pclose(Pipe);
108 return 0;
109}
110
kcc86e43882018-06-06 01:23:29 +0000111extern "C"
112void SetBytesForLabel(dfsan_label L, char *Bytes) {
113 if (LabelSeen[L])
114 return;
115 LabelSeen[L] = true;
kccb8a127f2018-05-23 23:55:54 +0000116 assert(L);
dor1sd7a96a22019-04-12 21:00:12 +0000117 if (L < InputSizeLabel) {
118 Bytes[L + InputLabelBeg - 1] = '1';
119 } else if (L == InputSizeLabel) {
120 Bytes[InputLen] = '1';
kcc54dce2c2018-05-23 20:57:11 +0000121 } else {
kccdbf20a02018-05-10 19:59:01 +0000122 auto *DLI = dfsan_get_label_info(L);
kcc54dce2c2018-05-23 20:57:11 +0000123 SetBytesForLabel(DLI->l1, Bytes);
124 SetBytesForLabel(DLI->l2, Bytes);
kccdbf20a02018-05-10 19:59:01 +0000125 }
kcc54dce2c2018-05-23 20:57:11 +0000126}
127
128static char *GetPrintableStringForLabel(dfsan_label L) {
129 memset(PrintableStringForLabel, '0', InputLen + 1);
130 PrintableStringForLabel[InputLen + 1] = 0;
kcc86e43882018-06-06 01:23:29 +0000131 memset(LabelSeen, 0, sizeof(LabelSeen));
kcc54dce2c2018-05-23 20:57:11 +0000132 SetBytesForLabel(L, PrintableStringForLabel);
133 return PrintableStringForLabel;
134}
135
136static void PrintDataFlow(FILE *Out) {
kccdbf20a02018-05-10 19:59:01 +0000137 for (size_t I = 0; I < NumFuncs; I++)
138 if (FuncLabels[I])
kcc54dce2c2018-05-23 20:57:11 +0000139 fprintf(Out, "F%zd %s\n", I, GetPrintableStringForLabel(FuncLabels[I]));
kccdbf20a02018-05-10 19:59:01 +0000140}
141
kccce6392a2019-05-08 00:51:15 +0000142static void PrintCoverage(FILE *Out) {
143 ssize_t CurrentFuncGuard = -1;
144 ssize_t CurrentFuncNum = -1;
145 int NumFuncsCovered = 0;
146 for (size_t I = 0; I < NumGuards; I++) {
147 bool IsEntry = PCsBeg[I * 2 + 1] & PCFLAG_FUNC_ENTRY;
148 if (IsEntry) {
149 CurrentFuncNum++;
150 CurrentFuncGuard = I;
151 }
152 if (!BBExecuted[I]) continue;
153 if (IsEntry) {
154 if (NumFuncsCovered) fprintf(Out, "\n");
155 fprintf(Out, "C%zd ", CurrentFuncNum);
156 NumFuncsCovered++;
157 } else {
158 fprintf(Out, "%zd ", I - CurrentFuncGuard);
159 }
160 }
161 fprintf(Out, "\n");
162}
163
kccdbf20a02018-05-10 19:59:01 +0000164int main(int argc, char **argv) {
165 if (LLVMFuzzerInitialize)
166 LLVMFuzzerInitialize(&argc, &argv);
167 if (argc == 1)
168 return PrintFunctions();
kcc0a250e22018-05-24 01:43:48 +0000169 assert(argc == 4 || argc == 5);
dor1sd7a96a22019-04-12 21:00:12 +0000170 InputLabelBeg = atoi(argv[1]);
171 InputLabelEnd = atoi(argv[2]);
172 assert(InputLabelBeg < InputLabelEnd);
kccdbf20a02018-05-10 19:59:01 +0000173
kcc0a250e22018-05-24 01:43:48 +0000174 const char *Input = argv[3];
kccdbf20a02018-05-10 19:59:01 +0000175 fprintf(stderr, "INFO: reading '%s'\n", Input);
176 FILE *In = fopen(Input, "r");
177 assert(In);
178 fseek(In, 0, SEEK_END);
179 InputLen = ftell(In);
180 fseek(In, 0, SEEK_SET);
181 unsigned char *Buf = (unsigned char*)malloc(InputLen);
182 size_t NumBytesRead = fread(Buf, 1, InputLen, In);
183 assert(NumBytesRead == InputLen);
kcc54dce2c2018-05-23 20:57:11 +0000184 PrintableStringForLabel = (char*)malloc(InputLen + 2);
kccdbf20a02018-05-10 19:59:01 +0000185 fclose(In);
186
187 fprintf(stderr, "INFO: running '%s'\n", Input);
188 for (size_t I = 1; I <= InputLen; I++) {
kcc0a250e22018-05-24 01:43:48 +0000189 size_t Idx = I - 1;
dor1sd7a96a22019-04-12 21:00:12 +0000190 if (Idx >= InputLabelBeg && Idx < InputLabelEnd) {
191 dfsan_label L = dfsan_create_label("", nullptr);
192 assert(L == I - InputLabelBeg);
kcc0a250e22018-05-24 01:43:48 +0000193 dfsan_set_label(L, Buf + Idx, 1);
dor1sd7a96a22019-04-12 21:00:12 +0000194 }
kccdbf20a02018-05-10 19:59:01 +0000195 }
196 dfsan_label SizeL = dfsan_create_label("", nullptr);
dor1sd7a96a22019-04-12 21:00:12 +0000197 InputSizeLabel = SizeL;
198 assert(InputSizeLabel == InputLabelEnd - InputLabelBeg + 1);
kccdbf20a02018-05-10 19:59:01 +0000199 dfsan_set_label(SizeL, &InputLen, sizeof(InputLen));
200
201 LLVMFuzzerTestOneInput(Buf, InputLen);
202 free(Buf);
203
kcc0a250e22018-05-24 01:43:48 +0000204 bool OutIsStdout = argc == 4;
kccdbf20a02018-05-10 19:59:01 +0000205 fprintf(stderr, "INFO: writing dataflow to %s\n",
kcc0a250e22018-05-24 01:43:48 +0000206 OutIsStdout ? "<stdout>" : argv[4]);
207 FILE *Out = OutIsStdout ? stdout : fopen(argv[4], "w");
kccdbf20a02018-05-10 19:59:01 +0000208 PrintDataFlow(Out);
kccce6392a2019-05-08 00:51:15 +0000209 PrintCoverage(Out);
kccdbf20a02018-05-10 19:59:01 +0000210 if (!OutIsStdout) fclose(Out);
211}
212
213extern "C" {
214
215void __sanitizer_cov_trace_pc_guard_init(uint32_t *start,
216 uint32_t *stop) {
217 assert(NumFuncs == 0 && "This tool does not support DSOs");
218 assert(start < stop && "The code is not instrumented for coverage");
219 if (start == stop || *start) return; // Initialize only once.
kccce6392a2019-05-08 00:51:15 +0000220 GuardsBeg = start;
221 GuardsEnd = stop;
kccdbf20a02018-05-10 19:59:01 +0000222}
223
224void __sanitizer_cov_pcs_init(const uintptr_t *pcs_beg,
225 const uintptr_t *pcs_end) {
kccce6392a2019-05-08 00:51:15 +0000226 if (NumGuards) return; // Initialize only once.
227 NumGuards = GuardsEnd - GuardsBeg;
228 PCsBeg = pcs_beg;
229 PCsEnd = pcs_end;
230 assert(NumGuards == (PCsEnd - PCsBeg) / 2);
231 for (size_t i = 0; i < NumGuards; i++) {
232 if (PCsBeg[i * 2 + 1] & PCFLAG_FUNC_ENTRY) {
233 NumFuncs++;
234 GuardsBeg[i] = NumFuncs;
235 }
236 }
237 FuncLabels = (dfsan_label*)calloc(NumFuncs, sizeof(dfsan_label));
238 BBExecuted = (bool*)calloc(NumGuards, sizeof(bool));
239 fprintf(stderr, "INFO: %zd instrumented function(s) observed "
240 "and %zd basic blocks\n", NumFuncs, NumGuards);
kccdbf20a02018-05-10 19:59:01 +0000241}
242
243void __sanitizer_cov_trace_pc_indir(uint64_t x){} // unused.
244
kccce6392a2019-05-08 00:51:15 +0000245void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
246 size_t GuardIdx = guard - GuardsBeg;
247 assert(GuardIdx < NumGuards);
248 BBExecuted[GuardIdx] = true;
249 if (!*guard) return; // not a function entry.
kccdbf20a02018-05-10 19:59:01 +0000250 uint32_t FuncNum = *guard - 1; // Guards start from 1.
251 assert(FuncNum < NumFuncs);
252 CurrentFunc = FuncNum;
253}
254
255void __dfsw___sanitizer_cov_trace_switch(uint64_t Val, uint64_t *Cases,
256 dfsan_label L1, dfsan_label UnusedL) {
257 assert(CurrentFunc < NumFuncs);
258 FuncLabels[CurrentFunc] = dfsan_union(FuncLabels[CurrentFunc], L1);
259}
260
261#define HOOK(Name, Type) \
262 void Name(Type Arg1, Type Arg2, dfsan_label L1, dfsan_label L2) { \
263 assert(CurrentFunc < NumFuncs); \
264 FuncLabels[CurrentFunc] = \
265 dfsan_union(FuncLabels[CurrentFunc], dfsan_union(L1, L2)); \
266 }
267
268HOOK(__dfsw___sanitizer_cov_trace_const_cmp1, uint8_t)
269HOOK(__dfsw___sanitizer_cov_trace_const_cmp2, uint16_t)
270HOOK(__dfsw___sanitizer_cov_trace_const_cmp4, uint32_t)
271HOOK(__dfsw___sanitizer_cov_trace_const_cmp8, uint64_t)
272HOOK(__dfsw___sanitizer_cov_trace_cmp1, uint8_t)
273HOOK(__dfsw___sanitizer_cov_trace_cmp2, uint16_t)
274HOOK(__dfsw___sanitizer_cov_trace_cmp4, uint32_t)
275HOOK(__dfsw___sanitizer_cov_trace_cmp8, uint64_t)
276
277} // extern "C"