  Coverage-guided fuzzing's aggressive, high-volume testing has helped reveal
tens of thousands of software security flaws. While executing billions of test
cases mandates fast code coverage tracing, the nature of binary-only targets
leads to reduced tracing performance. A recent advancement in binary fuzzing
performance is Coverage-guided Tracing (CGT), which brings orders-of-magnitude
gains in throughput by restricting the expense of coverage tracing to only when
new coverage is guaranteed. Unfortunately, CGT suits only a basic block
coverage granularity -- yet most fuzzers require finer-grain coverage metrics:
edge coverage and hit counts. It is this limitation which prohibits nearly all
of today's state-of-the-art fuzzers from attaining the performance benefits of
CGT.
  This paper tackles the challenges of adapting CGT to fuzzing's most
ubiquitous coverage metrics. We introduce and implement a suite of enhancements
that expand CGT's introspection to fuzzing's most common code coverage metrics,
while maintaining its orders-of-magnitude speedup over conventional always-on
coverage tracing. We evaluate their trade-offs with respect to fuzzing
performance and effectiveness across 12 diverse real-world binaries (8 open-
and 4 closed-source). On average, our coverage-preserving CGT attains
near-identical speed to the present block-coverage-only CGT, UnTracer; and
outperforms leading binary- and source-level coverage tracers QEMU, Dyninst,
RetroWrite, and AFL-Clang by 2-24x, finding more bugs in less time.
