1 /* 2 * 3 * honggfuzz - architecture dependent code (LINUX/PERF) 4 * ----------------------------------------- 5 * 6 * Author: Robert Swiecki <swiecki (at) 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) 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 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 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 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 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 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 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 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 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 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