Skip to content

Commit 62403eb

Browse files
authored
wasm-smith: Generate "interesting" constants (#1454)
* wasm-smith: Generate "interesting" constants * review
1 parent ac3a464 commit 62403eb

3 files changed

Lines changed: 186 additions & 29 deletions

File tree

crates/wasm-smith/src/core.rs

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ pub struct Module {
146146

147147
/// What the maximum type index that can be referenced is.
148148
max_type_limit: MaxTypeLimit,
149+
150+
/// Some known-interesting values, such as powers of two, values just before
151+
/// or just after a memory size, etc...
152+
interesting_values32: Vec<u32>,
153+
interesting_values64: Vec<u64>,
149154
}
150155

151156
impl<'a> Arbitrary<'a> for Module {
@@ -232,6 +237,8 @@ impl Module {
232237
export_names: HashSet::new(),
233238
const_expr_choices: Vec::new(),
234239
max_type_limit: MaxTypeLimit::ModuleTypes,
240+
interesting_values32: Vec::new(),
241+
interesting_values64: Vec::new(),
235242
}
236243
}
237244
}
@@ -2039,6 +2046,8 @@ impl Module {
20392046
}
20402047

20412048
fn arbitrary_code(&mut self, u: &mut Unstructured) -> Result<()> {
2049+
self.compute_interesting_values();
2050+
20422051
self.code.reserve(self.num_defined_funcs);
20432052
let mut allocs = CodeBuilderAllocations::new(self, self.config.exports.is_some());
20442053
for (_, ty) in self.funcs[self.funcs.len() - self.num_defined_funcs..].iter() {
@@ -2224,6 +2233,170 @@ impl Module {
22242233
}
22252234
})
22262235
}
2236+
2237+
fn compute_interesting_values(&mut self) {
2238+
debug_assert!(self.interesting_values32.is_empty());
2239+
debug_assert!(self.interesting_values64.is_empty());
2240+
2241+
let mut interesting_values32 = HashSet::new();
2242+
let mut interesting_values64 = HashSet::new();
2243+
2244+
let mut interesting = |val: u64| {
2245+
interesting_values32.insert(val as u32);
2246+
interesting_values64.insert(val);
2247+
};
2248+
2249+
// Zero is always interesting.
2250+
interesting(0);
2251+
2252+
// Max values are always interesting.
2253+
interesting(u8::MAX as _);
2254+
interesting(u16::MAX as _);
2255+
interesting(u32::MAX as _);
2256+
interesting(u64::MAX);
2257+
2258+
// Min values are always interesting.
2259+
interesting(i8::MIN as _);
2260+
interesting(i16::MIN as _);
2261+
interesting(i32::MIN as _);
2262+
interesting(i64::MIN as _);
2263+
2264+
for i in 0..64 {
2265+
// Powers of two.
2266+
interesting(1 << i);
2267+
2268+
// Inverted powers of two.
2269+
interesting(!(1 << i));
2270+
2271+
// Powers of two minus one, AKA high bits unset and low bits set.
2272+
interesting((1 << i) - 1);
2273+
2274+
// Negative powers of two, AKA high bits set and low bits unset.
2275+
interesting(((1_i64 << 63) >> i) as _);
2276+
}
2277+
2278+
// Some repeating bit patterns.
2279+
for pattern in [0b01010101, 0b00010001, 0b00010001, 0b00000001] {
2280+
for b in [pattern, !pattern] {
2281+
interesting(u64::from_ne_bytes([b, b, b, b, b, b, b, b]));
2282+
}
2283+
}
2284+
2285+
// Interesting float values.
2286+
let mut interesting_f64 = |x: f64| interesting(x.to_bits());
2287+
interesting_f64(0.0);
2288+
interesting_f64(-0.0);
2289+
interesting_f64(f64::INFINITY);
2290+
interesting_f64(f64::NEG_INFINITY);
2291+
interesting_f64(f64::EPSILON);
2292+
interesting_f64(-f64::EPSILON);
2293+
interesting_f64(f64::MIN);
2294+
interesting_f64(f64::MIN_POSITIVE);
2295+
interesting_f64(f64::MAX);
2296+
interesting_f64(f64::NAN);
2297+
let mut interesting_f32 = |x: f32| interesting(x.to_bits() as _);
2298+
interesting_f32(0.0);
2299+
interesting_f32(-0.0);
2300+
interesting_f32(f32::INFINITY);
2301+
interesting_f32(f32::NEG_INFINITY);
2302+
interesting_f32(f32::EPSILON);
2303+
interesting_f32(-f32::EPSILON);
2304+
interesting_f32(f32::MIN);
2305+
interesting_f32(f32::MIN_POSITIVE);
2306+
interesting_f32(f32::MAX);
2307+
interesting_f32(f32::NAN);
2308+
2309+
// Interesting values related to table bounds.
2310+
for t in self.tables.iter() {
2311+
interesting(t.minimum as _);
2312+
if let Some(x) = t.minimum.checked_add(1) {
2313+
interesting(x as _);
2314+
}
2315+
2316+
if let Some(x) = t.maximum {
2317+
interesting(x as _);
2318+
if let Some(y) = x.checked_add(1) {
2319+
interesting(y as _);
2320+
}
2321+
}
2322+
}
2323+
2324+
// Interesting values related to memory bounds.
2325+
for m in self.memories.iter() {
2326+
let min = m.minimum.saturating_mul(crate::WASM_PAGE_SIZE);
2327+
interesting(min);
2328+
for i in 0..5 {
2329+
if let Some(x) = min.checked_add(1 << i) {
2330+
interesting(x);
2331+
}
2332+
if let Some(x) = min.checked_sub(1 << i) {
2333+
interesting(x);
2334+
}
2335+
}
2336+
2337+
if let Some(max) = m.maximum {
2338+
let max = max.saturating_mul(crate::WASM_PAGE_SIZE);
2339+
interesting(max);
2340+
for i in 0..5 {
2341+
if let Some(x) = max.checked_add(1 << i) {
2342+
interesting(x);
2343+
}
2344+
if let Some(x) = max.checked_sub(1 << i) {
2345+
interesting(x);
2346+
}
2347+
}
2348+
}
2349+
}
2350+
2351+
self.interesting_values32.extend(interesting_values32);
2352+
self.interesting_values64.extend(interesting_values64);
2353+
2354+
// Sort for determinism.
2355+
self.interesting_values32.sort();
2356+
self.interesting_values64.sort();
2357+
}
2358+
2359+
fn arbitrary_const_instruction(
2360+
&self,
2361+
ty: ValType,
2362+
u: &mut Unstructured<'_>,
2363+
) -> Result<Instruction> {
2364+
debug_assert!(self.interesting_values32.len() > 0);
2365+
debug_assert!(self.interesting_values64.len() > 0);
2366+
match ty {
2367+
ValType::I32 => Ok(Instruction::I32Const(if u.arbitrary()? {
2368+
*u.choose(&self.interesting_values32)? as i32
2369+
} else {
2370+
u.arbitrary()?
2371+
})),
2372+
ValType::I64 => Ok(Instruction::I64Const(if u.arbitrary()? {
2373+
*u.choose(&self.interesting_values64)? as i64
2374+
} else {
2375+
u.arbitrary()?
2376+
})),
2377+
ValType::F32 => Ok(Instruction::F32Const(if u.arbitrary()? {
2378+
f32::from_bits(*u.choose(&self.interesting_values32)?)
2379+
} else {
2380+
u.arbitrary()?
2381+
})),
2382+
ValType::F64 => Ok(Instruction::F64Const(if u.arbitrary()? {
2383+
f64::from_bits(*u.choose(&self.interesting_values64)?)
2384+
} else {
2385+
u.arbitrary()?
2386+
})),
2387+
ValType::V128 => Ok(Instruction::V128Const(if u.arbitrary()? {
2388+
let upper = (*u.choose(&self.interesting_values64)? as i128) << 64;
2389+
let lower = *u.choose(&self.interesting_values64)? as i128;
2390+
upper | lower
2391+
} else {
2392+
u.arbitrary()?
2393+
})),
2394+
ValType::Ref(ty) => {
2395+
assert!(ty.nullable);
2396+
Ok(Instruction::RefNull(ty.heap_type))
2397+
}
2398+
}
2399+
}
22272400
}
22282401

22292402
pub(crate) fn arbitrary_limits32(

crates/wasm-smith/src/core/code_builder.rs

Lines changed: 11 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,7 +1435,7 @@ impl CodeBuilder<'_> {
14351435
}
14361436
operands = &[];
14371437
}
1438-
instructions.push(arbitrary_val(*expected, u));
1438+
instructions.push(module.arbitrary_const_instruction(*expected, u)?);
14391439
}
14401440
Ok(())
14411441
}
@@ -1544,20 +1544,6 @@ impl CodeBuilder<'_> {
15441544
}
15451545
}
15461546

1547-
fn arbitrary_val(ty: ValType, u: &mut Unstructured<'_>) -> Instruction {
1548-
match ty {
1549-
ValType::I32 => Instruction::I32Const(u.arbitrary().unwrap_or(0)),
1550-
ValType::I64 => Instruction::I64Const(u.arbitrary().unwrap_or(0)),
1551-
ValType::F32 => Instruction::F32Const(u.arbitrary().unwrap_or(0.0)),
1552-
ValType::F64 => Instruction::F64Const(u.arbitrary().unwrap_or(0.0)),
1553-
ValType::V128 => Instruction::V128Const(u.arbitrary().unwrap_or(0)),
1554-
ValType::Ref(ty) => {
1555-
assert!(ty.nullable);
1556-
Instruction::RefNull(ty.heap_type)
1557-
}
1558-
}
1559-
}
1560-
15611547
#[inline]
15621548
fn unreachable_valid(module: &Module, _: &mut CodeBuilder) -> bool {
15631549
!module.config.disallow_traps
@@ -3419,49 +3405,45 @@ fn data_drop(
34193405

34203406
fn i32_const(
34213407
u: &mut Unstructured,
3422-
_module: &Module,
3408+
module: &Module,
34233409
builder: &mut CodeBuilder,
34243410
instructions: &mut Vec<Instruction>,
34253411
) -> Result<()> {
3426-
let x = u.arbitrary()?;
34273412
builder.push_operands(&[ValType::I32]);
3428-
instructions.push(Instruction::I32Const(x));
3413+
instructions.push(module.arbitrary_const_instruction(ValType::I32, u)?);
34293414
Ok(())
34303415
}
34313416

34323417
fn i64_const(
34333418
u: &mut Unstructured,
3434-
_module: &Module,
3419+
module: &Module,
34353420
builder: &mut CodeBuilder,
34363421
instructions: &mut Vec<Instruction>,
34373422
) -> Result<()> {
3438-
let x = u.arbitrary()?;
34393423
builder.push_operands(&[ValType::I64]);
3440-
instructions.push(Instruction::I64Const(x));
3424+
instructions.push(module.arbitrary_const_instruction(ValType::I64, u)?);
34413425
Ok(())
34423426
}
34433427

34443428
fn f32_const(
34453429
u: &mut Unstructured,
3446-
_module: &Module,
3430+
module: &Module,
34473431
builder: &mut CodeBuilder,
34483432
instructions: &mut Vec<Instruction>,
34493433
) -> Result<()> {
3450-
let x = u.arbitrary()?;
34513434
builder.push_operands(&[ValType::F32]);
3452-
instructions.push(Instruction::F32Const(x));
3435+
instructions.push(module.arbitrary_const_instruction(ValType::F32, u)?);
34533436
Ok(())
34543437
}
34553438

34563439
fn f64_const(
34573440
u: &mut Unstructured,
3458-
_module: &Module,
3441+
module: &Module,
34593442
builder: &mut CodeBuilder,
34603443
instructions: &mut Vec<Instruction>,
34613444
) -> Result<()> {
3462-
let x = u.arbitrary()?;
34633445
builder.push_operands(&[ValType::F64]);
3464-
instructions.push(Instruction::F64Const(x));
3446+
instructions.push(module.arbitrary_const_instruction(ValType::F64, u)?);
34653447
Ok(())
34663448
}
34673449

@@ -5220,10 +5202,10 @@ fn memory_offset(u: &mut Unstructured, module: &Module, memory_index: u32) -> Re
52205202
assert!(a + b + c != 0);
52215203

52225204
let memory_type = &module.memories[memory_index as usize];
5223-
let min = memory_type.minimum.saturating_mul(65536);
5205+
let min = memory_type.minimum.saturating_mul(crate::WASM_PAGE_SIZE);
52245206
let max = memory_type
52255207
.maximum
5226-
.map(|max| max.saturating_mul(65536))
5208+
.map(|max| max.saturating_mul(crate::WASM_PAGE_SIZE))
52275209
.unwrap_or(u64::MAX);
52285210

52295211
let (min, max, true_max) = match (memory_type.memory64, module.config.disallow_traps) {

crates/wasm-smith/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ use std::{collections::HashSet, fmt::Write, str};
6767
#[cfg(feature = "_internal_cli")]
6868
pub use config::InternalOptionalConfig;
6969

70+
const WASM_PAGE_SIZE: u64 = 65_536;
71+
7072
/// Do something an arbitrary number of times.
7173
///
7274
/// The callback can return `false` to exit the loop early.

0 commit comments

Comments
 (0)