Honggfuzz -- Intel PT源码分析

2024-03-04 1213阅读

温馨提示:这篇文章已超过384天没有更新,请注意相关的内容是否还可用!

文章目录

  • 前言
  • 一、简介
  • 二、Feedback-driven fuzzing
  • 三、源码解读
    • 3.1 arch_archInit
    • 3.2 fuzz_fuzzLoop
    • 3.3 arch_perfAnalyze
    • 3.4 arch_perfMmapParse
    • 3.5 arch_ptAnalyze
    • 3.6 perf_ptAnalyzePkt
    • 四、perf采集过程
    • 五、Intel BTS分析
    • 参考资料

      前言

      最近看到Honggfuzz工具中使用到了Intel PT的功能,便研究了一下其源码。

      一、简介

      Honggfuzz是一个面向安全的、以反馈驱动和演化为特点的易于使用的模糊测试工具。它旨在通过模糊测试的方式发现软件中的漏洞,具有以下特点:

      (1)反馈驱动:Honggfuzz采用反馈驱动的模糊测试技术,监视目标程序的执行并收集覆盖信息。利用这些反馈信息指导突变过程,并优先选择能够探索新的或未覆盖的程序路径的输入,提高发现漏洞的机会。

      (2)演化式模糊测试:Honggfuzz采用演化式的方式提高模糊测试的效果。它会随着时间的推移不断进化和突变输入数据,逐渐优化测试用例并探索不同的执行路径。这种迭代的过程有助于发现更深层次的漏洞和边界情况,这些问题可能无法在传统的模糊测试方法中发现。

      (3)安全导向:Honggfuzz专注于安全测试和漏洞发现。它着重于发现内存损坏问题、输入验证漏洞和其他可能被攻击者利用的软件漏洞。

      请参考:https://github.com/google/honggfuzz

      Honggfuzz是支持多种硬件级别特性的模糊测试工具,其中包括其他基于覆盖率的反馈驱动模糊测试工具所不具备的功能。主要支持的硬件特性包括:

      (1)CPU分支/指令计数:Honggfuzz可以利用CPU级别的分支和指令计数功能来监视目标程序的执行路径。这种技术可以提供更详细的覆盖信息,帮助指导模糊测试过程,并优先选择探索新的程序路径。

      (2)Intel BTS(Branch Trace Store):Honggfuzz支持使用Intel BTS技术进行模糊测试。BTS是Intel处理器上的一种硬件特性,用于捕获程序执行期间的分支信息。通过使用BTS,Honggfuzz可以更准确地了解程序的执行路径,并生成更有效的模糊输入。

      (3)Intel PT(Processor Trace):Honggfuzz还支持使用Intel PT技术进行模糊测试。PT是一种硬件级别的追踪功能,可以捕获程序执行期间的指令级别跟踪信息。借助Intel PT,Honggfuzz可以更全面地了解程序的执行过程,并生成更有针对性的模糊输入。

      二、Feedback-driven fuzzing

      Honggfuzz是一种支持反馈驱动(以代码覆盖率为导向)的模糊测试工具。它可以利用以下数据源进行模糊测试:

      (1)(Linux)基于硬件的计数器(指令、分支):Honggfuzz可以利用CPU的硬件计数器来监测目标程序的执行路径。它可以跟踪指令和分支的执行次数,从而提供代码覆盖率的信息。

      (2)(Linux)Intel BTS代码覆盖率(内核版本>=4.2):Honggfuzz支持使用Intel BTS技术进行代码覆盖率的监测。Intel BTS是一种硬件特性,可以捕获程序执行期间的分支信息,从而提供更详细的代码覆盖率数据。

      (3)(Linux)Intel PT代码覆盖率(内核版本>=4.2):Honggfuzz还支持使用Intel PT技术进行代码覆盖率的监测。Intel PT是一种硬件级别的追踪功能,可以捕获程序执行期间的指令级别跟踪信息,进而提供更全面的代码覆盖率信息。

      (4) Sanitizer-coverage工具的插桩(-fsanitize-coverage=bb):Honggfuzz还支持使用Sanitizer-coverage工具进行插桩,以收集代码覆盖率数据。通过在程序中插入Sanitizer-coverage的相关指令,可以跟踪基本块(basic block)的执行情况。

      (5) 编译时插桩(-finstrument-functions或-fsanitize-coverage=trace-pc[-guard],indirect-calls,trace-cmp或两者都有):Honggfuzz还支持在编译时进行插桩,用于收集代码覆盖率数据。通过编译时插桩,可以在程序中插入额外的函数调用或指令,以便跟踪代码的执行情况。

      开发人员可以提供初始的文件集合(corpus),Honggfuzz会逐步改进这些文件集合,但在反馈驱动模式下,提供初始文件集合并不是必需的。Honggfuzz会根据反馈信息自动改进测试用例,优先选择能够探索新的程序路径和覆盖更多代码的输入。

      基于硬件的覆盖反馈模糊化要求(英特尔):perf(aux area – 4.2) + pt

      支持Intel PT(Processor Tracing)的CPU / 支持BTS(Branch Trace Store)的CPU。

      Linux内核版本 >= v4.2:需要使用Linux内核版本4.2或更高版本,以支持perf AUXTRACE功能。这是用于收集硬件辅助的覆盖率数据的关键功能。

      三、源码解读

      在程序内通过perf_event_open系统调用可以使用PT实现BB基本块的覆盖率追踪,传递给指定进程pid来实现监控:

      static long perf_event_open(
          struct perf_event_attr* hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) {
          return syscall(__NR_perf_event_open, hw_event, (uintptr_t)pid, (uintptr_t)cpu,
              (uintptr_t)group_fd, (uintptr_t)flags);
      }
      

      将 perf_event_open系统调用 返回的文件描述符传递给mmap映射为可读写的用户内存空间,以便从中读取PT记录的追踪数据:

      static bool arch_perfCreate(run_t* run, pid_t pid, dynFileMethod_t method, int* perfFd) {
      	......
          *perfFd = perf_event_open(&pe, pid, -1, -1, PERF_FLAG_FD_CLOEXEC);
          if (*perfFd == -1) {
              PLOG_E("perf_event_open() failed");
              return false;
          }
          if (method != _HF_DYNFILE_BTS_EDGE && method != _HF_DYNFILE_IPT_BLOCK) {
              return true;
          }
      #if defined(PERF_ATTR_SIZE_VER5)
          if ((run->arch_linux.perfMmapBuf = mmap(NULL, _HF_PERF_MAP_SZ + getpagesize(),
                   PROT_READ | PROT_WRITE, MAP_SHARED, *perfFd, 0)) == MAP_FAILED) {
              run->arch_linux.perfMmapBuf = NULL;
              PLOG_W("mmap(mmapBuf) failed, sz=%zu, try increasing the kernel.perf_event_mlock_kb sysctl "
                     "(up to even 300000000)",
                  (size_t)_HF_PERF_MAP_SZ + getpagesize());
              close(*perfFd);
              *perfFd = -1;
              return false;
          }
          struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->arch_linux.perfMmapBuf;
          pem->aux_offset                  = pem->data_offset + pem->data_size;
          pem->aux_size                    = _HF_PERF_AUX_SZ;
          if ((run->arch_linux.perfMmapAux = mmap(
                   NULL, pem->aux_size, PROT_READ, MAP_SHARED, *perfFd, pem->aux_offset)) == MAP_FAILED) {
              munmap(run->arch_linux.perfMmapBuf, _HF_PERF_MAP_SZ + getpagesize());
              run->arch_linux.perfMmapBuf = NULL;
              run->arch_linux.perfMmapAux = NULL;
              PLOG_W(
                  "mmap(mmapAuxBuf) failed, try increasing the kernel.perf_event_mlock_kb sysctl (up to "
                  "even 300000000)");
              close(*perfFd);
              *perfFd = -1;
              return false;
          }
      	......
      }
      

      PT记录的追踪数据采用压缩的二进制格式输出,每秒每个CPU都会持续记录并输出,由于是硬件记录的,最早自然是出现在内核空间,为了使用它,就需要将其导出到用户空间,即通过mmap系统调用映射到用户可写的内存空间,然后再去定位数据解码。

      PT导出的追踪数据通过perf系统调用采集后被存储在一个叫AUX space的内存区域,它相对perfMmapBuf的偏移记录在perf_event_mmap_page->aux_offset,大小为perf_event_mmap_page->aux_size,mmap就是去映射AUX space。

      最后将执行到的BB基本块信息更新到feedback map。

      这里主要分析Honggfuzz – Intel PT源码:

      3.1 arch_archInit

      main() //honggfuzz.c
      	-->fuzz_threadsStart() //fuzz.c
      		-->arch_archInit() //linux/arch.c
      			-->arch_perfInit() //linux/perf.c
      				-->perf_ptInit() //linux/pt.c
      

      arch_perfInit 和 perf_ptInit 只是查看 pt 的类型和 cpu的一些信息,主要就是打印这个文件的信息:

      /sys/bus/event_source/devices/intel_pt/type
      
      /proc/cpuinfo
      

      3.2 fuzz_fuzzLoop

      main() //honggfuzz.c
      	-->fuzz_threadsStart() //fuzz.c
      		-->fuzz_threadsStart() //fuzz.c
      			-->fuzz_threadNew() //fuzz.c
      				-->fuzz_fuzzLoop() //fuzz.c // or -->fuzz_fuzzLoopSocket()
      					-->subproc_Run() //fuzz.c
      					-->fuzz_runVerifier() //fuzz.c
      						-->subproc_Run() //fuzz.c
      							-->arch_reapChild() //linux/arch.c
      								-->arch_perfAnalyze() //linux/perf.c
      									-->arch_perfMmapParse() //linux/perf.c
      										-->arch_ptAnalyze() //linux/pt.c 
      											-->perf_ptAnalyzePkt() //linux/pt.c 
      

      3.3 arch_perfAnalyze

      void arch_perfAnalyze(run_t* run) {
      	......
          if ((run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) &&
              run->arch_linux.cpuIptBtsFd != -1) {
              ioctl(run->arch_linux.cpuIptBtsFd, PERF_EVENT_IOC_DISABLE, 0);
              arch_perfMmapParse(run);
              arch_perfMmapReset(run);
              ioctl(run->arch_linux.cpuIptBtsFd, PERF_EVENT_IOC_RESET, 0);
          }
          ......
      }
      

      如果启用了IPT块计数(_HF_DYNFILE_IPT_BLOCK)并且文件描述符run->arch_linux.cpuIptBtsFd不为-1,则进行以下操作:

      调用ioctl函数禁用性能事件计数器。

      调用arch_perfMmapParse函数解析PT记录的数据。

      调用arch_perfMmapReset函数重置PT记录。

      调用ioctl函数重置性能事件计数器。

      3.4 arch_perfMmapParse

      static inline void arch_perfMmapParse(run_t* run HF_ATTR_UNUSED) {
      #if defined(PERF_ATTR_SIZE_VER5)
          struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->arch_linux.perfMmapBuf;
          if (pem->aux_head == pem->aux_tail) {
              return;
          }
          if (pem->aux_head aux_tail) {
              LOG_F("The PERF AUX data has been overwritten. The AUX buffer is too small");
          }
          
      	......
      	
          if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
              arch_ptAnalyze(run);
          }
      #endif /* defined(PERF_ATTR_SIZE_VER5) */
      }
      

      这段代码在解析性能事件的内存映射页时,检查是否有可用的辅助数据,并根据启用的反馈方法 arch_ptAnalyze 对辅助数据进行处理和分析。

      3.5 arch_ptAnalyze

      void arch_ptAnalyze(run_t* run) {
      	(1)
          struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->arch_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();
      	(2)
          struct pt_config ptc;
          pt_config_init(&ptc);
          ptc.begin = &run->arch_linux.perfMmapAux[aux_tail];
          ptc.end   = &run->arch_linux.perfMmapAux[aux_head];
          ptc.cpu   = ptCpu;
          int errcode = pt_cpu_errata(&ptc.errata, &ptc.cpu);
          if (errcode  
      

      arch_ptAnalyze 函数负责使用 Intel Processor Trace (IPT) 库分析性能追踪数据。让我们更深入地理解代码并详细了解其功能:

      (1)将run->arch_linux.perfMmapBuf强制转换为struct perf_event_mmap_page*类型的指针pem。

      然后,它使用原子操作ATOMIC_GET获取pem->aux_tail和pem->aux_head的值,并将它们分别存储在aux_tail和aux_head变量中。

      // honggfuzz/honggfuzz.h
      typedef struct {
      	......
          struct {
              /* For Linux code */
              uint8_t* perfMmapBuf;
              uint8_t* perfMmapAux;
              int      cpuInstrFd;
              int      cpuBranchFd;
              int      cpuIptBtsFd;
          } arch_linux;
      } run_t;
      
      /*
       * Structure of the page that can be mapped via mmap
       */
      struct perf_event_mmap_page {
      	......
      	/*
      	 * AUX area is defined by aux_{offset,size} fields that should be set
      	 * by the userspace, so that
      	 *
      	 *   aux_offset >= data_offset + data_size
      	 *
      	 * prior to mmap()ing it. Size of the mmap()ed area should be aux_size.
      	 *
      	 * Ring buffer pointers aux_{head,tail} have the same semantics as
      	 * data_{head,tail} and same ordering rules apply.
      	 */
      	__u64	aux_head;
      	__u64	aux_tail;
      	__u64	aux_offset;
      	__u64	aux_size;
      };
      

      (2)从 run 结构中获取必要的信息后,该函数初始化一个名为 ptc 的 struct pt_config 变量,用于配置 PT 库。

      ptc 结构的 ptc.begin 和 ptc.end 字段被设置为 run->arch_linux.perfMmapAux 缓冲区中辅助数据的起始和结束地址。

      ptc.cpu 字段被设置为全局变量 ptCpu,用于指定当前 CPU。

      调用 pt_cpu_errata 函数处理特定于 CPU 的勘误,并相应地更新 ptc.errata 字段。

      (3)调用 pt_pkt_alloc_decoder 函数来为数据包解码器分配内存,并将返回的解码器指针存储在 ptd 变量中。如果分配失败,将生成错误日志。

      (4)使用 pt_pkt_sync_forward 函数将解码器与数据流的起始位置同步。如果同步失败,将生成警告日志,并函数返回。

      (5)代码进入一个循环,以处理数据流中的每个数据包。

      在循环内部,调用 pt_pkt_next 函数从解码器中获取下一个数据包,并将其存储在 packet 变量中。如果达到数据流的末尾,将退出循环。如果获取数据包失败,将生成警告日志,并退出循环。

      获取的数据包将与 last_tip_ip 值一起传递给 perf_ptAnalyzePkt 函数进行进一步的分析。该函数根据数据包类型执行特定的分析,并更新相关信息以进行进一步处理。

      总体而言,arch_ptAnalyze 函数利用 PT 库分析性能追踪数据。它配置库、分配数据包解码器、同步解码器,并遍历每个数据包,将其传递给专门的分析函数进行处理。这样可以对通过 Intel Processor Trace 捕获的性能事件进行详细的分析和解释。

      3.6 perf_ptAnalyzePkt

      __attribute__((hot)) inline static void perf_ptAnalyzePkt(
          run_t* run, struct pt_packet* packet, uint64_t* last_tip_ip) {
          (1)
          if ((packet->type != ppt_tip) && (packet->type != ppt_tip_pge) &&
              (packet->type != ppt_tip_pgd)) {
              return;
          }
      	(2)
          uint64_t ip;
          switch (packet->payload.ip.ipc) {
          case pt_ipc_update_16:
              ip = (*last_tip_ip & ~0xFFFFull) | (packet->payload.ip.ip & 0xFFFFull);
              break;
          case pt_ipc_update_32:
              ip = (*last_tip_ip & ~0xFFFFFFFFull) | (packet->payload.ip.ip & 0xFFFFFFFFull);
              break;
          case pt_ipc_update_48:
              ip = (*last_tip_ip & ~0xFFFFFFFFFFFFull) | (packet->payload.ip.ip & 0xFFFFFFFFFFFFull);
              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;
          }
          *last_tip_ip = ip;
      	(3)
          if (packet->type != ppt_tip) {
              return;
          }
      	(4)
          if (ip >= run->global->arch_linux.dynamicCutOffAddr) {
              return;
          }
      	(5)
          ip &= _HF_PERF_BITMAP_BITSZ_MASK;
          register bool prev = ATOMIC_BITMAP_SET(run->global->feedback.covFeedbackMap->bbMapPc, ip);
          if (!prev) {
              run->hwCnts.newBBCnt++;
          }
      }
      

      perf_ptAnalyzePkt 函数用于分析 Intel 处理器追踪 (IPT) 数据包。下面是函数的详细说明:

      (1)首先,函数检查数据包的类型是否为 ppt_tip、ppt_tip_pge 或 ppt_tip_pgd。这些类型对应于与跟踪指令指针 (IP) 相关的不同类型的 Intel 处理器追踪数据包。如果数据包类型不是上述类型之一,函数会直接返回,不进行进一步的分析,因为它只关注与 IP 相关的数据包。

      Target IP (TIP) 数据包:TIP 数据包记录间接分支、异常、中断和其他分支或事件的目标地址。这些数据包可以包含指令指针(IP)。TIP 数据包有多种类型,在Intel vo3 第32.4.2.2节中有更详细的介绍。

      (2)函数根据数据包的 ipc 字段从数据包的载荷中提取 IP 地址。IP 地址可以是 16、32、48 位或完整的 64 位地址。根据 ipc 字段选择适当的位,并将其与现有的 last_tip_ip 值组合,形成更新后的 IP 地址。更新后的 IP 地址被存储回 last_tip_ip 变量。

      (3)如果数据包类型不是 ppt_tip,函数会直接返回,不进行进一步的分析。函数只对 ppt_tip 数据包执行额外的操作。

      (4)函数检查 IP 地址是否大于等于 run->global->arch_linux.dynamicCutOffAddr 值。如果是,则函数直接返回,不进行进一步的分析。这个条件可能用于过滤超出特定地址范围的 IP 地址。

      (5)IP 地址与 _HF_PERF_BITMAP_BITSZ_MASK 进行位与运算,将其限制在覆盖反馈位图的大小范围内。

      函数使用原子位图操作 ATOMIC_BITMAP_SET,将 IP 地址在覆盖反馈位图中设置为相应的位。如果该位在之前已经被设置过,表示该 IP 地址的代码覆盖已经记录过,则函数返回 false。否则,如果成功设置了该位,表示出现了新的代码覆盖情况,函数返回 true,并增加 run->hwCnts.newBBCnt 计数器的值。

      总的来说,perf_ptAnalyzePkt 函数用于分析与 Intel 处理器追踪相关的数据包。它根据数据包的载荷更新 IP 地址,将其存储在 last_tip_ip 变量中,并在覆盖反馈位图中设置相应的位。该函数可用于跟踪代码执行路径和收集覆盖率信息。

      四、perf采集过程

      subproc_Run()
      	-->subproc_New()
      		-->arch_prepareParentAfterFork()
      			-->arch_perfOpen()
      				-->arch_perfCreate()
      

      (1) arch_perfOpen

      typedef enum {
          _HF_DYNFILE_NONE         = 0x0,
          _HF_DYNFILE_INSTR_COUNT  = 0x1,
          _HF_DYNFILE_BRANCH_COUNT = 0x2,
          _HF_DYNFILE_BTS_EDGE     = 0x10,
          _HF_DYNFILE_IPT_BLOCK    = 0x20,
          _HF_DYNFILE_SOFT         = 0x40,
      } dynFileMethod_t;
      
      bool arch_perfOpen(run_t* run) {
          if (run->global->feedback.dynFileMethod == _HF_DYNFILE_NONE) {
              return true;
          }
      	......
      	
          if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
              if (!arch_perfCreate(run, run->pid, _HF_DYNFILE_IPT_BLOCK, &run->arch_linux.cpuIptBtsFd)) {
                  LOG_E("Cannot set up perf for pid=%d (_HF_DYNFILE_IPT_BLOCK)", (int)run->pid);
                  goto out;
              }
          }
          return true;
      }
      

      (2) arch_perfCreate

      static bool arch_perfCreate(run_t* run, pid_t pid, dynFileMethod_t method, int* perfFd) {
      	......
      	(1)
          struct perf_event_attr pe = {};
          pe.size                   = sizeof(struct perf_event_attr);
          if (run->global->arch_linux.kernelOnly) {
              pe.exclude_user = 1;
          } else {
              pe.exclude_kernel = 1;
          }
          pe.disabled = 1;
          if (!run->global->exe.persistent) {
              pe.enable_on_exec = 1;
          }
          pe.exclude_hv = 1;
          pe.type       = PERF_TYPE_HARDWARE;
      	(2)
          switch (method) {
      	......
          case _HF_DYNFILE_IPT_BLOCK:
              LOG_D("Using: (Intel PT) type=%" PRIu32 " for pid=%d", perfIntelPtPerfType, (int)pid);
              pe.type   = perfIntelPtPerfType;
              pe.config = RTIT_CTL_DISRETC;
              break;
      	......
          }
      	......
      	(3)
          *perfFd = perf_event_open(&pe, pid, -1, -1, PERF_FLAG_FD_CLOEXEC);
          if (*perfFd == -1) {
              PLOG_E("perf_event_open() failed");
              return false;
          }
      	(4)
          if ((run->arch_linux.perfMmapBuf = mmap(NULL, _HF_PERF_MAP_SZ + getpagesize(),
                   PROT_READ | PROT_WRITE, MAP_SHARED, *perfFd, 0)) == MAP_FAILED) {
      		......
          }
      	(5)
          struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->arch_linux.perfMmapBuf;
          pem->aux_offset                  = pem->data_offset + pem->data_size;
          pem->aux_size                    = _HF_PERF_AUX_SZ;
          if ((run->arch_linux.perfMmapAux = mmap(
                   NULL, pem->aux_size, PROT_READ, MAP_SHARED, *perfFd, pem->aux_offset)) == MAP_FAILED) {
      		......
          }
          return true;
      }
      

      (1)初始化一个名为pe的struct perf_event_attr结构体,并根据配置和选择的方法设置其各个字段。例如,根据arch_linux.kernelOnly标志设置exclude_user或exclude_kernel字段,启用或禁用某些功能,并将类型设置为PERF_TYPE_HARDWARE。

      (2)根据选择的方法,配置pe结构体的事件类型和配置值。例如,如果方法是_HF_DYNFILE_IPT_BLOCK,则将pe.config设置为RTIT_CTL_DISRETC并启用继承。其他方法也有类似的配置。

      (3)调用perf_event_open函数尝试打开性能事件,使用pe结构体、进程ID(pid)和其他参数作为输入。

      (4)使用 mmap 函数将一个共享内存区域映射到指定的地址空间。这个内存区域的大小是 _HF_PERF_MAP_SZ + getpagesize() 字节,具有读写权限,并且是与文件描述符 *perfFd 关联的共享映射。

      (5)将映射后的内存区域强制转换为 struct perf_event_mmap_page* 类型,并进行一些设置。其中,aux_offset 字段的值被设置为 data_offset + data_size,而 aux_size 字段被设置为 _HF_PERF_AUX_SZ。

      然后,使用 mmap 函数再次进行内存映射操作。这次映射的目标是另一个共享内存区域,大小为 pem->aux_size 字节,具有只读权限,并且是与同一个文件描述符 *perfFd 关联的共享映射。

      (4)和(5) 的目的是在指定的进程地址空间中创建两个共享内存区域,一个用于性能事件的主缓冲区(perfMmapBuf),另一个用于辅助缓冲区(perfMmapAux)。这些映射操作是为了方便读取和处理性能事件数据。

      备注:perf 采集数据,mmap映射了两次内存区域,一次是 perfMmapBuf 内存区域,一次是 perfMmapAux 内存区域。

      这段代码提供了一种启用和配置特定进程性能事件监控的方法,支持不同的监控方法,如指令计数、分支计数、Intel BTS和Intel PT事件。

      五、Intel BTS分析

      这里分析下BTS,和PT都是采用 perf 采集,只是处理数据时简单一些。BTS采集的数据格式:

      struct bts_branch {
          uint64_t from;
          uint64_t to;
          uint64_t misc;
      };
      
      static inline void arch_perfMmapParse(run_t* run HF_ATTR_UNUSED) {
      #if defined(PERF_ATTR_SIZE_VER5)
          struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->arch_linux.perfMmapBuf;
          if (pem->aux_head == pem->aux_tail) {
              return;
          }
          if (pem->aux_head aux_tail) {
              LOG_F("The PERF AUX data has been overwritten. The AUX buffer is too small");
          }
          if (run->global->feedback.dynFileMethod & _HF_DYNFILE_BTS_EDGE) {
              arch_perfBtsCount(run);
          }
          if (run->global->feedback.dynFileMethod & _HF_DYNFILE_IPT_BLOCK) {
              arch_ptAnalyze(run);
          }
      #endif /* defined(PERF_ATTR_SIZE_VER5) */
      }
      
      #if defined(PERF_ATTR_SIZE_VER5)
      __attribute__((hot)) static inline void arch_perfBtsCount(run_t* run) {
          struct perf_event_mmap_page* pem = (struct perf_event_mmap_page*)run->arch_linux.perfMmapBuf;
          struct bts_branch {
              uint64_t from;
              uint64_t to;
              uint64_t misc;
          };
          uint64_t           aux_head = ATOMIC_GET(pem->aux_head);
          struct bts_branch* br       = (struct bts_branch*)run->arch_linux.perfMmapAux;
          for (; br arch_linux.perfMmapAux + aux_head)); br++) {
              /*
               * Kernel sometimes reports branches from the kernel (iret), we are not interested in that
               * as it makes the whole concept of unique branch counting less predictable
               */
              if (!run->global->arch_linux.kernelOnly &&
                  (__builtin_expect(br->from > 0xFFFFFFFF00000000, false) ||
                      __builtin_expect(br->to > 0xFFFFFFFF00000000, false))) {
                  LOG_D("Adding branch %#018" PRIx64 " - %#018" PRIx64, br->from, br->to);
                  continue;
              }
              if (br->from >= run->global->arch_linux.dynamicCutOffAddr ||
                  br->to >= run->global->arch_linux.dynamicCutOffAddr) {
                  continue;
              }
              register size_t pos = ((br->from to & 0xFFF));
              pos &= _HF_PERF_BITMAP_BITSZ_MASK;
              register bool prev = ATOMIC_BITMAP_SET(run->global->feedback.covFeedbackMap->bbMapPc, pos);
              if (!prev) {
                  run->hwCnts.newBBCnt++;
              }
          }
      }
      #endif /* defined(PERF_ATTR_SIZE_VER5) */
      

      arch_perfBtsCount函数是用于在特定体系结构上计算分支跟踪存储(Branch Trace Store,BTS)的计数的函数。该函数用于处理性能监测工具中与分支跟踪存储相关的操作,并对其进行计数。代码中使用了一些宏和数据结构,下面是对函数的详细说明:

      函数接收一个指向perf_event_mmap_page结构的指针pem,该结构用于内核和用户空间之间的通信,是一个内存映射页面。

      函数使用struct bts_branch结构定义了一个分支跟踪存储(BTS)的条目结构,其中包含from、to和misc三个成员,分别表示分支的起始地址、目标地址和其他杂项数据。

      函数通过原子操作获取pem->aux_head的值,并将其存储在aux_head变量中,表示当前辅助缓冲区的头部位置。

      函数使用run->arch_linux.perfMmapAux指针将br指向辅助缓冲区的起始地址。

      通过循环遍历辅助缓冲区中的每个struct bts_branch条目。

      在遍历过程中,如果run->global->arch_linux.kernelOnly为假,并且分支的起始地址或目标地址位于内核空间(高于0xFFFFFFFF00000000),则被认为是内核分支,将跳过该分支并继续下一次循环。

      如果分支的起始地址或目标地址超过了给定的run->global->arch_linux.dynamicCutOffAddr值,则跳过该分支并继续下一次循环。这个条件可能用于过滤超出特定地址范围的分支。

      通过将分支的起始地址左移12位,并与目标地址的后12位进行异或运算,然后使用位图大小掩码_HF_PERF_BITMAP_BITSZ_MASK进行与运算,计算出一个位置值pos。

      使用原子位图操作ATOMIC_BITMAP_SET,将位置pos处的位设置为代码覆盖反馈位图中的一个位。如果该位之前已被设置,则返回的prev值为true,否则返回prev值为false。如果该位之前未被设置,则将run->hwCnts.newBBCnt自增。

      总的来说,arch_perfBtsCount函数用于在特定体系结构上计算分支跟踪存储(BTS)的计数。它遍历辅助缓冲区中的每个分支跟踪存储(BTS)条目,并根据特定条件将其对应的位置设置为代码覆盖反馈位图中的一个位。该函数通常用于跟踪分支的执行并收集覆盖率信息。

      参考资料

      https://github.com/google/honggfuzz

      https://github.com/intel/libipt

      honggfuzz漏洞挖掘技术深究系列

      Intel Processor Trace(一)

      Intel Processor Trace(三)

VPS购买请点击我

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

目录[+]