From 8a216ada9d6e15730730e7ef005cf6a688049441 Mon Sep 17 00:00:00 2001 From: Dan Blackwell Date: Fri, 2 Aug 2024 19:11:30 +0100 Subject: [PATCH 1/8] Add -simplifycfg-branch-fold-threshold=0 flag to elminate LLVMs select instructions (cmov) optimisations. This gives extra coverage checkpoint in && chained conditionals --- src/project.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/project.rs b/src/project.rs index 3bcc37e..63a3113 100644 --- a/src/project.rs +++ b/src/project.rs @@ -168,7 +168,8 @@ impl FuzzProject { let mut rustflags: String = "-Cpasses=sancov-module \ -Cllvm-args=-sanitizer-coverage-level=4 \ -Cllvm-args=-sanitizer-coverage-inline-8bit-counters \ - -Cllvm-args=-sanitizer-coverage-pc-table" + -Cllvm-args=-sanitizer-coverage-pc-table \ + -Cllvm-args=-simplifycfg-branch-fold-threshold=0" .to_owned(); if !build.no_trace_compares { From 75ecc74526846ec4f41ec407131fa8fce2cc5efc Mon Sep 17 00:00:00 2001 From: Dan Blackwell Date: Tue, 6 Aug 2024 19:46:09 +0100 Subject: [PATCH 2/8] Hide simplifycfg-branch-fold-threshold=0 behind flag disable_branch_folding --- src/options.rs | 8 ++++++++ src/project.rs | 7 +++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/options.rs b/src/options.rs index 220784f..96f4a1c 100644 --- a/src/options.rs +++ b/src/options.rs @@ -134,6 +134,13 @@ pub struct BuildOptions { /// the `*-trace-compares` instrumentation assumes that the instruction is /// available. pub no_trace_compares: bool, + + #[arg(long)] + /// Disable transformation of if-statements into `cmov` instructions (when this + /// happens, we get no coverage feedback for that branch). Default setting is true. + /// A further explanation can be found here: + /// https://github.com/rust-fuzz/cargo-fuzz/pull/380#issue-2445529059 + pub disable_branch_folding: bool, } impl stdfmt::Display for BuildOptions { @@ -233,6 +240,7 @@ mod test { strip_dead_code: false, no_cfg_fuzzing: false, no_trace_compares: false, + disable_branch_folding: true, }; let opts = vec![ diff --git a/src/project.rs b/src/project.rs index 63a3113..1fb25d3 100644 --- a/src/project.rs +++ b/src/project.rs @@ -168,14 +168,17 @@ impl FuzzProject { let mut rustflags: String = "-Cpasses=sancov-module \ -Cllvm-args=-sanitizer-coverage-level=4 \ -Cllvm-args=-sanitizer-coverage-inline-8bit-counters \ - -Cllvm-args=-sanitizer-coverage-pc-table \ - -Cllvm-args=-simplifycfg-branch-fold-threshold=0" + -Cllvm-args=-sanitizer-coverage-pc-table" .to_owned(); if !build.no_trace_compares { rustflags.push_str(" -Cllvm-args=-sanitizer-coverage-trace-compares"); } + if build.disable_branch_folding { + rustflags.push_str("-Cllvm-args=-simplifycfg-branch-fold-threshold=0"); + } + if !build.no_cfg_fuzzing { rustflags.push_str(" --cfg fuzzing"); } From d90503485d477ed954974e15bbc6cbcc5e9e47b0 Mon Sep 17 00:00:00 2001 From: Dan Blackwell Date: Tue, 6 Aug 2024 19:59:32 +0100 Subject: [PATCH 3/8] Set default value for disable_branch_folding --- src/options.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/options.rs b/src/options.rs index 96f4a1c..d886a66 100644 --- a/src/options.rs +++ b/src/options.rs @@ -135,7 +135,7 @@ pub struct BuildOptions { /// available. pub no_trace_compares: bool, - #[arg(long)] + #[arg(long, default_value = "true")] /// Disable transformation of if-statements into `cmov` instructions (when this /// happens, we get no coverage feedback for that branch). Default setting is true. /// A further explanation can be found here: From 160066d07b4d1b7a755624408629290c38362a86 Mon Sep 17 00:00:00 2001 From: Dan Blackwell Date: Tue, 6 Aug 2024 20:16:39 +0100 Subject: [PATCH 4/8] Missing space character --- src/project.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/project.rs b/src/project.rs index 1fb25d3..fc856b0 100644 --- a/src/project.rs +++ b/src/project.rs @@ -176,7 +176,7 @@ impl FuzzProject { } if build.disable_branch_folding { - rustflags.push_str("-Cllvm-args=-simplifycfg-branch-fold-threshold=0"); + rustflags.push_str(" -Cllvm-args=-simplifycfg-branch-fold-threshold=0"); } if !build.no_cfg_fuzzing { From 79902307006b163dfbe2932422005f56346206d3 Mon Sep 17 00:00:00 2001 From: Dan Blackwell Date: Tue, 6 Aug 2024 22:00:37 +0100 Subject: [PATCH 5/8] Move disable_branch_folding over to Option and default false for now --- src/options.rs | 6 +++--- src/project.rs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/options.rs b/src/options.rs index d886a66..4cb6ad8 100644 --- a/src/options.rs +++ b/src/options.rs @@ -135,12 +135,12 @@ pub struct BuildOptions { /// available. pub no_trace_compares: bool, - #[arg(long, default_value = "true")] + #[arg(long)] /// Disable transformation of if-statements into `cmov` instructions (when this /// happens, we get no coverage feedback for that branch). Default setting is true. /// A further explanation can be found here: /// https://github.com/rust-fuzz/cargo-fuzz/pull/380#issue-2445529059 - pub disable_branch_folding: bool, + pub disable_branch_folding: Option, } impl stdfmt::Display for BuildOptions { @@ -240,7 +240,7 @@ mod test { strip_dead_code: false, no_cfg_fuzzing: false, no_trace_compares: false, - disable_branch_folding: true, + disable_branch_folding: None, }; let opts = vec![ diff --git a/src/project.rs b/src/project.rs index fc856b0..d9f32a4 100644 --- a/src/project.rs +++ b/src/project.rs @@ -175,7 +175,7 @@ impl FuzzProject { rustflags.push_str(" -Cllvm-args=-sanitizer-coverage-trace-compares"); } - if build.disable_branch_folding { + if build.disable_branch_folding.is_some_and(|v| v) { rustflags.push_str(" -Cllvm-args=-simplifycfg-branch-fold-threshold=0"); } From 83cb30bd1430bad9878b5f643f02622064b12ded Mon Sep 17 00:00:00 2001 From: Dan Blackwell Date: Tue, 6 Aug 2024 22:06:11 +0100 Subject: [PATCH 6/8] Set disable_branch_folding to default true --- src/project.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/project.rs b/src/project.rs index d9f32a4..7847e28 100644 --- a/src/project.rs +++ b/src/project.rs @@ -175,7 +175,7 @@ impl FuzzProject { rustflags.push_str(" -Cllvm-args=-sanitizer-coverage-trace-compares"); } - if build.disable_branch_folding.is_some_and(|v| v) { + if build.disable_branch_folding.unwrap_or(true) { rustflags.push_str(" -Cllvm-args=-simplifycfg-branch-fold-threshold=0"); } From 03d40405fec184153eaf94f7949ff7a530a164fc Mon Sep 17 00:00:00 2001 From: Dan Blackwell Date: Wed, 7 Aug 2024 18:17:52 +0100 Subject: [PATCH 7/8] Inline disable_branch_folding explanation to the doc comment --- src/options.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/src/options.rs b/src/options.rs index 4cb6ad8..0b9711d 100644 --- a/src/options.rs +++ b/src/options.rs @@ -138,8 +138,32 @@ pub struct BuildOptions { #[arg(long)] /// Disable transformation of if-statements into `cmov` instructions (when this /// happens, we get no coverage feedback for that branch). Default setting is true. - /// A further explanation can be found here: - /// https://github.com/rust-fuzz/cargo-fuzz/pull/380#issue-2445529059 + /// This is done by setting the `-simplifycfg-branch-fold-threshold=0` LLVM arg. + /// + /// For example, in the following program shows the default coverage feedback when + /// compiled with `-Copt-level=3`: + /// + /// mark_covered(1); // mark edge 1 as covered + /// let mut res = 1; + /// if x > 5 && y < 6 { + /// res = 2; + /// } + /// + /// With `disable_branch_folding` enabled, the code compiles to be equivalent to: + /// + /// mark_covered(1); + /// let mut res = 1; + /// if x > 5 { + /// mark_covered(2); + /// if y < 6 { + /// mark_covered(3); + /// res = 2; + /// } + /// } + /// + /// Note, that in the second program, there are now 2 new coverage feedback points, + /// and the fuzzer can store an input to the corpus at each condition that it passes; + /// giving it a better chance of producing an input that reaches `res = 2;`. pub disable_branch_folding: Option, } From 9daf1fc108b594a6631d339f757fab895c2906f2 Mon Sep 17 00:00:00 2001 From: Dan Blackwell Date: Wed, 7 Aug 2024 18:26:18 +0100 Subject: [PATCH 8/8] Keep the linter happy --- src/options.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/options.rs b/src/options.rs index 0b9711d..677b0e7 100644 --- a/src/options.rs +++ b/src/options.rs @@ -139,18 +139,18 @@ pub struct BuildOptions { /// Disable transformation of if-statements into `cmov` instructions (when this /// happens, we get no coverage feedback for that branch). Default setting is true. /// This is done by setting the `-simplifycfg-branch-fold-threshold=0` LLVM arg. - /// - /// For example, in the following program shows the default coverage feedback when + /// + /// For example, in the following program shows the default coverage feedback when /// compiled with `-Copt-level=3`: - /// + /// /// mark_covered(1); // mark edge 1 as covered /// let mut res = 1; /// if x > 5 && y < 6 { /// res = 2; /// } - /// + /// /// With `disable_branch_folding` enabled, the code compiles to be equivalent to: - /// + /// /// mark_covered(1); /// let mut res = 1; /// if x > 5 { @@ -160,7 +160,7 @@ pub struct BuildOptions { /// res = 2; /// } /// } - /// + /// /// Note, that in the second program, there are now 2 new coverage feedback points, /// and the fuzzer can store an input to the corpus at each condition that it passes; /// giving it a better chance of producing an input that reaches `res = 2;`.