Skip to content

Commit a9f262f

Browse files
committed
fuzz: Switch from panics to an error that we can panic from later
1 parent ae742bc commit a9f262f

1 file changed

Lines changed: 83 additions & 50 deletions

File tree

fuzz/src/main.rs

Lines changed: 83 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -563,9 +563,10 @@ fn main() {
563563
None => &mut io::stdin(),
564564
};
565565
reader.read_to_end(&mut buf).unwrap();
566-
match decode_eval_check(&buf, &cli_args, Mode::Assert) {
566+
match decode_eval_check(&buf, &cli_args, false) {
567567
Ok(()) => (),
568-
Err(e) => println!("error: {e} (no panic raised)"),
568+
Err(Error::Decode(e)) => println!("error: {e} (no panic raised)"),
569+
Err(Error::Check(e)) => panic!("check error: {e}"),
569570
}
570571
}
571572
Commands::Decode { files } => run_decode_subcmd(files, &cli_args),
@@ -595,7 +596,7 @@ fn main() {
595596
// FIXME(eddyb) make the first argument (panic hook choice) a CLI toggle.
596597
afl::fuzz(true, |buf| {
597598
// Discard decoding errors
598-
let _ = decode_eval_check(&buf, &cli_args, Mode::Assert);
599+
decode_eval_check(&buf, &cli_args, false).unwrap();
599600
});
600601

601602
return;
@@ -646,8 +647,36 @@ impl fmt::Display for DecodeError {
646647
}
647648
}
648649

650+
/// Context for when a check fails.
651+
#[derive(Clone, Debug)]
652+
struct CheckError(Box<(EvalCfg, ResultSummary)>);
653+
654+
impl fmt::Display for CheckError {
655+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
656+
write!(f, "cfg {:?} results {:?}", self.0.0, self.0.1)
657+
}
658+
}
659+
660+
#[derive(Clone, Debug)]
661+
enum Error {
662+
Decode(DecodeError),
663+
Check(CheckError),
664+
}
665+
666+
impl From<DecodeError> for Error {
667+
fn from(value: DecodeError) -> Self {
668+
Self::Decode(value)
669+
}
670+
}
671+
672+
impl From<CheckError> for Error {
673+
fn from(value: CheckError) -> Self {
674+
Self::Check(value)
675+
}
676+
}
677+
649678
/// Configuration for the current operation.
650-
#[derive(Debug)]
679+
#[derive(Debug, Clone)]
651680
struct EvalCfg {
652681
kind: FpKind,
653682
op: Op,
@@ -727,14 +756,6 @@ impl EvalCfg {
727756
}
728757
}
729758

730-
#[derive(Clone, Copy, Debug)]
731-
enum Mode {
732-
/// Print the results of checking but don't panic. Used for decoding error results.
733-
PrintOnly,
734-
/// Exit on failure, used when actually running the fuzzer.
735-
Assert,
736-
}
737-
738759
/// Decode and evaluate all passed files without exiting on mismatches.
739760
fn run_decode_subcmd(files: &[PathBuf], cli_args: &Args) {
740761
let mut buf = Vec::new();
@@ -745,49 +766,50 @@ fn run_decode_subcmd(files: &[PathBuf], cli_args: &Args) {
745766
let mut f = fs::File::open(path).unwrap();
746767
f.read_to_end(&mut buf).unwrap();
747768

748-
match decode_eval_check(&buf, cli_args, Mode::PrintOnly) {
769+
match decode_eval_check(&buf, cli_args, true) {
749770
Ok(()) => (),
750-
Err(e) => println!("error decoding file: {e}"),
771+
Err(Error::Decode(e)) => println!("error decoding file: {e}"),
772+
Err(Error::Check(e)) => println!("check error: {e:?}"),
751773
}
752774
}
753775
}
754776

755777
/// Main runner: decode a config, inputs based on that config, and then evaluate the results
756778
/// for Rust, LLVM APFloat, and the host.
757-
fn decode_eval_check(data: &[u8], cli_args: &Args, mode: Mode) -> Result<(), DecodeError> {
779+
fn decode_eval_check(data: &[u8], cli_args: &Args, always_print: bool) -> Result<(), Error> {
758780
let (cfg, data) = EvalCfg::decode(data, cli_args)?;
759781
match cfg.kind {
760782
FpKind::Ieee16 => {
761783
let (a, b, c, r) = decode_for_ty_eval::<Ieee16>(&cfg, data)?;
762-
r.check_all(&cfg, a, b, c, mode);
784+
r.check_all(&cfg, a, b, c, always_print)?;
763785
}
764786
FpKind::Ieee32 => {
765787
let (a, b, c, r) = decode_for_ty_eval::<Ieee32>(&cfg, data)?;
766-
r.check_all(&cfg, a, b, c, mode);
788+
r.check_all(&cfg, a, b, c, always_print)?;
767789
}
768790
FpKind::Ieee64 => {
769791
let (a, b, c, r) = decode_for_ty_eval::<Ieee64>(&cfg, data)?;
770-
r.check_all(&cfg, a, b, c, mode);
792+
r.check_all(&cfg, a, b, c, always_print)?;
771793
}
772794
FpKind::Ieee128 => {
773795
let (a, b, c, r) = decode_for_ty_eval::<Ieee128>(&cfg, data)?;
774-
r.check_all(&cfg, a, b, c, mode);
796+
r.check_all(&cfg, a, b, c, always_print)?;
775797
}
776798
FpKind::F8E5M2 => {
777799
let (a, b, c, r) = decode_for_ty_eval::<F8E5M2>(&cfg, data)?;
778-
r.check_all(&cfg, a, b, c, mode);
800+
r.check_all(&cfg, a, b, c, always_print)?;
779801
}
780802
FpKind::F8E4M3FN => {
781803
let (a, b, c, r) = decode_for_ty_eval::<F8E4M3FN>(&cfg, data)?;
782-
r.check_all(&cfg, a, b, c, mode);
804+
r.check_all(&cfg, a, b, c, always_print)?;
783805
}
784806
FpKind::BrainF16 => {
785807
let (a, b, c, r) = decode_for_ty_eval::<BrainF16>(&cfg, data)?;
786-
r.check_all(&cfg, a, b, c, mode);
808+
r.check_all(&cfg, a, b, c, always_print)?;
787809
}
788810
FpKind::X87_F80 => {
789811
let (a, b, c, r) = decode_for_ty_eval::<X87_F80>(&cfg, data)?;
790-
r.check_all(&cfg, a, b, c, mode);
812+
r.check_all(&cfg, a, b, c, always_print)?;
791813
}
792814
}
793815

@@ -804,9 +826,18 @@ where
804826
Double: FloatConvert<<F as FloatRepr>::RustcApFloat>,
805827
{
806828
let (a, b, c) = decode_operands::<F>(cfg.op, data)?;
829+
let r = eval_all(cfg, a, b, c);
830+
Ok((a, b, c, r))
831+
}
807832

833+
#[must_use]
834+
fn eval_all<F: FloatRepr>(cfg: &EvalCfg, a: F, b: F, c: F) -> FuzzOpEvalOutputs2<F>
835+
where
836+
Single: FloatConvert<<F as FloatRepr>::RustcApFloat>,
837+
Double: FloatConvert<<F as FloatRepr>::RustcApFloat>,
838+
{
808839
// Evaluate the APFloat version as well as all possible references.
809-
let r = FuzzOpEvalOutputs2 {
840+
FuzzOpEvalOutputs2 {
810841
rs_apf: eval_rust_ap(cfg.op, cfg.rm, a, b, c),
811842
cxx_apf: cfg
812843
.run_cxx
@@ -815,9 +846,7 @@ where
815846
.run_host
816847
.then(|| F::host_eval_fuzz_op_if_supported2(cfg.op, cfg.rm, a, b, c))
817848
.flatten(),
818-
};
819-
820-
Ok((a, b, c, r))
849+
}
821850
}
822851

823852
/// Given a N-arity opcode, decode N `F`s from `data` and return zero in any remaining
@@ -851,15 +880,29 @@ struct FuzzOpEvalOutputs2<F> {
851880
host: Option<StatusAnd<F>>,
852881
}
853882

854-
impl<F: FloatRepr> FuzzOpEvalOutputs2<F> {
855-
/// Validate that outputs are correct. May unconitionally print all values or exit on error
856-
/// base on the mode (mismatches always print).
857-
fn check_all(&self, cfg: &EvalCfg, a: F, b: F, c: F, mode: Mode) {
858-
let (always_print, always_assert) = match mode {
859-
Mode::PrintOnly => (true, false),
860-
Mode::Assert => (false, true),
861-
};
883+
#[derive(Clone, Debug)]
884+
struct ResultSummary {
885+
cxx_error: bool,
886+
cxx_ignore: Option<&'static str>,
887+
cxx_stat_error: bool,
888+
cxx_stat_ignore: Option<&'static str>,
889+
host_error: bool,
890+
host_ignore: Option<&'static str>,
891+
host_stat_error: bool,
892+
host_stat_ignore: Option<&'static str>,
893+
}
862894

895+
impl<F: FloatRepr> FuzzOpEvalOutputs2<F> {
896+
/// Validate that outputs are correct. Pass `always_print` for decoding use where we want to
897+
/// see the output regardless of whether or not it is a failure.
898+
fn check_all(
899+
&self,
900+
cfg: &EvalCfg,
901+
a: F,
902+
b: F,
903+
c: F,
904+
always_print: bool,
905+
) -> Result<(), CheckError> {
863906
let print_float = |x: StatusAnd<F>,
864907
label,
865908
error,
@@ -898,19 +941,7 @@ impl<F: FloatRepr> FuzzOpEvalOutputs2<F> {
898941
println!();
899942
};
900943

901-
#[derive(Debug)]
902-
struct Results {
903-
cxx_error: bool,
904-
cxx_ignore: Option<&'static str>,
905-
cxx_stat_error: bool,
906-
cxx_stat_ignore: Option<&'static str>,
907-
host_error: bool,
908-
host_ignore: Option<&'static str>,
909-
host_stat_error: bool,
910-
host_stat_ignore: Option<&'static str>,
911-
}
912-
913-
let mut res = Results {
944+
let mut res = ResultSummary {
914945
cxx_error: false,
915946
cxx_ignore: cfg.ignore_cxx,
916947
cxx_stat_error: false,
@@ -985,8 +1016,10 @@ impl<F: FloatRepr> FuzzOpEvalOutputs2<F> {
9851016
}
9861017
}
9871018

988-
if always_assert && failure {
989-
panic!("mismatched results: {cfg:#?}\n{res:#?}");
1019+
if failure {
1020+
Err(CheckError(Box::new((cfg.clone(), res))))
1021+
} else {
1022+
Ok(())
9901023
}
9911024
}
9921025
}

0 commit comments

Comments
 (0)