Skip to content

Add support for automatically reducing found fuzz cases. #700

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 12, 2025

Conversation

FractalFir
Copy link
Contributor

When reducing the issues found via fuzzing, I often find myself repeating certain cleanup steps, needed to much more quickly reduce a given file.

This PR automates those steps for all cases.

  1. The saved examples use println and not hashing(when searching for bugs, hashing is still used). This is slower to run by a factor of 2, but it makes analyzing the bug samples much, much easier.
  2. Remove most calls to dump_var. This drastically speeds up the execution of the sample(both native and in MIRI). This more than makes up for the slow-down of using println.
  3. The remove_dup_assign step is not needed, but it exploits some properties of Rust to quickly reduce the sample by about 10-15%.
  4. match_to_goto uses the way Rustlantis generates MIR to very quickly turn almost all condtional branching in the sample into unconditional jumps. This makes analysing the generated MIR much easier, and allows for further reduction.
  5. block_abort attempts to replace decoy blocks with calls to abort. In code generated by rustlantis, the decoy blocks may never execute, and are there only to confuse the compiler. So, replacing them with abort is always sound. This greatly reduces the generated sample, and makes reasoning about control flow simpler.
  6. remove_block tries to remove small blocks allotoghter. It runs after block_abort, because non-rechable blocks are still often needed(eg. they are the target of a jump).
  7. linearize_cf tries to merge blocks, removing terminators. This, once again, makes reasoning about the control flow much easier. In some cases, it can reduce a function to a single, continuous block.

Since those reductions exploit certain properties of Rust, more general tools like creduce are often incapable of applying them. So, this set of reduction steps should both speed up the production of minimized samples, and also automate some of the steps of the process.

The resulting file still needs to be feed to a proper reducer for further processing, but, sinice it is considerably simpler already, the whole process will be quicker.

Additionally, since I know that none of the transformations here can introduce UB, and that most of them don't alter the behaviour of the program, I can avoid running MIRI & rustc + LLVM when reducing samples, which further speeds up the process.

Copy link
Contributor

@antoyo antoyo left a comment

Choose a reason for hiding this comment

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

A few nitpicks.
(And sorry about all the noise for new lines between functions: I checked and rustfmt doesn't seem to support adding a new line between functions. I'll try to find another solution to check this in the CI.)
Looks good!
Thanks for your work!

let source_file = generate(seed, print_tmp_vars)?;
test_file(&source_file, true)
}
/// Tests a file with a cached LLVM result. Used for reduction, when it is known
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
/// Tests a file with a cached LLVM result. Used for reduction, when it is known
/// Tests a file with a cached LLVM result. Used for reduction, when it is known

Ok(Ok(()))
}
}
fn test_file(
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
fn test_file(
fn test_file(

@FractalFir
Copy link
Contributor Author

Fixed the issues you pointed out, and improved the reduction by a tiny bit.

Now, the reducer will try to remove function calls, and then functions. This can significantly reduce the example at a pretty fast pace.

@FractalFir
Copy link
Contributor Author

I checked and rustfmt doesn't seem to support adding a new line between functions.

I suppose we could open an issue about that. tidy enforces this(I think), why shouldn't rustfmt?

@antoyo
Copy link
Contributor

antoyo commented Jun 8, 2025

I suppose we could open an issue about that. tidy enforces this(I think), why shouldn't rustfmt?

There's already a bunch of issues about this. The most up-to-date seems to be this one.
The fix apparently was made a couple years ago, but was not merged to the main branch, for some reasons.

Copy link
Contributor

@antoyo antoyo left a comment

Choose a reason for hiding this comment

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

A few more nitpicks.
After that, it should be good to merged.
Thanks!

@FractalFir
Copy link
Contributor Author

Should be good to go now

@antoyo antoyo merged commit b4be0b9 into rust-lang:master Jun 12, 2025
38 checks passed
@antoyo
Copy link
Contributor

antoyo commented Jun 12, 2025

Thanks a lot for your contribution!

Sign up for free to 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.

2 participants