@@ -28,8 +28,33 @@ impl Module {
2828 self . defined_globals
2929 . push ( ( fuel_global, ConstExpr :: i32_const ( default_fuel as i32 ) ) ) ;
3030
31- for code in & mut self . code {
32- let check_fuel = |insts : & mut Vec < Instruction > | {
31+ let defined_funcs = & self . funcs [ self . funcs . len ( ) - self . code . len ( ) ..] ;
32+ for ( ( _, ty) , code) in defined_funcs. iter ( ) . zip ( self . code . iter_mut ( ) ) {
33+ let params = ty. params . len ( ) ;
34+
35+ let mut temp_i32_local = None ;
36+ let mut temp_i32_local = |locals : & mut Vec < _ > | {
37+ if let Some ( l) = temp_i32_local {
38+ return l;
39+ }
40+ let l = u32:: try_from ( locals. len ( ) + params) . unwrap ( ) ;
41+ locals. push ( ValType :: I32 ) ;
42+ temp_i32_local = Some ( l) ;
43+ l
44+ } ;
45+
46+ let mut temp_i64_local = None ;
47+ let mut temp_i64_local = |locals : & mut Vec < _ > | {
48+ if let Some ( l) = temp_i64_local {
49+ return l;
50+ }
51+ let l = u32:: try_from ( locals. len ( ) + params) . unwrap ( ) ;
52+ locals. push ( ValType :: I64 ) ;
53+ temp_i64_local = Some ( l) ;
54+ l
55+ } ;
56+
57+ let check_fuel_1 = |insts : & mut Vec < Instruction > | {
3358 // if fuel == 0 { trap }
3459 insts. push ( Instruction :: GlobalGet ( fuel_global) ) ;
3560 insts. push ( Instruction :: I32Eqz ) ;
@@ -44,6 +69,44 @@ impl Module {
4469 insts. push ( Instruction :: GlobalSet ( fuel_global) ) ;
4570 } ;
4671
72+ let check_fuel_n = |insts : & mut Vec < Instruction > , local : u32 | {
73+ // if local >= fuel { trap }
74+ insts. push ( Instruction :: LocalGet ( local) ) ;
75+ insts. push ( Instruction :: GlobalGet ( fuel_global) ) ;
76+ insts. push ( Instruction :: I32GeU ) ;
77+ insts. push ( Instruction :: If ( BlockType :: Empty ) ) ;
78+ insts. push ( Instruction :: Unreachable ) ;
79+ insts. push ( Instruction :: End ) ;
80+ // fuel -= local
81+ insts. push ( Instruction :: GlobalGet ( fuel_global) ) ;
82+ insts. push ( Instruction :: LocalGet ( local) ) ;
83+ insts. push ( Instruction :: I32Sub ) ;
84+ insts. push ( Instruction :: GlobalSet ( fuel_global) ) ;
85+ } ;
86+
87+ let check_fuel_32_or_64 = |locals : & mut Vec < ValType > ,
88+ temp_i32_local : & mut dyn FnMut ( & mut Vec < ValType > ) -> u32 ,
89+ temp_i64_local : & mut dyn FnMut ( & mut Vec < ValType > ) -> u32 ,
90+ new_insts : & mut Vec < Instruction > ,
91+ inst : Instruction ,
92+ is_64 : bool | {
93+ if is_64 {
94+ let local64 = temp_i64_local ( locals) ;
95+ let local32 = temp_i32_local ( locals) ;
96+ new_insts. push ( Instruction :: LocalTee ( local64) ) ;
97+ new_insts. push ( Instruction :: I32WrapI64 ) ;
98+ new_insts. push ( Instruction :: LocalSet ( local32) ) ;
99+ check_fuel_n ( new_insts, local32) ;
100+ new_insts. push ( Instruction :: LocalGet ( local64) ) ;
101+ new_insts. push ( inst) ;
102+ } else {
103+ let local = temp_i32_local ( locals) ;
104+ new_insts. push ( Instruction :: LocalTee ( local) ) ;
105+ check_fuel_n ( new_insts, local) ;
106+ new_insts. push ( inst) ;
107+ }
108+ } ;
109+
47110 let instrs = match & mut code. instructions {
48111 Instructions :: Generated ( list) => list,
49112 Instructions :: Arbitrary ( _) => {
@@ -57,15 +120,116 @@ impl Module {
57120
58121 // Check fuel at the start of functions to deal with infinite
59122 // recursion.
60- check_fuel ( & mut new_insts) ;
123+ check_fuel_1 ( & mut new_insts) ;
61124
62125 for inst in mem:: replace ( instrs, vec ! [ ] ) {
63- let is_loop = matches ! ( & inst, Instruction :: Loop ( _) ) ;
64- new_insts. push ( inst) ;
126+ match & inst {
127+ // Check fuel at loop heads to deal with infinite loops.
128+ Instruction :: Loop ( _) => {
129+ new_insts. push ( inst) ;
130+ check_fuel_1 ( & mut new_insts) ;
131+ }
132+
133+ // Check fuel on instructions that imply loops and decrement
134+ // fuel accordingly and always have `len: i32` on top of the
135+ // stack.
136+ Instruction :: ArrayCopy { .. }
137+ | Instruction :: ArrayFill ( _)
138+ | Instruction :: ArrayInitData { .. }
139+ | Instruction :: ArrayInitElem { .. }
140+ | Instruction :: ArrayNew ( _)
141+ | Instruction :: ArrayNewDefault ( _)
142+ | Instruction :: ArrayNewData { .. }
143+ | Instruction :: ArrayNewElem { .. } => {
144+ let local = temp_i32_local ( & mut code. locals ) ;
145+ new_insts. push ( Instruction :: LocalTee ( local) ) ;
146+ check_fuel_n ( & mut new_insts, local) ;
147+ new_insts. push ( inst) ;
148+ }
149+
150+ // Check fuel on `table.init`, whose `len` operand is always
151+ // `i32`, even for `table64`.
152+ Instruction :: TableInit { .. } => {
153+ let local = temp_i32_local ( & mut code. locals ) ;
154+ new_insts. push ( Instruction :: LocalTee ( local) ) ;
155+ check_fuel_n ( & mut new_insts, local) ;
156+ new_insts. push ( inst) ;
157+ }
158+
159+ // Check fuel on `table.fill`, whose `len` operand has the
160+ // table's index type.
161+ Instruction :: TableFill ( table) => {
162+ let table = usize:: try_from ( * table) . unwrap ( ) ;
163+ let is_64 = self . tables [ table] . table64 ;
164+ check_fuel_32_or_64 (
165+ & mut code. locals ,
166+ & mut temp_i32_local,
167+ & mut temp_i64_local,
168+ & mut new_insts,
169+ inst,
170+ is_64,
171+ ) ;
172+ }
173+
174+ // Check fuel on `table.copy`, whose `len` operand has the
175+ // smaller of the source and destination tables' index types.
176+ Instruction :: TableCopy {
177+ dst_table,
178+ src_table,
179+ } => {
180+ let dst_table = usize:: try_from ( * dst_table) . unwrap ( ) ;
181+ let src_table = usize:: try_from ( * src_table) . unwrap ( ) ;
182+ check_fuel_32_or_64 (
183+ & mut code. locals ,
184+ & mut temp_i32_local,
185+ & mut temp_i64_local,
186+ & mut new_insts,
187+ inst,
188+ self . tables [ dst_table] . table64 && self . tables [ src_table] . table64 ,
189+ ) ;
190+ }
191+
192+ // Check fuel on `memory.init`, whose `len` operand is
193+ // always `i32`, even for `memory64`.
194+ Instruction :: MemoryInit { .. } => {
195+ let local = temp_i32_local ( & mut code. locals ) ;
196+ new_insts. push ( Instruction :: LocalTee ( local) ) ;
197+ check_fuel_n ( & mut new_insts, local) ;
198+ new_insts. push ( inst) ;
199+ }
200+
201+ // Check fuel on `memory.fill`, whose `len` operand has the
202+ // memory's index type.
203+ Instruction :: MemoryFill ( mem) => {
204+ let mem = usize:: try_from ( * mem) . unwrap ( ) ;
205+ check_fuel_32_or_64 (
206+ & mut code. locals ,
207+ & mut temp_i32_local,
208+ & mut temp_i64_local,
209+ & mut new_insts,
210+ inst,
211+ self . memories [ mem] . memory64 ,
212+ ) ;
213+ }
214+
215+ // Check fuel on `memory.copy`, whose `len` operand has the
216+ // smaller of the source and destination memories' index
217+ // types.
218+ Instruction :: MemoryCopy { dst_mem, src_mem } => {
219+ let dst_mem = usize:: try_from ( * dst_mem) . unwrap ( ) ;
220+ let src_mem = usize:: try_from ( * src_mem) . unwrap ( ) ;
221+ check_fuel_32_or_64 (
222+ & mut code. locals ,
223+ & mut temp_i32_local,
224+ & mut temp_i64_local,
225+ & mut new_insts,
226+ inst,
227+ self . memories [ dst_mem] . memory64 && self . memories [ src_mem] . memory64 ,
228+ ) ;
229+ }
65230
66- // Check fuel at loop heads to deal with infinite loops.
67- if is_loop {
68- check_fuel ( & mut new_insts) ;
231+ // Otherwise, just keep the instruction.
232+ _ => new_insts. push ( inst) ,
69233 }
70234 }
71235
0 commit comments