Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bytecodealliance
GitHub Repository: bytecodealliance/wasmtime
Path: blob/main/cranelift/fuzzgen/src/passes/int_divz.rs
1692 views
1
use crate::FuzzGen;
2
use anyhow::Result;
3
use cranelift::codegen::cursor::{Cursor, FuncCursor};
4
use cranelift::codegen::ir::{Function, Inst, Opcode};
5
use cranelift::prelude::{InstBuilder, IntCC};
6
7
pub fn do_int_divz_pass(fuzz: &mut FuzzGen, func: &mut Function) -> Result<()> {
8
// Insert this per function, otherwise the actual rate of int_divz doesn't go down that much
9
// Experimentally if we decide this per instruction with a 0.1% allow rate, we get 4.4% of runs
10
// trapping. Doing this per function decreases the number of runs that trap. It also consumes
11
// fewer fuzzer input bytes which is nice.
12
let ratio = fuzz.config.allowed_int_divz_ratio;
13
let insert_seq = !fuzz.u.ratio(ratio.0, ratio.1)?;
14
if !insert_seq {
15
return Ok(());
16
}
17
18
let mut pos = FuncCursor::new(func);
19
while let Some(_block) = pos.next_block() {
20
while let Some(inst) = pos.next_inst() {
21
if can_int_divz(&pos, inst) {
22
insert_int_divz_sequence(&mut pos, inst);
23
}
24
}
25
}
26
Ok(())
27
}
28
29
/// Returns true/false if this instruction can cause a `int_divz` trap
30
fn can_int_divz(pos: &FuncCursor, inst: Inst) -> bool {
31
let opcode = pos.func.dfg.insts[inst].opcode();
32
33
matches!(
34
opcode,
35
Opcode::Sdiv | Opcode::Udiv | Opcode::Srem | Opcode::Urem
36
)
37
}
38
39
/// Prepend instructions to inst to avoid `int_divz` traps
40
fn insert_int_divz_sequence(pos: &mut FuncCursor, inst: Inst) {
41
let opcode = pos.func.dfg.insts[inst].opcode();
42
let inst_args = pos.func.dfg.inst_args(inst);
43
let (lhs, rhs) = (inst_args[0], inst_args[1]);
44
assert_eq!(pos.func.dfg.value_type(lhs), pos.func.dfg.value_type(rhs));
45
let ty = pos.func.dfg.value_type(lhs);
46
47
// All of these instructions can trap if the denominator is zero
48
let zero = pos.ins().iconst(ty, 0);
49
let one = pos.ins().iconst(ty, 1);
50
let denominator_is_zero = pos.ins().icmp(IntCC::Equal, rhs, zero);
51
52
let replace_denominator = if matches!(opcode, Opcode::Srem | Opcode::Sdiv) {
53
// Srem and Sdiv can also trap on INT_MIN / -1. So we need to check for the second one
54
55
// 1 << (ty bits - 1) to get INT_MIN
56
let int_min = pos.ins().ishl_imm(one, ty.lane_bits() as i64 - 1);
57
58
// Get a -1 const
59
// TODO: A iconst -1 would be clearer, but #2906 makes this impossible for i128
60
let neg_one = pos.ins().isub(zero, one);
61
62
let lhs_check = pos.ins().icmp(IntCC::Equal, lhs, int_min);
63
let rhs_check = pos.ins().icmp(IntCC::Equal, rhs, neg_one);
64
let is_invalid = pos.ins().band(lhs_check, rhs_check);
65
66
// These also crash if the denominator is zero, so we still need to check for that.
67
pos.ins().bor(denominator_is_zero, is_invalid)
68
} else {
69
denominator_is_zero
70
};
71
72
// If we have a trap we replace the denominator with a 1
73
let new_rhs = pos.ins().select(replace_denominator, one, rhs);
74
75
// Replace the previous rhs with the new one
76
let args = pos.func.dfg.inst_args_mut(inst);
77
args[1] = new_rhs;
78
}
79
80