/*
|
*
|
* honggfuzz - Intel PT decoder
|
* -----------------------------------------
|
*
|
* Author: Robert Swiecki <swiecki@google.com>
|
*
|
* Copyright 2010-2018 by Google Inc. All Rights Reserved.
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may
|
* not use this file except in compliance with the License. You may obtain
|
* a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
* implied. See the License for the specific language governing
|
* permissions and limitations under the License.
|
*
|
*/
|
|
#include "libhfcommon/common.h"
|
|
#include <inttypes.h>
|
#include <linux/perf_event.h>
|
#include <stdio.h>
|
|
#include "libhfcommon/log.h"
|
#include "libhfcommon/util.h"
|
#include "pt.h"
|
|
#ifdef _HF_LINUX_INTEL_PT_LIB
|
|
#include <intel-pt.h>
|
|
struct pt_cpu ptCpu = {
|
.vendor = pcv_unknown,
|
.family = 0,
|
.model = 0,
|
.stepping = 0,
|
};
|
|
void perf_ptInit(void) {
|
FILE* f = fopen("/proc/cpuinfo", "rb");
|
if (!f) {
|
PLOG_E("Couldn't open '/proc/cpuinfo'");
|
return;
|
}
|
for (;;) {
|
char k[1024], t[1024], v[1024];
|
int ret = fscanf(f, "%1023[^\t]%1023[\t]: %1023[^\n]\n", k, t, v);
|
if (ret == EOF) {
|
break;
|
}
|
if (ret != 3) {
|
break;
|
}
|
if (strcmp(k, "vendor_id") == 0) {
|
if (strcmp(v, "GenuineIntel") == 0) {
|
ptCpu.vendor = pcv_intel;
|
LOG_D("IntelPT vendor: Intel");
|
} else {
|
ptCpu.vendor = pcv_unknown;
|
LOG_D("Current processor is not Intel, IntelPT will not work");
|
}
|
}
|
if (strcmp(k, "cpu family") == 0) {
|
ptCpu.family = atoi(v);
|
LOG_D("IntelPT family: %" PRIu16, ptCpu.family);
|
}
|
if (strcmp(k, "model") == 0) {
|
ptCpu.model = atoi(v);
|
LOG_D("IntelPT model: %" PRIu8, ptCpu.model);
|
}
|
if (strcmp(k, "stepping") == 0) {
|
ptCpu.stepping = atoi(v);
|
LOG_D("IntelPT stepping: %" PRIu8, ptCpu.stepping);
|
}
|
}
|
fclose(f);
|
}
|
|
/* Sign-extend a uint64_t value. */
|
inline static uint64_t sext(uint64_t val, uint8_t sign) {
|
uint64_t signbit, mask;
|
|
signbit = 1ull << (sign - 1);
|
mask = ~0ull << sign;
|
|
return val & signbit ? val | mask : val & ~mask;
|
}
|
|
__attribute__((hot)) inline static void perf_ptAnalyzePkt(run_t* run, struct pt_packet* packet) {
|
if (packet->type != ppt_tip) {
|
return;
|
}
|
|
uint64_t ip;
|
switch (packet->payload.ip.ipc) {
|
case pt_ipc_update_16:
|
ip = packet->payload.ip.ip & 0xFFFF;
|
break;
|
case pt_ipc_update_32:
|
ip = packet->payload.ip.ip & 0xFFFFFFFF;
|
break;
|
case pt_ipc_update_48:
|
ip = packet->payload.ip.ip & 0xFFFFFFFFFFFF;
|
break;
|
case pt_ipc_sext_48:
|
ip = sext(packet->payload.ip.ip, 48);
|
break;
|
case pt_ipc_full:
|
ip = packet->payload.ip.ip;
|
break;
|
default:
|
return;
|
}
|
|
if (ip >= run->global->linux.dynamicCutOffAddr) {
|
return;
|
}
|
|
ip &= _HF_PERF_BITMAP_BITSZ_MASK;
|
register uint8_t prev = ATOMIC_BTS(run->global->feedback.feedbackMap->bbMapPc, ip);
|
if (!prev) {
|
run->linux.hwCnts.newBBCnt++;
|
}
|
return;
|
}
|
|
void arch_ptAnalyze(run_t* run) {
|
struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->linux.perfMmapBuf;
|
|
uint64_t aux_tail = ATOMIC_GET(pem->aux_tail);
|
uint64_t aux_head = ATOMIC_GET(pem->aux_head);
|
|
/* smp_rmb() required as per /usr/include/linux/perf_event.h */
|
rmb();
|
|
struct pt_config ptc;
|
pt_config_init(&ptc);
|
ptc.begin = &run->linux.perfMmapAux[aux_tail];
|
ptc.end = &run->linux.perfMmapAux[aux_head];
|
ptc.cpu = ptCpu;
|
|
int errcode = pt_cpu_errata(&ptc.errata, &ptc.cpu);
|
if (errcode < 0) {
|
LOG_F("pt_errata() failed: %s", pt_errstr(-errcode));
|
}
|
|
struct pt_packet_decoder* ptd = pt_pkt_alloc_decoder(&ptc);
|
if (ptd == NULL) {
|
LOG_F("pt_pkt_alloc_decoder() failed");
|
}
|
defer {
|
pt_pkt_free_decoder(ptd);
|
};
|
|
errcode = pt_pkt_sync_forward(ptd);
|
if (errcode < 0) {
|
LOG_W("pt_pkt_sync_forward() failed: %s", pt_errstr(-errcode));
|
return;
|
}
|
|
for (;;) {
|
struct pt_packet packet;
|
errcode = pt_pkt_next(ptd, &packet, sizeof(packet));
|
if (errcode == -pte_eos) {
|
break;
|
}
|
if (errcode < 0) {
|
LOG_W("pt_pkt_next() failed: %s", pt_errstr(-errcode));
|
break;
|
}
|
perf_ptAnalyzePkt(run, &packet);
|
}
|
}
|
|
#else /* _HF_LINUX_INTEL_PT_LIB */
|
|
void perf_ptInit(void) {
|
return;
|
}
|
|
void arch_ptAnalyze(run_t* fuzzer HF_ATTR_UNUSED) {
|
LOG_F(
|
"The program has not been linked against the Intel's Processor Trace Library (libipt.so)");
|
}
|
|
#endif /* _HF_LINUX_INTEL_PT_LIB */
|