Fix opt_diff

This commit is contained in:
Chris Fallin 2023-02-24 21:52:09 -08:00
parent 2ff4d80286
commit ac2556a7ad
3 changed files with 190 additions and 81 deletions

View file

@ -23,7 +23,13 @@ fuzz_target!(
let start = parsed_module.start_func.unwrap(); let start = parsed_module.start_func.unwrap();
let mut orig_ctx = InterpContext::new(&parsed_module); let mut orig_ctx = match InterpContext::new(&parsed_module) {
Ok(ctx) => ctx,
Err(e) => {
log::trace!("Rejecting due to instantiation error: {:?}", e);
return;
}
};
orig_ctx.fuel = 10000; orig_ctx.fuel = 10000;
match orig_ctx.call(&parsed_module, start, &[]) { match orig_ctx.call(&parsed_module, start, &[]) {
@ -32,6 +38,11 @@ fuzz_target!(
log::trace!("Rejecting due to timeout in orig"); log::trace!("Rejecting due to timeout in orig");
return; return;
} }
InterpResult::Trap => {
// Silently reject.
log::trace!("Rejecting due to trap in orig");
return;
}
InterpResult::Ok(_) => {} InterpResult::Ok(_) => {}
ret => panic!("Bad result: {:?}", ret), ret => panic!("Bad result: {:?}", ret),
} }
@ -39,7 +50,7 @@ fuzz_target!(
let mut opt_module = parsed_module.clone(); let mut opt_module = parsed_module.clone();
opt_module.per_func_body(|body| body.optimize()); opt_module.per_func_body(|body| body.optimize());
let mut opt_ctx = InterpContext::new(&opt_module); let mut opt_ctx = InterpContext::new(&opt_module).unwrap();
// Allow a little leeway for opts to not actually optimize. // Allow a little leeway for opts to not actually optimize.
opt_ctx.fuel = 20000; opt_ctx.fuel = 20000;
opt_ctx.call(&opt_module, start, &[]).ok().unwrap(); opt_ctx.call(&opt_module, start, &[]).ok().unwrap();

View file

@ -101,7 +101,7 @@ fn main() -> Result<()> {
// Ensure all functions are expanded -- this is necessary // Ensure all functions are expanded -- this is necessary
// for interpretation. // for interpretation.
module.expand_all_funcs()?; module.expand_all_funcs()?;
let mut ctx = InterpContext::new(&module); let mut ctx = InterpContext::new(&module)?;
debug!("Calling start function"); debug!("Calling start function");
if let Some(start) = module.start_func { if let Some(start) = module.start_func {
ctx.call(&module, start, &[]).ok().unwrap(); ctx.call(&module, start, &[]).ok().unwrap();

View file

@ -37,7 +37,7 @@ impl InterpResult {
} }
impl InterpContext { impl InterpContext {
pub fn new(module: &Module<'_>) -> Self { pub fn new(module: &Module<'_>) -> anyhow::Result<Self> {
let mut memories = PerEntity::default(); let mut memories = PerEntity::default();
for (memory, data) in module.memories.entries() { for (memory, data) in module.memories.entries() {
let mut interp_mem = InterpMemory { let mut interp_mem = InterpMemory {
@ -45,8 +45,14 @@ impl InterpContext {
max_pages: data.maximum_pages.unwrap_or(0x1_0000), max_pages: data.maximum_pages.unwrap_or(0x1_0000),
}; };
for segment in &data.segments { for segment in &data.segments {
interp_mem.data[segment.offset..(segment.offset + segment.data.len())] let end = match segment.offset.checked_add(segment.data.len()) {
.copy_from_slice(&segment.data[..]); Some(end) => end,
None => anyhow::bail!("Data segment offset + length overflows"),
};
if end > interp_mem.data.len() {
anyhow::bail!("Data segment out of bounds");
}
interp_mem.data[segment.offset..end].copy_from_slice(&segment.data[..]);
} }
memories[memory] = interp_mem; memories[memory] = interp_mem;
} }
@ -70,12 +76,12 @@ impl InterpContext {
}; };
} }
InterpContext { Ok(InterpContext {
memories, memories,
tables, tables,
globals, globals,
fuel: u64::MAX, fuel: u64::MAX,
} })
} }
pub fn call(&mut self, module: &Module<'_>, func: Func, args: &[ConstVal]) -> InterpResult { pub fn call(&mut self, module: &Module<'_>, func: Func, args: &[ConstVal]) -> InterpResult {
@ -939,125 +945,217 @@ pub fn const_eval(
(Operator::Nop, []) => Some(ConstVal::None), (Operator::Nop, []) => Some(ConstVal::None),
(Operator::Unreachable, []) => None, (Operator::Unreachable, []) => None,
(Operator::I32Load { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I32Load { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I32(read_u32(&global.memories[memory.memory], addr)) if addr.checked_add(4)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I32(read_u32(
&global.memories[memory.memory],
addr,
)))
}), }),
(Operator::I64Load { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I64Load { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I64(read_u64(&global.memories[memory.memory], addr)) if addr.checked_add(8)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I64(read_u64(
&global.memories[memory.memory],
addr,
)))
}), }),
(Operator::F32Load { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::F32Load { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::F32(read_u32(&global.memories[memory.memory], addr)) if addr.checked_add(4)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::F32(read_u32(
&global.memories[memory.memory],
addr,
)))
}), }),
(Operator::F64Load { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::F64Load { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::F64(read_u64(&global.memories[memory.memory], addr)) if addr.checked_add(8)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::F64(read_u64(
&global.memories[memory.memory],
addr,
)))
}), }),
(Operator::I32Load8S { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I32Load8S { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I32(read_u8(&global.memories[memory.memory], addr) as i8 as i32 as u32) if addr.checked_add(1)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I32(
read_u8(&global.memories[memory.memory], addr) as i8 as i32 as u32,
))
}), }),
(Operator::I32Load8U { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I32Load8U { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I32(read_u8(&global.memories[memory.memory], addr) as u32) if addr.checked_add(4)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I32(
read_u8(&global.memories[memory.memory], addr) as u32,
))
}), }),
(Operator::I32Load16S { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I32Load16S { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I32(read_u16(&global.memories[memory.memory], addr) as i16 as i32 as u32) if addr.checked_add(2)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I32(
read_u16(&global.memories[memory.memory], addr) as i16 as i32 as u32,
))
}), }),
(Operator::I32Load16U { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I32Load16U { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I32(read_u16(&global.memories[memory.memory], addr) as u32) if addr.checked_add(2)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I32(
read_u16(&global.memories[memory.memory], addr) as u32,
))
}), }),
(Operator::I64Load8S { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I64Load8S { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I64(read_u8(&global.memories[memory.memory], addr) as i8 as i64 as u64) if addr.checked_add(1)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I64(
read_u8(&global.memories[memory.memory], addr) as i8 as i64 as u64,
))
}), }),
(Operator::I64Load8U { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I64Load8U { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I64(read_u8(&global.memories[memory.memory], addr) as u64) if addr.checked_add(1)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I64(
read_u8(&global.memories[memory.memory], addr) as u64,
))
}), }),
(Operator::I64Load16S { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I64Load16S { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I64(read_u16(&global.memories[memory.memory], addr) as i16 as i64 as u64) if addr.checked_add(2)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I64(
read_u16(&global.memories[memory.memory], addr) as i16 as i64 as u64,
))
}), }),
(Operator::I64Load16U { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I64Load16U { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I64(read_u16(&global.memories[memory.memory], addr) as u64) if addr.checked_add(2)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I64(
read_u16(&global.memories[memory.memory], addr) as u64,
))
}), }),
(Operator::I64Load32S { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I64Load32S { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I64(read_u32(&global.memories[memory.memory], addr) as i32 as i64 as u64) if addr.checked_add(4)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I64(
read_u32(&global.memories[memory.memory], addr) as i32 as i64 as u64,
))
}), }),
(Operator::I64Load32U { memory }, [ConstVal::I32(addr)]) => ctx.map(|global| { (Operator::I64Load32U { memory }, [ConstVal::I32(addr)]) => ctx.and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
ConstVal::I64(read_u32(&global.memories[memory.memory], addr) as u64) if addr.checked_add(4)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
Some(ConstVal::I64(
read_u32(&global.memories[memory.memory], addr) as u64,
))
}), }),
(Operator::I32Store { memory }, [ConstVal::I32(addr), ConstVal::I32(data)]) => { (Operator::I32Store { memory }, [ConstVal::I32(addr), ConstVal::I32(data)]) => ctx
ctx.map(|global| { .and_then(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
if addr.checked_add(4)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
write_u32(&mut global.memories[memory.memory], addr, *data); write_u32(&mut global.memories[memory.memory], addr, *data);
ConstVal::None Some(ConstVal::None)
}) }),
} (Operator::I64Store { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => ctx
(Operator::I64Store { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => { .and_then(|global| {
ctx.map(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
if addr.checked_add(8)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
write_u64(&mut global.memories[memory.memory], addr, *data); write_u64(&mut global.memories[memory.memory], addr, *data);
ConstVal::None Some(ConstVal::None)
}) }),
} (Operator::I32Store8 { memory }, [ConstVal::I32(addr), ConstVal::I32(data)]) => ctx
(Operator::I32Store8 { memory }, [ConstVal::I32(addr), ConstVal::I32(data)]) => { .and_then(|global| {
ctx.map(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
if addr.checked_add(1)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
write_u8(&mut global.memories[memory.memory], addr, *data as u8); write_u8(&mut global.memories[memory.memory], addr, *data as u8);
ConstVal::None Some(ConstVal::None)
}) }),
} (Operator::I32Store16 { memory }, [ConstVal::I32(addr), ConstVal::I32(data)]) => ctx
(Operator::I32Store16 { memory }, [ConstVal::I32(addr), ConstVal::I32(data)]) => { .and_then(|global| {
ctx.map(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
if addr.checked_add(2)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
write_u16(&mut global.memories[memory.memory], addr, *data as u16); write_u16(&mut global.memories[memory.memory], addr, *data as u16);
ConstVal::None Some(ConstVal::None)
}) }),
} (Operator::I64Store8 { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => ctx
(Operator::I64Store8 { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => { .and_then(|global| {
ctx.map(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
if addr.checked_add(1)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
write_u8(&mut global.memories[memory.memory], addr, *data as u8); write_u8(&mut global.memories[memory.memory], addr, *data as u8);
ConstVal::None Some(ConstVal::None)
}) }),
} (Operator::I64Store16 { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => ctx
(Operator::I64Store16 { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => { .and_then(|global| {
ctx.map(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
if addr.checked_add(2)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
write_u16(&mut global.memories[memory.memory], addr, *data as u16); write_u16(&mut global.memories[memory.memory], addr, *data as u16);
ConstVal::None Some(ConstVal::None)
}) }),
} (Operator::I64Store32 { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => ctx
(Operator::I64Store32 { memory }, [ConstVal::I32(addr), ConstVal::I64(data)]) => { .and_then(|global| {
ctx.map(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
if addr.checked_add(4)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
write_u32(&mut global.memories[memory.memory], addr, *data as u32); write_u32(&mut global.memories[memory.memory], addr, *data as u32);
ConstVal::None Some(ConstVal::None)
}) }),
} (Operator::F32Store { memory }, [ConstVal::I32(addr), ConstVal::F32(data)]) => ctx
(Operator::F32Store { memory }, [ConstVal::I32(addr), ConstVal::F32(data)]) => { .and_then(|global| {
ctx.map(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
if addr.checked_add(4)? > global.memories[memory.memory].data.len() as u32 {
return None;
}
write_u32(&mut global.memories[memory.memory], addr, *data); write_u32(&mut global.memories[memory.memory], addr, *data);
ConstVal::None Some(ConstVal::None)
}) }),
} (Operator::F64Store { memory }, [ConstVal::I32(addr), ConstVal::F64(data)]) => ctx
(Operator::F64Store { memory }, [ConstVal::I32(addr), ConstVal::F64(data)]) => { .and_then(|global| {
ctx.map(|global| {
let addr = *addr + memory.offset; let addr = *addr + memory.offset;
write_u64(&mut global.memories[memory.memory], addr, *data); if addr.checked_add(8)? > global.memories[memory.memory].data.len() as u32 {
ConstVal::None return None;
})
} }
write_u64(&mut global.memories[memory.memory], addr, *data);
Some(ConstVal::None)
}),
(_, args) if args.iter().any(|&arg| arg == ConstVal::None) => None, (_, args) if args.iter().any(|&arg| arg == ConstVal::None) => None,
(op, args) => unimplemented!( (op, args) => unimplemented!(
"Undefined operator or arg combination: {:?}, {:?}", "Undefined operator or arg combination: {:?}, {:?}",