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