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) 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 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 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 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 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 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 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 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