Evaluate the function identified by `name` in two different engine instances--`lhs` and `rhs`. Returns `Ok(true)` if more evaluations can happen or `Ok(false)` if the instances may have drifted apart and no more evaluations can happen. # Panics This will panic if the evaluation is different between engines (e.g., results are different, hashed instance is different, one side traps, etc.).
(
lhs: &mut dyn DiffInstance,
lhs_engine: &dyn DiffEngine,
rhs: &mut WasmtimeInstance,
name: &str,
args: &[DiffValue],
result_tys: &[DiffValueType],
)
| 454 | /// This will panic if the evaluation is different between engines (e.g., |
| 455 | /// results are different, hashed instance is different, one side traps, etc.). |
| 456 | pub fn differential( |
| 457 | lhs: &mut dyn DiffInstance, |
| 458 | lhs_engine: &dyn DiffEngine, |
| 459 | rhs: &mut WasmtimeInstance, |
| 460 | name: &str, |
| 461 | args: &[DiffValue], |
| 462 | result_tys: &[DiffValueType], |
| 463 | ) -> wasmtime::Result<bool> { |
| 464 | log::debug!("Evaluating: `{name}` with {args:?}"); |
| 465 | let lhs_results = match lhs.evaluate(name, args, result_tys) { |
| 466 | Ok(Some(results)) => Ok(results), |
| 467 | Err(e) => Err(e), |
| 468 | // this engine couldn't execute this type signature, so discard this |
| 469 | // execution by returning success. |
| 470 | Ok(None) => return Ok(true), |
| 471 | }; |
| 472 | log::debug!(" -> lhs results on {}: {:?}", lhs.name(), &lhs_results); |
| 473 | |
| 474 | let rhs_results = rhs |
| 475 | .evaluate(name, args, result_tys) |
| 476 | // wasmtime should be able to invoke any signature, so unwrap this result |
| 477 | .map(|results| results.unwrap()); |
| 478 | log::debug!(" -> rhs results on {}: {:?}", rhs.name(), &rhs_results); |
| 479 | |
| 480 | // If Wasmtime hit its OOM condition, which is possible since it's set |
| 481 | // somewhat low while fuzzing, then don't return an error but return |
| 482 | // `false` indicating that differential fuzzing must stop. There's no |
| 483 | // guarantee the other engine has the same OOM limits as Wasmtime, and |
| 484 | // it's assumed that Wasmtime is configured to have a more conservative |
| 485 | // limit than the other engine. |
| 486 | if rhs.is_oom() { |
| 487 | return Ok(false); |
| 488 | } |
| 489 | |
| 490 | match DiffEqResult::new(lhs_engine, lhs_results, rhs_results) { |
| 491 | DiffEqResult::Success(lhs, rhs) => assert_eq!(lhs, rhs), |
| 492 | DiffEqResult::Poisoned => return Ok(false), |
| 493 | DiffEqResult::Failed => {} |
| 494 | } |
| 495 | |
| 496 | for (global, ty) in rhs.exported_globals() { |
| 497 | log::debug!("Comparing global `{global}`"); |
| 498 | let lhs = match lhs.get_global(&global, ty) { |
| 499 | Some(val) => val, |
| 500 | None => continue, |
| 501 | }; |
| 502 | let rhs = rhs.get_global(&global, ty).unwrap(); |
| 503 | assert_eq!(lhs, rhs); |
| 504 | } |
| 505 | for (memory, shared) in rhs.exported_memories() { |
| 506 | log::debug!("Comparing memory `{memory}`"); |
| 507 | let lhs = match lhs.get_memory(&memory, shared) { |
| 508 | Some(val) => val, |
| 509 | None => continue, |
| 510 | }; |
| 511 | let rhs = rhs.get_memory(&memory, shared).unwrap(); |
| 512 | if lhs == rhs { |
| 513 | continue; |
no test coverage detected