1 /*
2 * This implements rpc.statsd support, see docs/rpc_statsd.md
3 *
4 */
5
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <sys/socket.h>
9 #include <arpa/inet.h>
10 #include <string.h>
11 #include <sys/types.h>
12 #include <netdb.h>
13 #include <unistd.h>
14 #include "afl-fuzz.h"
15
16 #define MAX_STATSD_PACKET_SIZE 4096
17 #define MAX_TAG_LEN 200
18 #define METRIC_PREFIX "fuzzing"
19
20 /* Tags format for metrics
21 DogStatsD:
22 metric.name:<value>|<type>|#key:value,key2:value2
23
24 InfluxDB
25 metric.name,key=value,key2=value2:<value>|<type>
26
27 Librato
28 metric.name#key=value,key2=value2:<value>|<type>
29
30 SignalFX
31 metric.name[key=value,key2=value2]:<value>|<type>
32
33 */
34
35 // after the whole metric.
36 #define DOGSTATSD_TAGS_FORMAT "|#banner:%s,afl_version:%s"
37
38 // just after the metric name.
39 #define LIBRATO_TAGS_FORMAT "#banner=%s,afl_version=%s"
40 #define INFLUXDB_TAGS_FORMAT ",banner=%s,afl_version=%s"
41 #define SIGNALFX_TAGS_FORMAT "[banner=%s,afl_version=%s]"
42
43 // For DogstatsD
44 #define STATSD_TAGS_TYPE_SUFFIX 1
45 #define STATSD_TAGS_SUFFIX_METRICS \
46 METRIC_PREFIX \
47 ".cycle_done:%llu|g%s\n" METRIC_PREFIX \
48 ".cycles_wo_finds:%llu|g%s\n" METRIC_PREFIX \
49 ".execs_done:%llu|g%s\n" METRIC_PREFIX \
50 ".execs_per_sec:%0.02f|g%s\n" METRIC_PREFIX \
51 ".corpus_count:%u|g%s\n" METRIC_PREFIX \
52 ".corpus_favored:%u|g%s\n" METRIC_PREFIX \
53 ".corpus_found:%u|g%s\n" METRIC_PREFIX \
54 ".corpus_imported:%u|g%s\n" METRIC_PREFIX \
55 ".max_depth:%u|g%s\n" METRIC_PREFIX ".cur_item:%u|g%s\n" METRIC_PREFIX \
56 ".pending_favs:%u|g%s\n" METRIC_PREFIX \
57 ".pending_total:%u|g%s\n" METRIC_PREFIX \
58 ".corpus_variable:%u|g%s\n" METRIC_PREFIX \
59 ".saved_crashes:%llu|g%s\n" METRIC_PREFIX \
60 ".saved_hangs:%llu|g%s\n" METRIC_PREFIX \
61 ".total_crashes:%llu|g%s\n" METRIC_PREFIX \
62 ".slowest_exec_ms:%u|g%s\n" METRIC_PREFIX \
63 ".edges_found:%u|g%s\n" METRIC_PREFIX \
64 ".var_byte_count:%u|g%s\n" METRIC_PREFIX ".havoc_expansion:%u|g%s\n"
65
66 // For Librato, InfluxDB, SignalFX
67 #define STATSD_TAGS_TYPE_MID 2
68 #define STATSD_TAGS_MID_METRICS \
69 METRIC_PREFIX \
70 ".cycle_done%s:%llu|g\n" METRIC_PREFIX \
71 ".cycles_wo_finds%s:%llu|g\n" METRIC_PREFIX \
72 ".execs_done%s:%llu|g\n" METRIC_PREFIX \
73 ".execs_per_sec%s:%0.02f|g\n" METRIC_PREFIX \
74 ".corpus_count%s:%u|g\n" METRIC_PREFIX \
75 ".corpus_favored%s:%u|g\n" METRIC_PREFIX \
76 ".corpus_found%s:%u|g\n" METRIC_PREFIX \
77 ".corpus_imported%s:%u|g\n" METRIC_PREFIX \
78 ".max_depth%s:%u|g\n" METRIC_PREFIX ".cur_item%s:%u|g\n" METRIC_PREFIX \
79 ".pending_favs%s:%u|g\n" METRIC_PREFIX \
80 ".pending_total%s:%u|g\n" METRIC_PREFIX \
81 ".corpus_variable%s:%u|g\n" METRIC_PREFIX \
82 ".saved_crashes%s:%llu|g\n" METRIC_PREFIX \
83 ".saved_hangs%s:%llu|g\n" METRIC_PREFIX \
84 ".total_crashes%s:%llu|g\n" METRIC_PREFIX \
85 ".slowest_exec_ms%s:%u|g\n" METRIC_PREFIX \
86 ".edges_found%s:%u|g\n" METRIC_PREFIX \
87 ".var_byte_count%s:%u|g\n" METRIC_PREFIX ".havoc_expansion%s:%u|g\n"
88
statsd_setup_format(afl_state_t * afl)89 void statsd_setup_format(afl_state_t *afl) {
90
91 if (afl->afl_env.afl_statsd_tags_flavor &&
92 strcmp(afl->afl_env.afl_statsd_tags_flavor, "dogstatsd") == 0) {
93
94 afl->statsd_tags_format = DOGSTATSD_TAGS_FORMAT;
95 afl->statsd_metric_format = STATSD_TAGS_SUFFIX_METRICS;
96 afl->statsd_metric_format_type = STATSD_TAGS_TYPE_SUFFIX;
97
98 } else if (afl->afl_env.afl_statsd_tags_flavor &&
99
100 strcmp(afl->afl_env.afl_statsd_tags_flavor, "librato") == 0) {
101
102 afl->statsd_tags_format = LIBRATO_TAGS_FORMAT;
103 afl->statsd_metric_format = STATSD_TAGS_MID_METRICS;
104 afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID;
105
106 } else if (afl->afl_env.afl_statsd_tags_flavor &&
107
108 strcmp(afl->afl_env.afl_statsd_tags_flavor, "influxdb") == 0) {
109
110 afl->statsd_tags_format = INFLUXDB_TAGS_FORMAT;
111 afl->statsd_metric_format = STATSD_TAGS_MID_METRICS;
112 afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID;
113
114 } else if (afl->afl_env.afl_statsd_tags_flavor &&
115
116 strcmp(afl->afl_env.afl_statsd_tags_flavor, "signalfx") == 0) {
117
118 afl->statsd_tags_format = SIGNALFX_TAGS_FORMAT;
119 afl->statsd_metric_format = STATSD_TAGS_MID_METRICS;
120 afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID;
121
122 } else {
123
124 // No tags at all.
125 afl->statsd_tags_format = "";
126 // Still need to pick a format. Doesn't change anything since if will be
127 // replaced by the empty string anyway.
128 afl->statsd_metric_format = STATSD_TAGS_MID_METRICS;
129 afl->statsd_metric_format_type = STATSD_TAGS_TYPE_MID;
130
131 }
132
133 }
134
statsd_socket_init(afl_state_t * afl)135 int statsd_socket_init(afl_state_t *afl) {
136
137 /* Default port and host.
138 Will be overwritten by AFL_STATSD_PORT and AFL_STATSD_HOST environment
139 variable, if they exists.
140 */
141 u16 port = STATSD_DEFAULT_PORT;
142 char *host = STATSD_DEFAULT_HOST;
143
144 if (afl->afl_env.afl_statsd_port) {
145
146 port = atoi(afl->afl_env.afl_statsd_port);
147
148 }
149
150 if (afl->afl_env.afl_statsd_host) { host = afl->afl_env.afl_statsd_host; }
151
152 int sock;
153 if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {
154
155 FATAL("Failed to create socket");
156
157 }
158
159 memset(&afl->statsd_server, 0, sizeof(afl->statsd_server));
160 afl->statsd_server.sin_family = AF_INET;
161 afl->statsd_server.sin_port = htons(port);
162
163 struct addrinfo *result;
164 struct addrinfo hints;
165
166 memset(&hints, 0, sizeof(struct addrinfo));
167 hints.ai_family = AF_INET;
168 hints.ai_socktype = SOCK_DGRAM;
169
170 if ((getaddrinfo(host, NULL, &hints, &result))) {
171
172 FATAL("Fail to getaddrinfo");
173
174 }
175
176 memcpy(&(afl->statsd_server.sin_addr),
177 &((struct sockaddr_in *)result->ai_addr)->sin_addr,
178 sizeof(struct in_addr));
179 freeaddrinfo(result);
180
181 return sock;
182
183 }
184
statsd_send_metric(afl_state_t * afl)185 int statsd_send_metric(afl_state_t *afl) {
186
187 char buff[MAX_STATSD_PACKET_SIZE] = {0};
188
189 /* afl->statsd_sock is set once in the initialisation of afl-fuzz and reused
190 each time If the sendto later fail, we reset it to 0 to be able to recreates
191 it.
192 */
193 if (!afl->statsd_sock) {
194
195 afl->statsd_sock = statsd_socket_init(afl);
196 if (!afl->statsd_sock) {
197
198 WARNF("Cannot create socket");
199 return -1;
200
201 }
202
203 }
204
205 statsd_format_metric(afl, buff, MAX_STATSD_PACKET_SIZE);
206 if (sendto(afl->statsd_sock, buff, strlen(buff), 0,
207 (struct sockaddr *)&afl->statsd_server,
208 sizeof(afl->statsd_server)) == -1) {
209
210 if (!close(afl->statsd_sock)) { PFATAL("Cannot close socket"); }
211 afl->statsd_sock = 0;
212 WARNF("Cannot sendto");
213 return -1;
214
215 }
216
217 return 0;
218
219 }
220
statsd_format_metric(afl_state_t * afl,char * buff,size_t bufflen)221 int statsd_format_metric(afl_state_t *afl, char *buff, size_t bufflen) {
222
223 char tags[MAX_TAG_LEN * 2] = {0};
224 if (afl->statsd_tags_format) {
225
226 snprintf(tags, MAX_TAG_LEN * 2, afl->statsd_tags_format, afl->sync_id,
227 VERSION);
228
229 }
230
231 /* Sends multiple metrics with one UDP Packet.
232 bufflen will limit to the max safe size.
233 */
234 if (afl->statsd_metric_format_type == STATSD_TAGS_TYPE_SUFFIX) {
235
236 snprintf(
237 buff, bufflen, afl->statsd_metric_format,
238 afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags,
239 afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags,
240 afl->fsrv.total_execs /
241 ((double)(get_cur_time() + afl->prev_run_time - afl->start_time) /
242 1000),
243 tags, afl->queued_items, tags, afl->queued_favored, tags,
244 afl->queued_discovered, tags, afl->queued_imported, tags,
245 afl->max_depth, tags, afl->current_entry, tags, afl->pending_favored,
246 tags, afl->pending_not_fuzzed, tags, afl->queued_variable, tags,
247 afl->saved_crashes, tags, afl->saved_hangs, tags, afl->total_crashes,
248 tags, afl->slowest_exec_ms, tags,
249 count_non_255_bytes(afl, afl->virgin_bits), tags, afl->var_byte_count,
250 tags, afl->expand_havoc, tags);
251
252 } else if (afl->statsd_metric_format_type == STATSD_TAGS_TYPE_MID) {
253
254 snprintf(
255 buff, bufflen, afl->statsd_metric_format, tags,
256 afl->queue_cycle ? (afl->queue_cycle - 1) : 0, tags,
257 afl->cycles_wo_finds, tags, afl->fsrv.total_execs, tags,
258 afl->fsrv.total_execs /
259 ((double)(get_cur_time() + afl->prev_run_time - afl->start_time) /
260 1000),
261 tags, afl->queued_items, tags, afl->queued_favored, tags,
262 afl->queued_discovered, tags, afl->queued_imported, tags,
263 afl->max_depth, tags, afl->current_entry, tags, afl->pending_favored,
264 tags, afl->pending_not_fuzzed, tags, afl->queued_variable, tags,
265 afl->saved_crashes, tags, afl->saved_hangs, tags, afl->total_crashes,
266 tags, afl->slowest_exec_ms, tags,
267 count_non_255_bytes(afl, afl->virgin_bits), tags, afl->var_byte_count,
268 tags, afl->expand_havoc);
269
270 }
271
272 return 0;
273
274 }
275
276