<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Farzon's blog]]></title><description><![CDATA[Farzon's blog]]></description><link>https://codeblog.farzon.org</link><image><url>https://cdn.hashnode.com/uploads/logos/69f30a5b909e64ad078554d7/02e4698a-cbc4-40cd-85eb-0821784e3a9c.jpg</url><title>Farzon&apos;s blog</title><link>https://codeblog.farzon.org</link></image><generator>RSS for Node</generator><lastBuildDate>Thu, 30 Apr 2026 10:09:44 GMT</lastBuildDate><atom:link href="https://codeblog.farzon.org/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Using Pin As A Coverage Diagnostic Tool for Fuzzers]]></title><description><![CDATA[Originally published at blog.farzon.org on Apr 5

When working with libfuzzers, developers often hit a "coverage wall" where the fuzzer fails to reach deep code paths. While standard coverage reports ]]></description><link>https://codeblog.farzon.org/using-pin-as-a-coverage-diagnostic-tool-for-fuzzers</link><guid isPermaLink="true">https://codeblog.farzon.org/using-pin-as-a-coverage-diagnostic-tool-for-fuzzers</guid><category><![CDATA[code coverage]]></category><category><![CDATA[fuzzers]]></category><category><![CDATA[pintool]]></category><dc:creator><![CDATA[Farzon Lotfi]]></dc:creator><pubDate>Thu, 30 Apr 2026 09:21:07 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>Originally published at <a href="https://blog.farzon.org/2026/04/using-pin-as-coverage-diagnostic-tool.html">blog.farzon.org</a> on Apr 5</p>
</blockquote>
<p>When working with libfuzzers, developers often hit a "coverage wall" where the fuzzer fails to reach deep code paths. While standard coverage reports show what was hit, they lack the temporal context to explain <em>why</em> a fuzzer is getting stuck. This tool is designed to bridge that gap by providing a detailed analysis of basic block execution using dynamic binary instrumentation.</p>
<h2>Getting Started: Pintool Setup</h2>
<p>The project leverages the Intel Pin framework to monitor fuzzer execution at the basic block level. To get started, you'll need to fetch the framework and build the tool:</p>
<pre><code class="language-shell">
./fetchpintool.sh
export PIN_ROOT=pin-3.24-98612-g6bd5931f2-gcc-linux
make
</code></pre>
<h2>Implementation: The PinTool Core</h2>
<p>The core of the tool involves instrumenting every basic block (BBL) within a set of target functions. We use a trace-based approach to insert analysis calls before each block executes.</p>
<pre><code class="language-cpp">
// From aPinTool.cpp
// Instruments basic blocks to track visits
VOID Trace(TRACE trace, VOID* v) {
    for (BBL bbl = TRACE_BblHead(trace); BBL_Valid(bbl); bbl = BBL_Next(bbl)) {
        INS head = BBL_InsHead(bbl);
        PIN_LockClient();
        RTN rtn = INS_Rtn(head);
        if (RTN_Valid(rtn)) {
            string rtnName = RTN_Name(rtn);
            if (IsFunctionWatched(rtnName)) {
                // Record the visit for temporal analysis
                BBL_InsertCall(bbl, IPOINT_BEFORE, (AFUNPTR)RecordBblVisit, 
                               IARG_ADDRINT, BBL_Address(bbl), 
                               IARG_ADDRINT, RTN_Address(rtn),
                               IARG_PTR, new string(rtnName),
                               IARG_END);
            }
        }
        PIN_UnlockClient();
    }
}
</code></pre>
<h2>Building the Filter List</h2>
<p>Because the Pintool does not automatically unmangle C++ names, you need to identify the mangled names of the functions you wish to instrument. A common workflow involves comparing demangled and mangled symbols from your fuzzer binary:</p>
<pre><code class="language-shell">
readelf -Ws textFieldFuzzer | awk '{print $8}' | c++filt &gt; functionNamesDemangled.txt
readelf -Ws textFieldFuzzer | awk '{print $8}' &gt; functionNamesMangled.txt
</code></pre>
<p>By putting these files side-by-side, you can find the human-readable function name in the demangled file, jump to the corresponding line in the mangled file, and copy that string into your <code>function_filter.csv</code>.</p>
<h2>Filtering for Precision</h2>
<p>To manage the high volume of data generated during fuzzing, the tool supports function filtering via a CSV file. This allows you to focus the diagnostic overhead only on the modules or functions you are currently debugging. Note that function names must be provided in their <strong>mangled form</strong> (e.g., <code>_Z11FieldFuzzer...</code>).</p>
<pre><code class="language-cpp">
// Logic to filter instrumentation by function name
bool IsFunctionWatched(string name) {
    if (watchedFunctions.empty()) return true;
    return watchedFunctions.find(name) != watchedFunctions.end();
}
</code></pre>
<h2>Generating Control Flow Graph Images</h2>
<p>Once your filter is set, you can run the fuzzer through the Pintool to generate a <code>.dot</code> file, which can then be converted into a visual graph.</p>
<pre><code class="language-shell">
mkdir dotFiles
pin-3.24-98612-g6bd5931f2-gcc-linux/pin -t obj-intel64/aPinTool.so -od dotFiles/cfg.dot -i function_filter.csv -- ./textFieldFuzzer -max_total_time=1
dot -Tpng dotFiles/LLVMFuzzerTestOneInput_cfg.dot -o LLVMFuzzerTestOneInput.png
</code></pre>
<img src="https://raw.githubusercontent.com/farzonl/coverage-experiment/refs/heads/main/pintool/images/LLVMFuzzerTestOneInput.png" alt="LLVMFuzzerTestOneInput CFG" style="display:block;margin:0 auto" />

<p><em>Example CFG generated for LLVMFuzzerTestOneInput</em></p>
<h2>Interpreting the Report</h2>
<p>The tool generates a <code>bblockReport.txt</code> file which tracks two primary metrics:</p>
<ul>
<li><p><strong>Seen Order:</strong> The order basic blocks were first discovered, segmented by routine (function).</p>
</li>
<li><p><strong>Last Visited Order:</strong> A sorted list of basic blocks based on the last time they were reached. This is the "high-water mark" that tells you exactly where the fuzzer stopped making progress.</p>
</li>
</ul>
<img src="https://raw.githubusercontent.com/farzonl/coverage-experiment/refs/heads/main/pintool/images/main.png" alt="Main CFG" style="display:block;margin:0 auto" />

<h2>Technical Limitations &amp; Considerations</h2>
<p>As this is a specialized diagnostic tool, there are several key implementation details to keep in mind:</p>
<ul>
<li><p><strong>Debug Builds Only:</strong> The tool requires DWARF symbols to perform function name lookups for filtering.</p>
</li>
<li><p><strong>System Constraints:</strong> Certain C++17 features like <code>std::filesystem</code> do not currently run within the Pintool environment. Manual directory creation is required.</p>
</li>
<li><p><strong>Timing APIs:</strong> Due to language-specific API limitations in Pin, the tool uses <code>gettimeofday</code> for high-precision timing.</p>
</li>
<li><p><strong>Executable Scope:</strong> By default, instrumentation is limited to the main executable. To include shared libraries, the image filter logic must be adjusted:</p>
</li>
</ul>
<pre><code class="language-cpp">
// Change this:
if(!IMG_IsMainExecutable(insImage)) { return; }

// To this:
if(!IMG_IsMainExecutable(insImage) || !getTelemetry().isInSharedLibraryFilterList(insImage)) { return; }
</code></pre>
<h2>The "Taken Branch" Nuance</h2>
<p>Pintool creates basic blocks based on dynamic execution. In some cases, C++ code that looks like it should have 6 blocks might only show 5 in practice because certain branches (like a specific <code>if</code> condition) are never "taken" at the assembly level, resulting in the fuzzer flowing through them as a single execution block.</p>
<img src="https://raw.githubusercontent.com/farzonl/coverage-experiment/refs/heads/main/pintool/images/commentedCPPCode.png" alt="C++ Code" style="display:block;margin:0 auto" />

<img src="https://raw.githubusercontent.com/farzonl/coverage-experiment/refs/heads/main/pintool/images/asmTrace.png" alt="Assembly Trace" style="display:block;margin:0 auto" />

<p><strong>Check out the full source code and experiment details on GitHub:</strong></p>
<p><a href="https://github.com/farzonl/coverage-experiment">https://github.com/farzonl/coverage-experiment</a></p>
]]></content:encoded></item></channel></rss>