') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Add script to generate fuzz coverage by Prabhat1308 · Pull Request #3718 · lightningdevkit/rust-lightning · GitHub
Skip to content

Add script to generate fuzz coverage - #3718

Merged
TheBlueMatt merged 4 commits into
lightningdevkit:mainfrom
Prabhat1308:pv/add_cov_script
Jul 14, 2025
Merged

Add script to generate fuzz coverage#3718
TheBlueMatt merged 4 commits into
lightningdevkit:mainfrom
Prabhat1308:pv/add_cov_script

Conversation

@Prabhat1308

Copy link
Copy Markdown
Contributor

This script generates the fuzz coverage in a html file when run .

Considerations

  1. The script assumes a corpus directory in fuzz directory.
  2. Also assumes the corpus for a particular target to be under the name of the target itself. for eg - corpus for base32_target.rs would be under corpus/base32_target.
  3. The corpus I worked with was generated using libFuzzer it may/may not work with corpus generated from other fuzzers.

How to run

# from the root directory
chmod +x contrib/generate_fuzz_coverage.sh
contrib/generate_fuzz_coverage.sh

@ldk-reviews-bot

ldk-reviews-bot commented Apr 8, 2025

Copy link
Copy Markdown

👋 I see @jkczyz was un-assigned.
If you'd like another reviewer assignemnt, please click here.

@Prabhat1308

Copy link
Copy Markdown
ContributorAuthor

If needed I can also push my script to generate corpus from libFuzzer that create the corpus according to the considerations if the reviewers deem it worthy .

@jkczyz
jkczyz requested review from TheBlueMatt and removed request for jkczyzApril 8, 2025 21:50

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if we should include this coverage data in our codecov output in PRs...On the one hand its not "reliable" coverage data in that we aren't actually testing the behavior, but on the other hand we are at least hitting the code lines and presumable would find crashes. WDYT?

Comment threadcontrib/generate_fuzz_coverage.sh Outdated
Comment threadcontrib/generate_fuzz_coverage.sh Outdated
Comment threadcontrib/generate_fuzz_coverage.sh Outdated

if [ -d "$TARGET_CORPUS_DIR" ]; then
echo "Running cargo llvm-cov for target: $TARGET_NAME with corpus $TARGET_CORPUS_DIR"
if ! CARGO_TARGET_DIR=./target cargo +${TOOLCHAIN} llvm-cov run --no-report --manifest-path "$FUZZ_DIR/Cargo.toml" --target "$HOST_TRIPLE" --features "$COMBINED_FEATURES" --bin "$TARGET_NAME" -- "$TARGET_CORPUS_DIR" -runs=1; then

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than running the fuzz target specifically, can we just use cargo llvm-cov to run the tests? For each binary in the fuzz tests we already support running cargo test and it'll pull all the files from fuzz/test_cases/X and run it through the fuzz processor. Seems easier than relying on the libfuzzer logic.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure about that. Running on test_cases with cargo test would remove the fuzziness since now it will be run test_cases and wont run the fuzzer but since its coverage report we are basically doing the same thing. I would have to check on this.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Someone doing fuzzing can presumably copy their corpus from wherever it is to test_cases. In the comment you note that we currently expect a corpus in corpus, but it could well be any folder name?

@Prabhat1308Prabhat1308Apr 9, 2025

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have changed the script to use cargo test instance now. Surprisingly reduced the code by 99%. Although I don't see the test_cases in any branch . Maybe you can try running the script and provide some feedback since I dont know the format on how test_cases directory has the inputs.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, test_cases isn't automatically created anywhere, its just read by the tests if it exists. Not sure what to do about that wrt the script here, maybe we should have it look for the llvm-libfuzzer, afl, and honggfuzz dirs, copy them to test (if its empty) and then run cargo test?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you want to go ahead and do this?

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi . sorry for the late response . Have been busy recently with my academics . Would implement this by the end of this month hopefully.

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I updated the PR according to the comment , finding the corpus directories is not feasible since there is no clear indication/factor that can catch that this is a corpus directory apart from the object format which might also be different depending on the fuzzer. Also the location of corpus can be out of the project and some people tend to keep their project directory clean . Instead what I have done is to check for test_cases and if its not present/empty it will throw a message and instruction to copy paste (but not halt the fuzz run).

@ldk-reviews-bot

Copy link
Copy Markdown

👋 The first review has been submitted!

Do you think this PR is ready for a second reviewer? If so, click here to assign a second reviewer.

@Prabhat1308

Copy link
Copy Markdown
ContributorAuthor

I wonder if we should include this coverage data in our codecov output in PRs...On the one hand its not "reliable" coverage data in that we aren't actually testing the behavior, but on the other hand we are at least hitting the code lines and presumable would find crashes. WDYT?

I would prefer to not add it in codecov until we have a good public corpora of fuzz inputs to run the report on.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

I would prefer to not add it in codecov until we have a good public corpora of fuzz inputs to run the report on.

One reason to do this would be to only include "trivially-reachable" things in the codecov output. We don't really want to include stuff as coverage if the fuzzer happened to reach it only once, but if the fuzzer found its way into it in a minute of CI time, its probably decent coverage :)

@Prabhat1308

Copy link
Copy Markdown
ContributorAuthor

I would prefer to not add it in codecov until we have a good public corpora of fuzz inputs to run the report on.

One reason to do this would be to only include "trivially-reachable" things in the codecov output. We don't really want to include stuff as coverage if the fuzzer happened to reach it only once, but if the fuzzer found its way into it in a minute of CI time, its probably decent coverage :)

Makes sense if thats the case.

@TheBlueMatt

Copy link
Copy Markdown
Collaborator

Do you intend to go ahead and run this in CI? Or would you prefer not to?

@Prabhat1308

Copy link
Copy Markdown
ContributorAuthor

added this script to run in CI .

@Prabhat1308
Prabhat1308force-pushed the pv/add_cov_script branch 3 times, most recently from a554c14 to a460b7aCompareJune 15, 2025 16:54
@Prabhat1308

Copy link
Copy Markdown
ContributorAuthor

1.63 versions are failing because llvm-cov requires 1.65 , other 2 failures are because of storage issue on the CI instance.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, so sorry I missed your comments here.

Comment threadci/ci-tests.sh Outdated
RUSTFLAGS="--cfg=lsps1_service" cargo test --verbose --color always -p lightning-liquidity

# Generate fuzz coverage report
echo -e "\n\nGenerating fuzz coverage report"

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, rather than doing this in ci-tests.sh let's do this in a new script which is called from the coverage job in CI.

Comment threadcontrib/generate_fuzz_coverage.sh
Comment threadci/ci-fuzz.sh Outdated
Comment threadci/ci-fuzz.sh Outdated
Comment threadcontrib/generate_fuzz_coverage.sh Outdated
Comment on lines +54 to +56
export RUSTFLAGS="--cfg=fuzzing --cfg=secp256k1_fuzz --cfg=hashes_fuzz"
# ignore anything in fuzz directory since we don't want coverage of targets
cargo llvm-cov --html --ignore-filename-regex "fuzz/" --output-dir "$OUTPUT_DIR"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script currently checks for the existence of test corpus files but doesn't appear to use them when generating coverage with cargo llvm-cov. For accurate fuzz coverage reporting, the command should incorporate these corpus files as test inputs.

Consider modifying the cargo llvm-cov command to explicitly use the corpus files, perhaps with something like:

# For each target with a corpusfortarget_dirin test_cases/*;doif [ -d"$target_dir" ];then
target_name=$(basename "$target_dir")
cargo llvm-cov --html --test-dir "$target_dir" --target "$target_name" ...
fidone

This would ensure the coverage report reflects code paths exercised by the fuzzing corpus rather than just standard test execution.

Suggested change
export RUSTFLAGS="--cfg=fuzzing --cfg=secp256k1_fuzz --cfg=hashes_fuzz"
# ignore anything in fuzz directory since we don't want coverage of targets
cargo llvm-cov --html --ignore-filename-regex "fuzz/" --output-dir "$OUTPUT_DIR"
export RUSTFLAGS="--cfg=fuzzing --cfg=secp256k1_fuzz --cfg=hashes_fuzz"
# Process each fuzz target with its corpus
mkdir -p "$OUTPUT_DIR"
fortarget_dirin fuzz/corpus/*;do
if [ -d"$target_dir" ];then
target_name=$(basename "$target_dir")
echo"Generating coverage for target: $target_name"
# Run coverage for this specific target with its corpus
cargo llvm-cov --html --ignore-filename-regex "fuzz/" \
--fuzz-target "$target_name" \
--corpus-dir "$target_dir" \
--output-path "$OUTPUT_DIR/$target_name"
fi
done
# Generate a combined report
cargo llvm-cov --html --ignore-filename-regex "fuzz/" --output-dir "$OUTPUT_DIR/combined"

Spotted by Diamond

Is this helpful? React 👍 or 👎 to let us know.

@Prabhat1308Prabhat1308Jun 30, 2025

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm I dont think this is the correct way ? Cargo-llvm will use all the tests_cases (if present) and all targets in this scenario. Individually calling them seems redundant work

Comment threadcontrib/generate_fuzz_coverage.sh Outdated
@Prabhat1308

Copy link
Copy Markdown
ContributorAuthor

Failure caused because of no storage left on the ci infra

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Man I hate github CI. I think if we add a deletion of some of the files in target after the first cargo llvm-cov in the CI build.yml it should pass.

Comment threadcontrib/generate_fuzz_coverage.sh Outdated
Comment threadcontrib/generate_fuzz_coverage.sh
Comment threadcontrib/generate_fuzz_coverage.sh Outdated
@Prabhat1308

Copy link
Copy Markdown
ContributorAuthor

Working locally when run with the CI workflow.

Screenshot 2025-07-08 at 9 52 01 PM

Comment threadci/ci-fuzz.sh
Comment threadci/ci-fuzz.sh
Comment threadcontrib/generate_fuzz_coverage.sh
Comment threadcontrib/generate_fuzz_coverage.sh
@Prabhat1308

Copy link
Copy Markdown
ContributorAuthor

CI passed . had to nuke the entire target directory

@tnull

Copy link
Copy Markdown
Contributor

Might be worth giving CI a kick now that we fixed the No space left on device issue (for now) with #3916.

@TheBlueMattTheBlueMatt left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be cleaned up a bit, but it looks like it works, so just gonna land. I'll open a followup to trim down some fat.

Comment threadci/ci-fuzz.sh
@@ -0,0 +1,24 @@
#!/bin/bash

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ha, we now have a fuzz/ci-fuzz.sh and a ci/ci-fuzz.sh, that's confusing :)

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants

@Prabhat1308@ldk-reviews-bot@TheBlueMatt@tnull