Skip to content

Commit b0dba80

Browse files
Steampunkeryzixuanzh
authored andcommitted
verifyinstruction module
1 parent 422794c commit b0dba80

File tree

1 file changed

+311
-0
lines changed

1 file changed

+311
-0
lines changed
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
use super::InstructionValidator;
2+
3+
use parity_wasm::elements::*;
4+
use parity_wasm::elements::Instruction;
5+
use parity_wasm::elements::Instruction::*;
6+
7+
use crate::instructionerrors::*;
8+
9+
pub const GET_INST: [Instruction; 2] = [
10+
GetGlobal(0),
11+
GetLocal(0),
12+
];
13+
14+
pub const I32_BINOP: [Instruction; 15] = [
15+
I32Add,
16+
I32Sub,
17+
I32Mul,
18+
I32DivS,
19+
I32DivU,
20+
I32RemS,
21+
I32RemU,
22+
I32And,
23+
I32Or,
24+
I32Xor,
25+
I32Shl,
26+
I32ShrS,
27+
I32ShrU,
28+
I32Rotl,
29+
I32Rotr,
30+
];
31+
32+
pub const I64_BINOP: [Instruction; 15] = [
33+
I64Add,
34+
I64Sub,
35+
I64Mul,
36+
I64DivS,
37+
I64DivU,
38+
I64RemS,
39+
I64RemU,
40+
I64And,
41+
I64Or,
42+
I64Xor,
43+
I64Shl,
44+
I64ShrS,
45+
I64ShrU,
46+
I64Rotl,
47+
I64Rotr
48+
];
49+
50+
pub const F32_BINOP: [Instruction; 7] = [
51+
F32Add,
52+
F32Sub,
53+
F32Mul,
54+
F32Div,
55+
F32Min,
56+
F32Max,
57+
F32Copysign,
58+
];
59+
60+
pub const F64_BINOP: [Instruction; 7] = [
61+
F64Add,
62+
F64Sub,
63+
F64Mul,
64+
F64Div,
65+
F64Min,
66+
F64Max,
67+
F64Copysign,
68+
];
69+
70+
71+
use self::Filter::*;
72+
use std::mem::discriminant;
73+
74+
type Pop = Vec<ValueType>;
75+
type Push = Vec<ValueType>;
76+
77+
struct Signature {
78+
pop: Pop,
79+
push: Push
80+
}
81+
82+
pub enum Filter {
83+
NumericInstructions,
84+
NoFilter
85+
}
86+
87+
/// Basic struct for validating modules
88+
pub struct VerifyInstructions {
89+
filter: Filter,
90+
stack: Vec<ValueType>
91+
}
92+
93+
impl VerifyInstructions {
94+
95+
pub fn new(filter: Filter) -> Self {
96+
VerifyInstructions{ filter, stack: vec![] }
97+
}
98+
99+
fn check_instructions(&mut self, module: &Module, body: &FuncBody, index: usize) -> Result<bool, InstructionError> {
100+
for instruction in body.code().elements() {
101+
if contains(instruction, &GET_INST) && !self.push_global_or_local(module, instruction, body, index)? {
102+
return Ok(false)
103+
}
104+
match self.filter {
105+
NumericInstructions => {
106+
let signature = get_instruction_signature(instruction);
107+
// if the instruction does not have a signature we are interested in, we continue
108+
if signature.is_some() && !self.validate_instruction(&signature.unwrap(), instruction)? {
109+
return Ok(false)
110+
}
111+
}
112+
NoFilter => () // TODO: do this
113+
};
114+
}
115+
Ok(true)
116+
}
117+
118+
fn validate_instruction(&mut self, signature: &Signature, instruction: &Instruction) -> Result<bool, InstructionError> {
119+
for signature_value in &signature.pop {
120+
let value = self.stack.pop();
121+
match value {
122+
Some(stack_value) => {
123+
if stack_value != *signature_value {
124+
return Err(InstructionError::InvalidOperation(instruction.clone()))
125+
}
126+
}
127+
None => return Err(InstructionError::InvalidOperation(instruction.clone())) // Instructions are small, so clone
128+
129+
}
130+
}
131+
self.stack.extend(&signature.push);
132+
133+
Ok(true)
134+
}
135+
136+
fn push_global_or_local(&mut self, module: &Module, instruction: &Instruction, body: &FuncBody, index: usize) -> Result<bool, InstructionError> {
137+
138+
// These next couple lines are just to get the parameters of the function we're dealing with.
139+
// We need the parameters because they can be loaded like local variables but they're not in the locals vec
140+
141+
// type_ref is the index of the FunctionType in types_section
142+
let type_ref = &module.function_section().unwrap().entries()[index].type_ref();
143+
let type_variant = &module.type_section().unwrap().types()[*type_ref as usize];
144+
145+
let mut locals = body.locals().to_vec();
146+
match type_variant {
147+
Type::Function(ftype) => {
148+
locals.extend(ftype.params().iter().map(|f| Local::new(0, *f)));
149+
}
150+
}
151+
152+
match instruction {
153+
Instruction::GetGlobal(local) => {
154+
match locals.get(*local as usize) {
155+
Some(variable) => {
156+
self.stack.push(variable.value_type());
157+
Ok(true)
158+
},
159+
None => { Err(InstructionError::GlobalNotFound) },
160+
}
161+
},
162+
Instruction::GetLocal(local) => {
163+
match locals.get(*local as usize) {
164+
Some(variable) => {
165+
self.stack.push(variable.value_type());
166+
Ok(true)
167+
},
168+
None => { Err(InstructionError::LocalNotFound) },
169+
}
170+
},
171+
_ => { Err(InstructionError::UnmatchedInstruction) },
172+
}
173+
}
174+
175+
}
176+
177+
178+
impl InstructionValidator for VerifyInstructions {
179+
fn validate(&mut self, module: &Module) -> Result<bool, InstructionError> {
180+
match module.code_section() {
181+
Some(functions) => {
182+
for (index, function) in functions.bodies().iter().enumerate() {
183+
let is_function_valid: bool = self.check_instructions(module, function, index)?;
184+
if !is_function_valid {
185+
return Ok(false)
186+
}
187+
}
188+
Ok(true)
189+
},
190+
None => Ok(true),
191+
}
192+
}
193+
}
194+
195+
fn contains(instruction: &Instruction, container: &[Instruction]) -> bool {
196+
container.iter().any(|f| discriminant(f) == discriminant(instruction))
197+
}
198+
199+
fn get_instruction_signature(instruction: &Instruction) -> Option<Signature> {
200+
// returns some signature if there is a type we are interested in
201+
// returns None otherwise
202+
if contains(instruction, &I32_BINOP) {
203+
Some(Signature{ pop: [ValueType::I32; 2].to_vec(), push: [ValueType::I32; 1].to_vec() })
204+
} else if contains(instruction, &I64_BINOP) {
205+
Some(Signature{ pop: [ValueType::I64; 2].to_vec(), push: [ValueType::I64; 1].to_vec() })
206+
} else if contains(instruction, &F32_BINOP) {
207+
Some(Signature{ pop: [ValueType::F32; 2].to_vec(), push: [ValueType::F32; 1].to_vec() })
208+
} else if contains(instruction, &F64_BINOP) {
209+
Some(Signature{ pop: [ValueType::F64; 2].to_vec(), push: [ValueType::F64; 1].to_vec() })
210+
} else {
211+
None
212+
}
213+
}
214+
215+
216+
#[cfg(test)]
217+
mod tests {
218+
use super::*;
219+
use parity_wasm::elements::deserialize_buffer;
220+
221+
#[test]
222+
fn add_two_simple_binary() {
223+
// WAST:
224+
// (module
225+
// (type $t0 (func (param i32 i32) (result i32)))
226+
// (func $f0 (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
227+
// (i32.add
228+
// (get_local $p0)
229+
// (get_local $p1))))
230+
let wasm: Vec<u8> = vec![
231+
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7f, 0x7f, 0x01,
232+
0x7f, 0x03, 0x02, 0x01, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b,
233+
0x00, 0x14, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x02, 0x0d, 0x01, 0x00, 0x02, 0x00, 0x03, 0x6c, 0x68,
234+
0x73, 0x01, 0x03, 0x72, 0x68, 0x73
235+
];
236+
237+
let module = deserialize_buffer::<Module>(&wasm).unwrap();
238+
239+
let mut validator = VerifyInstructions::new(NumericInstructions);
240+
let is_valid = validator.validate(&module).unwrap();
241+
assert!(true, is_valid)
242+
}
243+
244+
#[test]
245+
#[should_panic]
246+
fn unmatched_type_failure_binary() {
247+
// Binary incorrectly tries to add an f64 with i32 to return an i32
248+
// This should be failed by the validator
249+
// WAST:
250+
// (module
251+
// (func $addTwo (param f64 i32) (result i32)
252+
// get_local 0
253+
// get_local 1
254+
// i32.add)
255+
// (export "addTwo" (func $addTwo)))
256+
let wasm: Vec<u8> = vec![
257+
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7c, 0x7f, 0x01,
258+
0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x0a, 0x01, 0x06, 0x61, 0x64, 0x64, 0x54, 0x77, 0x6f, 0x00,
259+
0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b, 0x00, 0x19, 0x04, 0x6e,
260+
0x61, 0x6d, 0x65, 0x01, 0x09, 0x01, 0x00, 0x06, 0x61, 0x64, 0x64, 0x54, 0x77, 0x6f, 0x02, 0x07,
261+
0x01, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00
262+
];
263+
264+
let module = deserialize_buffer::<Module>(&wasm).unwrap();
265+
266+
let mut validator = VerifyInstructions::new(NumericInstructions);
267+
validator.validate(&module).unwrap();
268+
}
269+
270+
#[test]
271+
fn print_instructions_complex_binary() {
272+
// WAST:
273+
// (module
274+
// (type $t0 (func (param i32 i32) (result i32)))
275+
// (func $_Z4multii (export "_Z4multii") (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
276+
// (i32.mul
277+
// (get_local $p1)
278+
// (get_local $p0)))
279+
// (func $_Z3addii (export "_Z3addii") (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
280+
// (i32.add
281+
// (get_local $p1)
282+
// (get_local $p0)))
283+
// (func $_Z6divideii (export "_Z6divideii") (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
284+
// (i32.div_s
285+
// (get_local $p0)
286+
// (get_local $p1)))
287+
// (table $T0 0 anyfunc)
288+
// (memory $memory (export "memory") 1))
289+
290+
let wasm: Vec<u8> = vec![
291+
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7f, 0x7f, 0x01,
292+
0x7f, 0x03, 0x04, 0x03, 0x00, 0x00, 0x00, 0x04, 0x04, 0x01, 0x70, 0x00, 0x00, 0x05, 0x03, 0x01,
293+
0x00, 0x01, 0x07, 0x2f, 0x04, 0x09, 0x5f, 0x5a, 0x34, 0x6d, 0x75, 0x6c, 0x74, 0x69, 0x69, 0x00,
294+
0x00, 0x08, 0x5f, 0x5a, 0x33, 0x61, 0x64, 0x64, 0x69, 0x69, 0x00, 0x01, 0x0b, 0x5f, 0x5a, 0x36,
295+
0x64, 0x69, 0x76, 0x69, 0x64, 0x65, 0x69, 0x69, 0x00, 0x02, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72,
296+
0x79, 0x02, 0x00, 0x0a, 0x19, 0x03, 0x07, 0x00, 0x20, 0x01, 0x20, 0x00, 0x6c, 0x0b, 0x07, 0x00,
297+
0x20, 0x01, 0x20, 0x00, 0x6a, 0x0b, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6d, 0x0b, 0x00, 0x4b,
298+
0x04, 0x6e, 0x61, 0x6d, 0x65, 0x01, 0x23, 0x03, 0x00, 0x09, 0x5f, 0x5a, 0x34, 0x6d, 0x75, 0x6c,
299+
0x74, 0x69, 0x69, 0x01, 0x08, 0x5f, 0x5a, 0x33, 0x61, 0x64, 0x64, 0x69, 0x69, 0x02, 0x0b, 0x5f,
300+
0x5a, 0x36, 0x64, 0x69, 0x76, 0x69, 0x64, 0x65, 0x69, 0x69, 0x02, 0x1f, 0x03, 0x00, 0x02, 0x00,
301+
0x02, 0x70, 0x30, 0x01, 0x02, 0x70, 0x31, 0x01, 0x02, 0x00, 0x02, 0x70, 0x30, 0x01, 0x02, 0x70,
302+
0x31, 0x02, 0x02, 0x00, 0x02, 0x70, 0x30, 0x01, 0x02, 0x70, 0x31
303+
];
304+
305+
let module = deserialize_buffer::<Module>(&wasm).unwrap();
306+
307+
let mut validator = VerifyInstructions::new(NumericInstructions);
308+
let is_valid = validator.validate(&module).unwrap();
309+
assert!(true, is_valid)
310+
}
311+
}

0 commit comments

Comments
 (0)