1 /*
2 *
3 * honggfuzz - architecture dependent code (LINUX/PERF)
4 * -----------------------------------------
5 *
6 * Author: Robert Swiecki <swiecki@google.com>
7 *
8 * Copyright 2010-2018 by Google Inc. All Rights Reserved.
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License"); you may
11 * not use this file except in compliance with the License. You may obtain
12 * a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * Unless required by applicable law or agreed to in writing, software
17 * distributed under the License is distributed on an "AS IS" BASIS,
18 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
19 * implied. See the License for the specific language governing
20 * permissions and limitations under the License.
21 *
22 */
23
24 #include "perf.h"
25
26 #include <asm/mman.h>
27 #include <errno.h>
28 #include <fcntl.h>
29 #include <inttypes.h>
30 #include <linux/hw_breakpoint.h>
31 #include <linux/perf_event.h>
32 #include <linux/sysctl.h>
33 #include <signal.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <sys/ioctl.h>
37 #include <sys/mman.h>
38 #include <sys/poll.h>
39 #include <sys/ptrace.h>
40 #include <sys/syscall.h>
41 #include <unistd.h>
42
43 #include "libhfcommon/common.h"
44 #include "libhfcommon/files.h"
45 #include "libhfcommon/log.h"
46 #include "libhfcommon/util.h"
47 #include "pt.h"
48
49 #define _HF_PERF_MAP_SZ (1024 * 512)
50 #define _HF_PERF_AUX_SZ (1024 * 1024)
51 /* PERF_TYPE for Intel_PT/BTS -1 if none */
52 static int32_t perfIntelPtPerfType = -1;
53 static int32_t perfIntelBtsPerfType = -1;
54
55 #if defined(PERF_ATTR_SIZE_VER5)
arch_perfBtsCount(run_t * run)56 __attribute__((hot)) static inline void arch_perfBtsCount(run_t* run) {
57 struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->linux.perfMmapBuf;
58 struct bts_branch {
59 uint64_t from;
60 uint64_t to;
61 uint64_t misc;
62 };
63
64 uint64_t aux_head = ATOMIC_GET(pem->aux_head);
65 struct bts_branch* br = (struct bts_branch*)run->linux.perfMmapAux;
66 for (; br < ((struct bts_branch*)(run->linux.perfMmapAux + aux_head)); br++) {
67 /*
68 * Kernel sometimes reports branches from the kernel (iret), we are not interested in that
69 * as it makes the whole concept of unique branch counting less predictable
70 */
71 if (run->global->linux.kernelOnly == false &&
72 (__builtin_expect(br->from > 0xFFFFFFFF00000000, false) ||
73 __builtin_expect(br->to > 0xFFFFFFFF00000000, false))) {
74 LOG_D("Adding branch %#018" PRIx64 " - %#018" PRIx64, br->from, br->to);
75 continue;
76 }
77 if (br->from >= run->global->linux.dynamicCutOffAddr ||
78 br->to >= run->global->linux.dynamicCutOffAddr) {
79 continue;
80 }
81
82 register size_t pos = ((br->from << 12) ^ (br->to & 0xFFF));
83 pos &= _HF_PERF_BITMAP_BITSZ_MASK;
84 register uint8_t prev = ATOMIC_BTS(run->global->feedback.feedbackMap->bbMapPc, pos);
85 if (!prev) {
86 run->linux.hwCnts.newBBCnt++;
87 }
88 }
89 }
90 #endif /* defined(PERF_ATTR_SIZE_VER5) */
91
arch_perfMmapParse(run_t * run HF_ATTR_UNUSED)92 static inline void arch_perfMmapParse(run_t* run HF_ATTR_UNUSED) {
93 #if defined(PERF_ATTR_SIZE_VER5)
94 struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->linux.perfMmapBuf;
95 if (pem->aux_head == pem->aux_tail) {
96 return;
97 }
98 if (pem->aux_head < pem->aux_tail) {
99 LOG_F("The PERF AUX data has been overwritten. The AUX buffer is too small");
100 }
101 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
102 arch_perfBtsCount(run);
103 }
104 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
105 arch_ptAnalyze(run);
106 }
107 #endif /* defined(PERF_ATTR_SIZE_VER5) */
108 }
109
perf_event_open(struct perf_event_attr * hw_event,pid_t pid,int cpu,int group_fd,unsigned long flags)110 static long perf_event_open(
111 struct perf_event_attr* hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) {
112 return syscall(__NR_perf_event_open, hw_event, (uintptr_t)pid, (uintptr_t)cpu,
113 (uintptr_t)group_fd, (uintptr_t)flags);
114 }
115
arch_perfCreate(run_t * run,pid_t pid,dynFileMethod_t method,int * perfFd)116 static bool arch_perfCreate(run_t* run, pid_t pid, dynFileMethod_t method, int* perfFd) {
117 LOG_D("Enabling PERF for pid=%d method=%x", pid, method);
118
119 if (*perfFd != -1) {
120 LOG_F("The PERF FD is already initialized, possibly conflicting perf types enabled");
121 }
122
123 if ((method & _HF_DYNFILE_BTS_EDGE) && perfIntelBtsPerfType == -1) {
124 LOG_F("Intel BTS events (new type) are not supported on this platform");
125 }
126 if ((method & _HF_DYNFILE_IPT_BLOCK) && perfIntelPtPerfType == -1) {
127 LOG_F("Intel PT events are not supported on this platform");
128 }
129
130 struct perf_event_attr pe;
131 memset(&pe, 0, sizeof(struct perf_event_attr));
132 pe.size = sizeof(struct perf_event_attr);
133 if (run->global->linux.kernelOnly) {
134 pe.exclude_user = 1;
135 } else {
136 pe.exclude_kernel = 1;
137 }
138 pe.disabled = 1;
139 if (!run->global->exe.persistent) {
140 pe.enable_on_exec = 1;
141 }
142 pe.exclude_hv = 1;
143 pe.type = PERF_TYPE_HARDWARE;
144
145 switch (method) {
146 case _HF_DYNFILE_INSTR_COUNT:
147 LOG_D("Using: PERF_COUNT_HW_INSTRUCTIONS for pid=%d", (int)pid);
148 pe.config = PERF_COUNT_HW_INSTRUCTIONS;
149 pe.inherit = 1;
150 break;
151 case _HF_DYNFILE_BRANCH_COUNT:
152 LOG_D("Using: PERF_COUNT_HW_BRANCH_INSTRUCTIONS for pid=%d", (int)pid);
153 pe.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
154 pe.inherit = 1;
155 break;
156 case _HF_DYNFILE_BTS_EDGE:
157 LOG_D("Using: (Intel BTS) type=%" PRIu32 " for pid=%d", perfIntelBtsPerfType, (int)pid);
158 pe.type = perfIntelBtsPerfType;
159 break;
160 case _HF_DYNFILE_IPT_BLOCK:
161 LOG_D("Using: (Intel PT) type=%" PRIu32 " for pid=%d", perfIntelPtPerfType, (int)pid);
162 pe.type = perfIntelPtPerfType;
163 pe.config = RTIT_CTL_DISRETC;
164 break;
165 default:
166 LOG_E("Unknown perf mode: '%d' for pid=%d", method, (int)pid);
167 return false;
168 break;
169 }
170
171 #if !defined(PERF_FLAG_FD_CLOEXEC)
172 #define PERF_FLAG_FD_CLOEXEC 0
173 #endif
174 *perfFd = perf_event_open(&pe, pid, -1, -1, PERF_FLAG_FD_CLOEXEC);
175 if (*perfFd == -1) {
176 PLOG_E("perf_event_open() failed");
177 return false;
178 }
179
180 if (method != _HF_DYNFILE_BTS_EDGE && method != _HF_DYNFILE_IPT_BLOCK) {
181 return true;
182 }
183 #if defined(PERF_ATTR_SIZE_VER5)
184 if ((run->linux.perfMmapBuf = mmap(NULL, _HF_PERF_MAP_SZ + getpagesize(),
185 PROT_READ | PROT_WRITE, MAP_SHARED, *perfFd, 0)) == MAP_FAILED) {
186 run->linux.perfMmapBuf = NULL;
187 PLOG_W("mmap(mmapBuf) failed, sz=%zu, try increasing the kernel.perf_event_mlock_kb sysctl "
188 "(up to even 300000000)",
189 (size_t)_HF_PERF_MAP_SZ + getpagesize());
190 close(*perfFd);
191 *perfFd = -1;
192 return false;
193 }
194
195 struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->linux.perfMmapBuf;
196 pem->aux_offset = pem->data_offset + pem->data_size;
197 pem->aux_size = _HF_PERF_AUX_SZ;
198 if ((run->linux.perfMmapAux = mmap(
199 NULL, pem->aux_size, PROT_READ, MAP_SHARED, *perfFd, pem->aux_offset)) == MAP_FAILED) {
200 munmap(run->linux.perfMmapBuf, _HF_PERF_MAP_SZ + getpagesize());
201 run->linux.perfMmapBuf = NULL;
202 run->linux.perfMmapAux = NULL;
203 PLOG_W(
204 "mmap(mmapAuxBuf) failed, try increasing the kernel.perf_event_mlock_kb sysctl (up to "
205 "even 300000000)");
206 close(*perfFd);
207 *perfFd = -1;
208 return false;
209 }
210 #else /* defined(PERF_ATTR_SIZE_VER5) */
211 LOG_F("Your <linux/perf_event.h> includes are too old to support Intel PT/BTS");
212 #endif /* defined(PERF_ATTR_SIZE_VER5) */
213
214 return true;
215 }
216
arch_perfOpen(run_t * run)217 bool arch_perfOpen(run_t* run) {
218 if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
219 return true;
220 }
221
222 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) {
223 if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_INSTR_COUNT, &run->linux.cpuInstrFd)) {
224 LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_INSTR_COUNT)", (int)run->pid);
225 goto out;
226 }
227 }
228 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) {
229 if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_BRANCH_COUNT, &run->linux.cpuBranchFd)) {
230 LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_BRANCH_COUNT)", (int)run->pid);
231 goto out;
232 }
233 }
234 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
235 if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_BTS_EDGE, &run->linux.cpuIptBtsFd)) {
236 LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_BTS_EDGE)", (int)run->pid);
237 goto out;
238 }
239 }
240 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
241 if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_IPT_BLOCK, &run->linux.cpuIptBtsFd)) {
242 LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_IPT_BLOCK)", (int)run->pid);
243 goto out;
244 }
245 }
246
247 return true;
248
249 out:
250 close(run->linux.cpuInstrFd);
251 run->linux.cpuInstrFd = -1;
252 close(run->linux.cpuBranchFd);
253 run->linux.cpuBranchFd = -1;
254 close(run->linux.cpuIptBtsFd);
255 run->linux.cpuIptBtsFd = -1;
256
257 return false;
258 }
259
arch_perfClose(run_t * run)260 void arch_perfClose(run_t* run) {
261 if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
262 return;
263 }
264
265 if (run->linux.perfMmapAux != NULL) {
266 munmap(run->linux.perfMmapAux, _HF_PERF_AUX_SZ);
267 run->linux.perfMmapAux = NULL;
268 }
269 if (run->linux.perfMmapBuf != NULL) {
270 munmap(run->linux.perfMmapBuf, _HF_PERF_MAP_SZ + getpagesize());
271 run->linux.perfMmapBuf = NULL;
272 }
273
274 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) {
275 close(run->linux.cpuInstrFd);
276 run->linux.cpuInstrFd = -1;
277 }
278 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) {
279 close(run->linux.cpuBranchFd);
280 run->linux.cpuBranchFd = -1;
281 }
282 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
283 close(run->linux.cpuIptBtsFd);
284 run->linux.cpuIptBtsFd = -1;
285 }
286 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
287 close(run->linux.cpuIptBtsFd);
288 run->linux.cpuIptBtsFd = -1;
289 }
290 }
291
arch_perfEnable(run_t * run)292 bool arch_perfEnable(run_t* run) {
293 if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
294 return true;
295 }
296 /* It's enabled on exec in such scenario */
297 if (!run->global->exe.persistent) {
298 return true;
299 }
300
301 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) {
302 ioctl(run->linux.cpuInstrFd, PERF_EVENT_IOC_ENABLE, 0);
303 }
304 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) {
305 ioctl(run->linux.cpuBranchFd, PERF_EVENT_IOC_ENABLE, 0);
306 }
307 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
308 ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_ENABLE, 0);
309 }
310 if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
311 ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_ENABLE, 0);
312 }
313
314 return true;
315 }
316
arch_perfMmapReset(run_t * run)317 static void arch_perfMmapReset(run_t* run) {
318 /* smp_mb() required as per /usr/include/linux/perf_event.h */
319 wmb();
320
321 struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->linux.perfMmapBuf;
322 ATOMIC_SET(pem->data_head, 0);
323 ATOMIC_SET(pem->data_tail, 0);
324 #if defined(PERF_ATTR_SIZE_VER5)
325 ATOMIC_SET(pem->aux_head, 0);
326 ATOMIC_SET(pem->aux_tail, 0);
327 #endif /* defined(PERF_ATTR_SIZE_VER5) */
328 }
329
arch_perfAnalyze(run_t * run)330 void arch_perfAnalyze(run_t* run) {
331 if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
332 return;
333 }
334
335 uint64_t instrCount = 0;
336 if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) &&
337 run->linux.cpuInstrFd != -1) {
338 ioctl(run->linux.cpuInstrFd, PERF_EVENT_IOC_DISABLE, 0);
339 if (files_readFromFd(run->linux.cpuInstrFd, (uint8_t*)&instrCount, sizeof(instrCount)) !=
340 sizeof(instrCount)) {
341 PLOG_E("read(perfFd='%d') failed", run->linux.cpuInstrFd);
342 }
343 ioctl(run->linux.cpuInstrFd, PERF_EVENT_IOC_RESET, 0);
344 }
345
346 uint64_t branchCount = 0;
347 if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) &&
348 run->linux.cpuBranchFd != -1) {
349 ioctl(run->linux.cpuBranchFd, PERF_EVENT_IOC_DISABLE, 0);
350 if (files_readFromFd(run->linux.cpuBranchFd, (uint8_t*)&branchCount, sizeof(branchCount)) !=
351 sizeof(branchCount)) {
352 PLOG_E("read(perfFd='%d') failed", run->linux.cpuBranchFd);
353 }
354 ioctl(run->linux.cpuBranchFd, PERF_EVENT_IOC_RESET, 0);
355 }
356
357 if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) &&
358 run->linux.cpuIptBtsFd != -1) {
359 ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_DISABLE, 0);
360 arch_perfMmapParse(run);
361 arch_perfMmapReset(run);
362 ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_RESET, 0);
363 }
364 if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) &&
365 run->linux.cpuIptBtsFd != -1) {
366 ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_DISABLE, 0);
367 arch_perfMmapParse(run);
368 arch_perfMmapReset(run);
369 ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_RESET, 0);
370 }
371
372 run->linux.hwCnts.cpuInstrCnt = instrCount;
373 run->linux.hwCnts.cpuBranchCnt = branchCount;
374 }
375
arch_perfInit(honggfuzz_t * hfuzz HF_ATTR_UNUSED)376 bool arch_perfInit(honggfuzz_t* hfuzz HF_ATTR_UNUSED) {
377 static char const intel_pt_path[] = "/sys/bus/event_source/devices/intel_pt/type";
378 static char const intel_bts_path[] = "/sys/bus/event_source/devices/intel_bts/type";
379
380 if (files_exists(intel_pt_path)) {
381 uint8_t buf[256];
382 ssize_t sz = files_readFileToBufMax(intel_pt_path, buf, sizeof(buf) - 1);
383 if (sz > 0) {
384 buf[sz] = '\0';
385 perfIntelPtPerfType = (int32_t)strtoul((char*)buf, NULL, 10);
386 LOG_D("perfIntelPtPerfType = %" PRIu32, perfIntelPtPerfType);
387 }
388 }
389
390 if (files_exists(intel_bts_path)) {
391 uint8_t buf[256];
392 ssize_t sz = files_readFileToBufMax(intel_bts_path, buf, sizeof(buf) - 1);
393 if (sz > 0) {
394 buf[sz] = '\0';
395 perfIntelBtsPerfType = (int32_t)strtoul((char*)buf, NULL, 10);
396 LOG_D("perfIntelBtsPerfType = %" PRIu32, perfIntelBtsPerfType);
397 }
398 }
399
400 perf_ptInit();
401
402 return true;
403 }
404