• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         PLOG_W(
203             "mmap(mmapAuxBuf) failed, try increasing the kernel.perf_event_mlock_kb sysctl (up to "
204             "even 300000000)");
205         close(*perfFd);
206         *perfFd = -1;
207         return false;
208     }
209 #else  /* defined(PERF_ATTR_SIZE_VER5) */
210     LOG_F("Your <linux/perf_event.h> includes are too old to support Intel PT/BTS");
211 #endif /* defined(PERF_ATTR_SIZE_VER5) */
212 
213     return true;
214 }
215 
arch_perfOpen(run_t * run)216 bool arch_perfOpen(run_t* run) {
217     if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
218         return true;
219     }
220 
221     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) {
222         if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_INSTR_COUNT, &run->linux.cpuInstrFd)) {
223             LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_INSTR_COUNT)", (int)run->pid);
224             goto out;
225         }
226     }
227     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) {
228         if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_BRANCH_COUNT, &run->linux.cpuBranchFd)) {
229             LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_BRANCH_COUNT)", (int)run->pid);
230             goto out;
231         }
232     }
233     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
234         if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_BTS_EDGE, &run->linux.cpuIptBtsFd)) {
235             LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_BTS_EDGE)", (int)run->pid);
236             goto out;
237         }
238     }
239     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
240         if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_IPT_BLOCK, &run->linux.cpuIptBtsFd)) {
241             LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_IPT_BLOCK)", (int)run->pid);
242             goto out;
243         }
244     }
245 
246     return true;
247 
248 out:
249     close(run->linux.cpuInstrFd);
250     run->linux.cpuInstrFd = -1;
251     close(run->linux.cpuBranchFd);
252     run->linux.cpuBranchFd = -1;
253     close(run->linux.cpuIptBtsFd);
254     run->linux.cpuIptBtsFd = 1;
255 
256     return false;
257 }
258 
arch_perfClose(run_t * run)259 void arch_perfClose(run_t* run) {
260     if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
261         return;
262     }
263 
264     if (run->linux.perfMmapAux != NULL) {
265         munmap(run->linux.perfMmapAux, _HF_PERF_AUX_SZ);
266         run->linux.perfMmapAux = NULL;
267     }
268     if (run->linux.perfMmapBuf != NULL) {
269         munmap(run->linux.perfMmapBuf, _HF_PERF_MAP_SZ + getpagesize());
270         run->linux.perfMmapBuf = NULL;
271     }
272 
273     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) {
274         close(run->linux.cpuInstrFd);
275         run->linux.cpuInstrFd = -1;
276     }
277     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) {
278         close(run->linux.cpuBranchFd);
279         run->linux.cpuBranchFd = -1;
280     }
281     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
282         close(run->linux.cpuIptBtsFd);
283         run->linux.cpuIptBtsFd = -1;
284     }
285     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
286         close(run->linux.cpuIptBtsFd);
287         run->linux.cpuIptBtsFd = -1;
288     }
289 }
290 
arch_perfEnable(run_t * run)291 bool arch_perfEnable(run_t* run) {
292     if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
293         return true;
294     }
295     /* It's enabled on exec in such scenario */
296     if (!run->global->exe.persistent) {
297         return true;
298     }
299 
300     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) {
301         ioctl(run->linux.cpuInstrFd, PERF_EVENT_IOC_ENABLE, 0);
302     }
303     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) {
304         ioctl(run->linux.cpuBranchFd, PERF_EVENT_IOC_ENABLE, 0);
305     }
306     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
307         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_ENABLE, 0);
308     }
309     if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
310         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_ENABLE, 0);
311     }
312 
313     return true;
314 }
315 
arch_perfMmapReset(run_t * run)316 static void arch_perfMmapReset(run_t* run) {
317     /* smp_mb() required as per /usr/include/linux/perf_event.h */
318     wmb();
319 
320     struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->linux.perfMmapBuf;
321     ATOMIC_SET(pem->data_head, 0);
322     ATOMIC_SET(pem->data_tail, 0);
323 #if defined(PERF_ATTR_SIZE_VER5)
324     ATOMIC_SET(pem->aux_head, 0);
325     ATOMIC_SET(pem->aux_tail, 0);
326 #endif /* defined(PERF_ATTR_SIZE_VER5) */
327 }
328 
arch_perfAnalyze(run_t * run)329 void arch_perfAnalyze(run_t* run) {
330     if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
331         return;
332     }
333 
334     uint64_t instrCount = 0;
335     if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_INSTR_COUNT) &&
336         run->linux.cpuInstrFd != -1) {
337         ioctl(run->linux.cpuInstrFd, PERF_EVENT_IOC_DISABLE, 0);
338         if (files_readFromFd(run->linux.cpuInstrFd, (uint8_t*)&instrCount, sizeof(instrCount)) !=
339             sizeof(instrCount)) {
340             PLOG_E("read(perfFd='%d') failed", run->linux.cpuInstrFd);
341         }
342         ioctl(run->linux.cpuInstrFd, PERF_EVENT_IOC_RESET, 0);
343     }
344 
345     uint64_t branchCount = 0;
346     if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_BRANCH_COUNT) &&
347         run->linux.cpuBranchFd != -1) {
348         ioctl(run->linux.cpuBranchFd, PERF_EVENT_IOC_DISABLE, 0);
349         if (files_readFromFd(run->linux.cpuBranchFd, (uint8_t*)&branchCount, sizeof(branchCount)) !=
350             sizeof(branchCount)) {
351             PLOG_E("read(perfFd='%d') failed", run->linux.cpuBranchFd);
352         }
353         ioctl(run->linux.cpuBranchFd, PERF_EVENT_IOC_RESET, 0);
354     }
355 
356     if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) &&
357         run->linux.cpuIptBtsFd != -1) {
358         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_DISABLE, 0);
359         arch_perfMmapParse(run);
360         arch_perfMmapReset(run);
361         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_RESET, 0);
362     }
363     if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) &&
364         run->linux.cpuIptBtsFd != -1) {
365         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_DISABLE, 0);
366         arch_perfMmapParse(run);
367         arch_perfMmapReset(run);
368         ioctl(run->linux.cpuIptBtsFd, PERF_EVENT_IOC_RESET, 0);
369     }
370 
371     run->linux.hwCnts.cpuInstrCnt = instrCount;
372     run->linux.hwCnts.cpuBranchCnt = branchCount;
373 }
374 
arch_perfInit(honggfuzz_t * hfuzz HF_ATTR_UNUSED)375 bool arch_perfInit(honggfuzz_t* hfuzz HF_ATTR_UNUSED) {
376     static char const intel_pt_path[] = "/sys/bus/event_source/devices/intel_pt/type";
377     static char const intel_bts_path[] = "/sys/bus/event_source/devices/intel_bts/type";
378 
379     if (files_exists(intel_pt_path)) {
380         uint8_t buf[256];
381         ssize_t sz = files_readFileToBufMax(intel_pt_path, buf, sizeof(buf) - 1);
382         if (sz > 0) {
383             buf[sz] = '\0';
384             perfIntelPtPerfType = (int32_t)strtoul((char*)buf, NULL, 10);
385             LOG_D("perfIntelPtPerfType = %" PRIu32, perfIntelPtPerfType);
386         }
387     }
388 
389     if (files_exists(intel_bts_path)) {
390         uint8_t buf[256];
391         ssize_t sz = files_readFileToBufMax(intel_bts_path, buf, sizeof(buf) - 1);
392         if (sz > 0) {
393             buf[sz] = '\0';
394             perfIntelBtsPerfType = (int32_t)strtoul((char*)buf, NULL, 10);
395             LOG_D("perfIntelBtsPerfType = %" PRIu32, perfIntelBtsPerfType);
396         }
397     }
398 
399     perf_ptInit();
400 
401     return true;
402 }
403