Merge branch 'main' into pch/wasi_error_handling

This commit is contained in:
Pat Hickey
2020-08-26 17:26:03 -07:00
committed by GitHub
6 changed files with 404 additions and 50 deletions

4
Cargo.lock generated
View File

@@ -2287,9 +2287,9 @@ dependencies = [
[[package]]
name = "wasm-smith"
version = "0.1.1"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ec52a2524c15abcc827e59c1eef9cdb742b4ba57a6db6a1ccd914e357326edd"
checksum = "2e09385df967070eaaf1c959f0c506a6df4a4a05ccab79d23651dcfd31e642df"
dependencies = [
"arbitrary",
"leb128",

View File

@@ -179,10 +179,8 @@ impl<'a> Lexer<'a> {
// Starting from `lookahead`, are we looking at a number?
fn looking_at_numeric(&self) -> bool {
if let Some(c) = self.lookahead {
if c.is_digit(10) {
return true;
}
match c {
'0'..='9' => return true,
'-' => return true,
'+' => return true,
'.' => return true,
@@ -291,7 +289,7 @@ impl<'a> Lexer<'a> {
match self.next_ch() {
Some('-') | Some('_') => {}
Some('.') => is_float = true,
Some(ch) if ch.is_alphanumeric() => {}
Some('0'..='9') | Some('a'..='z') | Some('A'..='Z') => {}
_ => break,
}
}
@@ -309,11 +307,10 @@ impl<'a> Lexer<'a> {
let begin = self.pos;
let loc = self.loc();
assert!(self.lookahead == Some('_') || self.lookahead.unwrap().is_alphabetic());
assert!(self.lookahead == Some('_') || self.lookahead.unwrap().is_ascii_alphabetic());
loop {
match self.next_ch() {
Some('_') => {}
Some(ch) if ch.is_alphanumeric() => {}
Some('_') | Some('0'..='9') | Some('a'..='z') | Some('A'..='Z') => {}
_ => break,
}
}
@@ -398,9 +395,10 @@ impl<'a> Lexer<'a> {
assert_eq!(self.lookahead, Some('%'));
while let Some(c) = self.next_ch() {
if !(c.is_ascii() && c.is_alphanumeric() || c == '_') {
break;
loop {
match self.next_ch() {
Some('_') | Some('0'..='9') | Some('a'..='z') | Some('A'..='Z') => {}
_ => break,
}
}
@@ -490,8 +488,8 @@ impl<'a> Lexer<'a> {
Some(self.scan_number())
}
}
Some(ch) if ch.is_digit(10) => Some(self.scan_number()),
Some(ch) if ch.is_alphabetic() => {
Some('0'..='9') => Some(self.scan_number()),
Some('a'..='z') | Some('A'..='Z') => {
if self.looking_at("NaN") || self.looking_at("Inf") {
Some(self.scan_number())
} else {
@@ -502,7 +500,8 @@ impl<'a> Lexer<'a> {
Some('"') => Some(self.scan_string()),
Some('#') => Some(self.scan_hex_sequence()),
Some('@') => Some(self.scan_srcloc()),
Some(ch) if ch.is_whitespace() => {
// all ascii whitespace
Some(' ') | Some('\x09'..='\x0d') => {
self.next_ch();
continue;
}

View File

@@ -445,7 +445,7 @@ impl<'a> Parser<'a> {
// running. I don't think this is true - self.lookahead is mutated in the loop body - so
// maybe this is a clippy bug? Either way, disable clippy for this.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::while_immutable_condition))]
while self.lookahead == None {
while self.lookahead.is_none() {
match self.lex.next() {
Some(Ok(LocatedToken { token, location })) => {
match token {

View File

@@ -2,7 +2,10 @@ use super::address_transform::AddressTransform;
use anyhow::{Context, Error, Result};
use gimli::{self, write, Expression, Operation, Reader, ReaderOffset, X86_64};
use more_asserts::{assert_le, assert_lt};
use std::cmp::PartialEq;
use std::collections::{HashMap, HashSet};
use std::hash::{Hash, Hasher};
use std::rc::Rc;
use wasmtime_environ::entity::EntityRef;
use wasmtime_environ::ir::{StackSlots, ValueLabel, ValueLabelsRanges, ValueLoc};
use wasmtime_environ::isa::TargetIsa;
@@ -88,9 +91,19 @@ enum CompiledExpressionPart {
// The wasm-local DWARF operator. The label points to `ValueLabel`.
// The trailing field denotes that the operator was last in sequence,
// and it is the DWARF location (not a pointer).
Local { label: ValueLabel, trailing: bool },
Local {
label: ValueLabel,
trailing: bool,
},
// Dereference is needed.
Deref,
// Jumping in the expression.
Jump {
conditionally: bool,
target: JumpTargetMarker,
},
// Floating landing pad.
LandingPad(JumpTargetMarker),
}
#[derive(Debug, Clone, PartialEq)]
@@ -288,7 +301,9 @@ impl CompiledExpression {
let mut ranges_builder = ValueLabelRangesBuilder::new(scope, addr_tr, frame_info);
for p in self.parts.iter() {
match p {
CompiledExpressionPart::Code(_) => (),
CompiledExpressionPart::Code(_)
| CompiledExpressionPart::Jump { .. }
| CompiledExpressionPart::LandingPad { .. } => (),
CompiledExpressionPart::Local { label, .. } => ranges_builder.process_label(*label),
CompiledExpressionPart::Deref => ranges_builder.process_label(vmctx_label),
}
@@ -310,6 +325,9 @@ impl CompiledExpression {
}| {
// build expression
let mut code_buf = Vec::new();
let mut jump_positions = Vec::new();
let mut landing_positions = HashMap::new();
macro_rules! deref {
() => {
if let (Some(vmctx_loc), Some(frame_info)) =
@@ -333,6 +351,24 @@ impl CompiledExpression {
CompiledExpressionPart::Code(c) => {
code_buf.extend_from_slice(c.as_slice())
}
CompiledExpressionPart::LandingPad(marker) => {
landing_positions.insert(marker.clone(), code_buf.len());
}
CompiledExpressionPart::Jump {
conditionally,
target,
} => {
code_buf.push(
match conditionally {
true => gimli::constants::DW_OP_bra,
false => gimli::constants::DW_OP_skip,
}
.0 as u8,
);
code_buf.push(!0);
code_buf.push(!0); // these will be relocated below
jump_positions.push((target.clone(), code_buf.len()));
}
CompiledExpressionPart::Local { label, trailing } => {
let loc =
*label_location.get(&label).context("label_location")?;
@@ -350,6 +386,15 @@ impl CompiledExpression {
if self.need_deref {
deref!();
}
for (marker, new_from) in jump_positions {
// relocate jump targets
let new_to = landing_positions[&marker];
let new_diff = new_to as isize - new_from as isize;
// FIXME: use encoding? LittleEndian for now...
&code_buf[new_from - 2..new_from]
.copy_from_slice(&(new_diff as i16).to_le_bytes());
}
Ok(Some((func_index, start, end, code_buf)))
},
)
@@ -376,9 +421,35 @@ pub fn compile_expression<R>(
where
R: Reader,
{
// Bail when `frame_base` is complicated.
if let Some(expr) = frame_base {
if expr.parts.iter().any(|p| match p {
CompiledExpressionPart::Jump { .. } => true,
_ => false,
}) {
return Ok(None);
}
}
// jump_targets key is offset in buf starting from the end
// (see also `unread_bytes` below)
let mut jump_targets: HashMap<u64, JumpTargetMarker> = HashMap::new();
let mut pc = expr.0.clone();
let buf = expr.0.to_slice()?;
let mut parts = Vec::new();
macro_rules! push {
($part:expr) => {{
let part = $part;
if let (CompiledExpressionPart::Code(cc2), Some(CompiledExpressionPart::Code(cc1))) =
(&part, parts.last_mut())
{
cc1.extend_from_slice(cc2);
} else {
parts.push(part)
}
}};
}
let mut need_deref = false;
if is_old_expression_format(&buf) && frame_base.is_some() {
// Still supporting old DWARF variable expressions without fbreg.
@@ -388,17 +459,41 @@ where
}
need_deref = frame_base.unwrap().need_deref;
}
let base_len = parts.len();
let mut code_chunk = Vec::new();
macro_rules! flush_code_chunk {
() => {
if !code_chunk.is_empty() {
parts.push(CompiledExpressionPart::Code(code_chunk));
push!(CompiledExpressionPart::Code(code_chunk));
code_chunk = Vec::new();
let _ = code_chunk; // suppresses warning for final flush
}
};
};
// Find all landing pads by scanning bytes, do not care about
// false location at this moment.
// Looks hacky but it is fast; does not need to be really exact.
for i in 0..buf.len() - 2 {
let op = buf[i];
if op == gimli::constants::DW_OP_bra.0 || op == gimli::constants::DW_OP_skip.0 {
// TODO fix for big-endian
let offset = i16::from_le_bytes([buf[i + 1], buf[i + 2]]);
let origin = i + 3;
// Discarding out-of-bounds jumps (also some of falsely detected ops)
if (offset >= 0 && offset as usize + origin <= buf.len())
|| (offset < 0 && -offset as usize <= origin)
{
let target = buf.len() as isize - origin as isize - offset as isize;
jump_targets.insert(target as u64, JumpTargetMarker::new());
}
}
}
while !pc.is_empty() {
let unread_bytes = pc.len().into_u64();
if let Some(marker) = jump_targets.get(&unread_bytes) {
flush_code_chunk!();
parts.push(CompiledExpressionPart::LandingPad(marker.clone()));
}
let next = buf[pc.offset_from(&expr.0).into_u64() as usize];
need_deref = true;
if next == 0xED {
@@ -413,7 +508,7 @@ where
let index = pc.read_sleb128()?;
flush_code_chunk!();
let label = ValueLabel::from_u32(index as u32);
parts.push(CompiledExpressionPart::Local {
push!(CompiledExpressionPart::Local {
label,
trailing: false,
});
@@ -439,10 +534,58 @@ where
code_chunk.extend(writer.into_vec());
continue;
}
Operation::UnsignedConstant { .. }
Operation::Drop { .. }
| Operation::Pick { .. }
| Operation::Swap { .. }
| Operation::Rot { .. }
| Operation::Nop { .. }
| Operation::UnsignedConstant { .. }
| Operation::SignedConstant { .. }
| Operation::ConstantIndex { .. }
| Operation::PlusConstant { .. }
| Operation::Abs { .. }
| Operation::And { .. }
| Operation::Or { .. }
| Operation::Xor { .. }
| Operation::Shr { .. }
| Operation::Shra { .. }
| Operation::Shl { .. }
| Operation::Plus { .. }
| Operation::Minus { .. }
| Operation::Div { .. }
| Operation::Mod { .. }
| Operation::Mul { .. }
| Operation::Neg { .. }
| Operation::Not { .. }
| Operation::Lt { .. }
| Operation::Gt { .. }
| Operation::Le { .. }
| Operation::Ge { .. }
| Operation::Eq { .. }
| Operation::Ne { .. }
| Operation::TypedLiteral { .. }
| Operation::Convert { .. }
| Operation::Reinterpret { .. }
| Operation::Piece { .. } => (),
Operation::Bra { target } | Operation::Skip { target } => {
flush_code_chunk!();
let arc_to = (pc.len().into_u64() as isize - target as isize) as u64;
let marker = match jump_targets.get(&arc_to) {
Some(m) => m.clone(),
None => {
// Marker not found: probably out of bounds.
return Ok(None);
}
};
push!(CompiledExpressionPart::Jump {
conditionally: match op {
Operation::Bra { .. } => true,
_ => false,
},
target: marker,
});
continue;
}
Operation::StackValue => {
need_deref = false;
@@ -457,11 +600,22 @@ where
}
Operation::Deref { .. } => {
flush_code_chunk!();
parts.push(CompiledExpressionPart::Deref);
push!(CompiledExpressionPart::Deref);
// Don't re-enter the loop here (i.e. continue), because the
// DW_OP_deref still needs to be kept.
}
_ => {
Operation::Address { .. }
| Operation::AddressIndex { .. }
| Operation::Call { .. }
| Operation::Register { .. }
| Operation::RegisterOffset { .. }
| Operation::CallFrameCFA
| Operation::PushObjectAddress
| Operation::TLS
| Operation::ImplicitValue { .. }
| Operation::ImplicitPointer { .. }
| Operation::EntryValue { .. }
| Operation::ParameterRef { .. } => {
return Ok(None);
}
}
@@ -470,20 +624,9 @@ where
}
}
if !code_chunk.is_empty() {
parts.push(CompiledExpressionPart::Code(code_chunk));
}
if base_len > 0 && base_len + 1 < parts.len() {
// see if we can glue two code chunks
if let [CompiledExpressionPart::Code(cc1), CompiledExpressionPart::Code(cc2)] =
&parts[base_len..=base_len]
{
let mut combined = cc1.clone();
combined.extend_from_slice(cc2);
parts[base_len] = CompiledExpressionPart::Code(combined);
parts.remove(base_len + 1);
}
flush_code_chunk!();
if let Some(marker) = jump_targets.get(&0) {
parts.push(CompiledExpressionPart::LandingPad(marker.clone()));
}
Ok(Some(CompiledExpression { parts, need_deref }))
@@ -602,10 +745,49 @@ impl<'a, 'b> ValueLabelRangesBuilder<'a, 'b> {
}
}
/// Marker for tracking incoming jumps.
/// Different when created new, and the same when cloned.
#[derive(Clone, Eq)]
struct JumpTargetMarker(Rc<u32>);
impl JumpTargetMarker {
fn new() -> JumpTargetMarker {
// Create somewhat unique hash data -- using part of
// the pointer of the RcBox.
let mut rc = Rc::new(0);
let hash_data = rc.as_ref() as *const u32 as usize as u32;
*Rc::get_mut(&mut rc).unwrap() = hash_data;
JumpTargetMarker(rc)
}
}
impl PartialEq for JumpTargetMarker {
fn eq(&self, other: &JumpTargetMarker) -> bool {
Rc::ptr_eq(&self.0, &other.0)
}
}
impl Hash for JumpTargetMarker {
fn hash<H: Hasher>(&self, hasher: &mut H) {
hasher.write_u32(*self.0);
}
}
impl std::fmt::Debug for JumpTargetMarker {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::result::Result<(), std::fmt::Error> {
write!(
f,
"JumpMarker<{:08x}>",
self.0.as_ref() as *const u32 as usize
)
}
}
#[cfg(test)]
mod tests {
use super::compile_expression;
use super::{AddressTransform, FunctionFrameInfo, ValueLabel, ValueLabelsRanges};
use super::{
compile_expression, AddressTransform, CompiledExpression, CompiledExpressionPart,
FunctionFrameInfo, JumpTargetMarker, ValueLabel, ValueLabelsRanges,
};
use gimli::{self, constants, Encoding, EndianSlice, Expression, RunTimeEndian};
use wasmtime_environ::CompiledFunction;
@@ -619,6 +801,9 @@ mod tests {
($d:ident) => {
constants::$d.0 as u8
};
($e:expr) => {
$e as u8
};
}
macro_rules! expression {
@@ -630,15 +815,38 @@ mod tests {
}
}
fn find_jump_targets<'a>(ce: &'a CompiledExpression) -> Vec<&'a JumpTargetMarker> {
ce.parts
.iter()
.filter_map(|p| {
if let CompiledExpressionPart::LandingPad(t) = p {
Some(t)
} else {
None
}
})
.collect::<Vec<_>>()
}
static DWARF_ENCODING: Encoding = Encoding {
address_size: 4,
format: gimli::Format::Dwarf32,
version: 4,
};
#[test]
fn test_debug_expression_jump_target() {
let m1 = JumpTargetMarker::new();
let m2 = JumpTargetMarker::new();
assert!(m1 != m2);
assert!(m1 == m1.clone());
// Internal hash_data test (theoretically can fail intermittently).
assert!(m1.0 != m2.0);
}
#[test]
fn test_debug_parse_expressions() {
use super::{CompiledExpression, CompiledExpressionPart};
use wasmtime_environ::entity::EntityRef;
let (val1, val3, val20) = (ValueLabel::new(1), ValueLabel::new(3), ValueLabel::new(20));
@@ -654,7 +862,7 @@ mod tests {
label: val20,
trailing: true
}],
need_deref: false
need_deref: false,
}
);
@@ -679,7 +887,7 @@ mod tests {
},
CompiledExpressionPart::Code(vec![35, 16, 159])
],
need_deref: false
need_deref: false,
}
);
@@ -699,7 +907,7 @@ mod tests {
},
CompiledExpressionPart::Code(vec![35, 18])
],
need_deref: true
need_deref: true,
}
);
@@ -727,7 +935,158 @@ mod tests {
CompiledExpressionPart::Deref,
CompiledExpressionPart::Code(vec![6, 159])
],
need_deref: false
need_deref: false,
}
);
let e = expression!(
DW_OP_lit1,
DW_OP_dup,
DW_OP_WASM_location,
0x0,
1,
DW_OP_and,
DW_OP_bra,
5,
0, // --> pointer
DW_OP_swap,
DW_OP_shr,
DW_OP_skip,
2,
0, // --> done
// pointer:
DW_OP_plus,
DW_OP_deref,
// done:
DW_OP_stack_value
);
let ce = compile_expression(&e, DWARF_ENCODING, None)
.expect("non-error")
.expect("expression");
let targets = find_jump_targets(&ce);
assert_eq!(targets.len(), 2);
assert_eq!(
ce,
CompiledExpression {
parts: vec![
CompiledExpressionPart::Code(vec![49, 18]),
CompiledExpressionPart::Local {
label: val1,
trailing: false
},
CompiledExpressionPart::Code(vec![26]),
CompiledExpressionPart::Jump {
conditionally: true,
target: targets[0].clone(),
},
CompiledExpressionPart::Code(vec![22, 37]),
CompiledExpressionPart::Jump {
conditionally: false,
target: targets[1].clone(),
},
CompiledExpressionPart::LandingPad(targets[0].clone()), // capture from
CompiledExpressionPart::Code(vec![34]),
CompiledExpressionPart::Deref,
CompiledExpressionPart::Code(vec![6]),
CompiledExpressionPart::LandingPad(targets[1].clone()), // capture to
CompiledExpressionPart::Code(vec![159])
],
need_deref: false,
}
);
let e = expression!(
DW_OP_lit1,
DW_OP_dup,
DW_OP_bra,
2,
0, // --> target
DW_OP_deref,
DW_OP_lit0,
// target:
DW_OP_stack_value
);
let ce = compile_expression(&e, DWARF_ENCODING, None)
.expect("non-error")
.expect("expression");
let targets = find_jump_targets(&ce);
assert_eq!(targets.len(), 1);
assert_eq!(
ce,
CompiledExpression {
parts: vec![
CompiledExpressionPart::Code(vec![49, 18]),
CompiledExpressionPart::Jump {
conditionally: true,
target: targets[0].clone(),
},
CompiledExpressionPart::Deref,
CompiledExpressionPart::Code(vec![6, 48]),
CompiledExpressionPart::LandingPad(targets[0].clone()), // capture to
CompiledExpressionPart::Code(vec![159])
],
need_deref: false,
}
);
let e = expression!(
DW_OP_lit1,
/* loop */ DW_OP_dup,
DW_OP_lit25,
DW_OP_ge,
DW_OP_bra,
5,
0, // --> done
DW_OP_plus_uconst,
1,
DW_OP_skip,
(-11 as i8),
(!0), // --> loop
/* done */ DW_OP_stack_value
);
let ce = compile_expression(&e, DWARF_ENCODING, None)
.expect("non-error")
.expect("expression");
let targets = find_jump_targets(&ce);
assert_eq!(targets.len(), 2);
assert_eq!(
ce,
CompiledExpression {
parts: vec![
CompiledExpressionPart::Code(vec![49]),
CompiledExpressionPart::LandingPad(targets[0].clone()),
CompiledExpressionPart::Code(vec![18, 73, 42]),
CompiledExpressionPart::Jump {
conditionally: true,
target: targets[1].clone(),
},
CompiledExpressionPart::Code(vec![35, 1]),
CompiledExpressionPart::Jump {
conditionally: false,
target: targets[0].clone(),
},
CompiledExpressionPart::LandingPad(targets[1].clone()),
CompiledExpressionPart::Code(vec![159])
],
need_deref: false,
}
);
let e = expression!(DW_OP_WASM_location, 0x0, 1, DW_OP_plus_uconst, 5);
let ce = compile_expression(&e, DWARF_ENCODING, None)
.expect("non-error")
.expect("expression");
assert_eq!(
ce,
CompiledExpression {
parts: vec![
CompiledExpressionPart::Local {
label: val1,
trailing: false
},
CompiledExpressionPart::Code(vec![35, 5])
],
need_deref: true,
}
);
}

View File

@@ -171,10 +171,6 @@ impl<'a> WasiSnapshotPreview1 for WasiCtx {
fn fd_filestat_set_size(&self, fd: types::Fd, size: types::Filesize) -> Result<()> {
let required_rights = HandleRights::from_base(types::Rights::FD_FILESTAT_SET_SIZE);
let entry = self.get_entry(fd)?;
// This check will be unnecessary when rust-lang/rust#63326 is fixed
if size > i64::max_value() as u64 {
return Err(Error::TooBig);
}
entry.as_handle(&required_rights)?.filestat_set_size(size)
}

View File

@@ -17,7 +17,7 @@ target-lexicon = "0.10"
peepmatic-fuzzing = { path = "../cranelift/peepmatic/crates/fuzzing", optional = true }
wasmtime = { path = "../crates/wasmtime" }
wasmtime-fuzzing = { path = "../crates/fuzzing" }
wasm-smith = "0.1.1"
wasm-smith = "0.1.2"
[[bin]]
name = "compile"