-
Notifications
You must be signed in to change notification settings - Fork 110
/
afl-proxy.c
262 lines (220 loc) · 8.37 KB
/
afl-proxy.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
/*
* Copyright (c) 2017, University of California, Berkeley
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <sys/shm.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include "afl-proxy.h"
#define PID_MAX_LIMIT (1 << 22) // Portable upper-bound for value defined in linux/threads.h
/*
* Proxy between AFL and JQF. Communicates to
* both via pipes -- those launched by AFL and two whose names
* are given as command line args and must be created before
* launching the proxy.
*
* This proxy is general and can be used to exchange information
* from any external utility writing in the command-line pipes.
*
* author: Caroline Lemieux
* author: Rohan Padhye
*/
// whether to share an extra map of PERF_SIZE after the trace bits
// (this is unused when compiling with stock AFL)
int use_perf_map = 0;
// whether to log non-fatal (exit(1) causing messages)
// set to != 0 for debugging
int log_non_fatal = 1;
/*
* Log proxy's progress for debugging.
* to_exit: whether to exit(1) after logging
* log_file_name: name of file in which to log
* fmt... : format string parameters
*/
void log_to_file(int to_exit, char* log_file_name, char const *fmt, ...) {
/* If no log file name was provided, do not log */
if (log_file_name == NULL){
if (to_exit) exit(1);
else return;
}
/* if it's not an exit message and we don't log
non-fatal, do not log */
if (!to_exit & !log_non_fatal){
return;
}
/* Open the log file for logging if it is not yet open */
static FILE * log_file = NULL;
if (log_file == NULL) {
log_file = fopen(log_file_name, "w");
if (log_file < 0) {
printf("Couldn't open log file, %s\n", log_file_name);
exit(1);
}
}
/* print to log file */
va_list ap;
va_start(ap, fmt);
vfprintf(log_file, fmt, ap);
va_end(ap);
/* flush to log file since this program might terminate strangely */
fflush(log_file);
/* exit if the exit parameter is given */
if (to_exit) {
fclose(log_file);
exit(1);
}
}
/* main proxy driver. communication channel between a running instance
of AFL and Java */
int main(int argc, char** argv) {
/* usage */
if (argc < 3 || argc > 4){
printf("usage: %s afltojavafifo javatoaflfifo [logfile]", argv[0]);
exit(1);
}
/* collect file names from arguments */
char * to_java_str = argv[1];
char * from_java_str = argv[2];
char * log_file_name = NULL;
if (argc == 4) log_file_name = argv[3];
/* set up buffers */
u8 helo[4] = {'H', 'E', 'L', 'O'}; // to set up connections
uint32_t status = 0; // to receive + send status from java
u8 buf[4]; // to receive signals from AFL
u32 child_pid = PID_MAX_LIMIT + 1; // A PID that can never exist in practice
/* Run once? Used by afl-showmap and perhaps other tools to avoid looping. */
bool run_once = false;
/* temp variable to store communicated bytes */
int comm_bytes;
#ifdef PERF_SIZE
/* decide if perf_bits have to be shared */
if (getenv("JQF_PERF_MAP") != NULL) {
use_perf_map = 1;
}
#endif
/* set up FIFOs to talk to Java */
FILE * to_java_fd = fopen(to_java_str, "w");
if (to_java_fd == NULL){
log_to_file(1, log_file_name, "Failed to open to java fifo %s\n", to_java_str);
}
log_to_file(0, log_file_name, "opened to java fifo %s\n", to_java_str);
FILE * from_java_fd = fopen(from_java_str, "r");
if (from_java_fd == NULL){
log_to_file(1, log_file_name, "Failed to open from java fifo %s\n", from_java_str);
}
log_to_file(0, log_file_name, "opened from java fifo %s\n", from_java_str);
/* set up the trace bits */
char * shm_str = getenv(SHM_ENV_VAR);
if (shm_str == NULL){
log_to_file(1, log_file_name,
"Error getting the address of trace_bits from env var %s\n", shm_str);
}
int shm_id = atoi(shm_str);
u8* trace_bits = shmat(shm_id, NULL, 0);
if (trace_bits < 0){
log_to_file(1, log_file_name, "Error shmat()ing trace_bits from id %d\n", shm_id);
}
/* perf map is right after coverage bit map (unused with stock AFL) */
u32* perf_bits = (u32*) &trace_bits[MAP_SIZE];
/* say the first hello to AFL. use write() because we
have an int file descriptor */
if (write(FORKSRV_FD + 1, (void*) &helo, 4) < 4) {
log_to_file(0, log_file_name, "Error saying initial hello to AFL\n");
run_once = true;
} else {
log_to_file(0, log_file_name, "Said hello to AFL (init).\n");
}
/* main fuzzing loop. AFL sends ready signals through
pipe with file descriptor FORKSRV_FD */
while (run_once || read(FORKSRV_FD,(void *)&buf, 4) == 4){
if (!run_once) {
/* this sends "child pid" to AFL -- we use an impossible PID
* that AFL cannot kill even in the presence of timeouts. */
if ((comm_bytes = write(FORKSRV_FD+1, &child_pid, 4)) < 4) {
log_to_file(1, log_file_name,
"Something went wrong saying hello to AFL in loop: wrote %d bytes.\n", comm_bytes);
}
log_to_file(0, log_file_name, "Said hello to AFL (in loop).\n");
}
/* Say hello to Java */
if ((comm_bytes = fwrite(&helo, 1, 4, to_java_fd)) < 4) {
log_to_file(1, log_file_name,
"Something went wrong saying hello to Java: wrote %d bytes.\n", comm_bytes);
}
/* need to flush the buffer */
fflush(to_java_fd);
log_to_file(0, log_file_name, "Said hello to Java.\n");
/* Get return code from Java */
if ((comm_bytes = fread((void *) &status, 1 , 4, from_java_fd)) < 4) {
log_to_file(1, log_file_name,
"Something went wrong getting return status from Java: read %d bytes.\n", comm_bytes);
}
log_to_file(0, log_file_name, "Got return status from Java.\n");
/* Get trace bits from Java */
if ((comm_bytes = fread( trace_bits, 1, MAP_SIZE, from_java_fd)) < MAP_SIZE) {
log_to_file(1, log_file_name,
"Something went wrong getting trace_bits from Java: read %d bytes.\n", comm_bytes);
}
log_to_file(0, log_file_name, "Got trace bits from java.\n");
#ifdef PERF_SIZE
if (use_perf_map) {
/* Get perf bits from Java */
if ((comm_bytes = fread( perf_bits, 4, PERF_SIZE, from_java_fd)) < PERF_SIZE) {
log_to_file(1, log_file_name,
"Something went wrong getting perf_bits from Java: read %d bytes.\n", comm_bytes);
}
log_to_file(0, log_file_name, "Got perf bits from java.\n");
}
#endif
if (!run_once) {
/* Tell AFL we got the return */
if((comm_bytes = write(FORKSRV_FD + 1, &status, 4)) < 4) {
log_to_file(1, log_file_name,
"Something went wrong sending return status to AFL: wrote %d bytes.\n", comm_bytes);
}
log_to_file(0, log_file_name, "sent return status to AFL.\n");
} else {
break; // run_once
}
}
/* teardown. Will probably never be called */
if (fclose(to_java_fd) != 0) {
log_to_file(1, log_file_name,
"Something went wrong closing pipe to Java.\n");
}
if (fclose(from_java_fd) != 0) {
log_to_file(1, log_file_name,
"Something went wrong closing pipe from Java.\n");
}
exit(status);
}