Skip to content

Commit 35d08d2

Browse files
authored
[Interpreter] Fix bugs found by Lars' tests (WebAssembly#66)
* The data count section is required if the `memory.init` or `data.drop` instructions are used. * `memory.init` had an off-by-one bug when reading at the end of a segment. * All instructions that operate on regions need to compare length to 0 using unsigned comparison (e.g. `I32.gt_u n 0l`). * Converting length from 32- to 64-bit now uses `I64_convert.extend_i32_u`, instead of `Int64.of_int32`, since the latter will sign-extend. * The `table.copy` overlap test now uses an unsigned comparison. * The passive element text syntax still allows function indexes, e.g. `(elem passive $f1 $f2)`. * Add element type to passive element segments
1 parent d97e8fb commit 35d08d2

File tree

10 files changed

+61
-35
lines changed

10 files changed

+61
-35
lines changed

interpreter/binary/decode.ml

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,12 @@ type stream =
55
name : string;
66
bytes : string;
77
pos : int ref;
8+
has_data_count : bool ref;
89
}
910

1011
exception EOS
1112

12-
let stream name bs = {name; bytes = bs; pos = ref 0}
13+
let stream name bs = {name; bytes = bs; pos = ref 0; has_data_count = ref false}
1314

1415
let len s = String.length s.bytes
1516
let pos s = !(s.pos)
@@ -201,14 +202,18 @@ let memop s =
201202
let offset = vu32 s in
202203
Int32.to_int align, offset
203204

205+
let check_data_count s =
206+
require !(s.has_data_count) s (pos s - 1) "data count section required"
207+
204208
let misc_instr s =
205209
let pos = pos s in
206210
match op s with
207211
| 0x08 ->
212+
check_data_count s;
208213
let x = at var s in
209214
zero_flag s;
210215
memory_init x
211-
| 0x09 -> data_drop (at var s)
216+
| 0x09 -> check_data_count s; data_drop (at var s)
212217
| 0x0a -> zero_flag s; zero_flag s; memory_copy
213218
| 0x0b -> zero_flag s; memory_fill
214219
| 0x0c ->
@@ -640,8 +645,16 @@ let passive_elem s =
640645
Func x
641646
| _ -> error s (pos s - 1) "invalid elem"
642647

648+
let active_elem_segment s =
649+
FuncRefType, vec (at active_elem) s
650+
651+
let passive_elem_segment s =
652+
let etype = elem_type s in
653+
let init = vec (at passive_elem) s in
654+
etype, init
655+
643656
let table_segment s =
644-
segment (vec (at active_elem)) (vec (at passive_elem)) s
657+
segment active_elem_segment passive_elem_segment s
645658

646659
let elem_section s =
647660
section `ElemSection (vec (at table_segment)) [] s
@@ -659,7 +672,10 @@ let data_section s =
659672
(* DataCount section *)
660673

661674
let data_count_section s =
662-
section `DataCountSection (opt vu32 true) None s
675+
let contents s =
676+
s.has_data_count := true;
677+
opt vu32 true s
678+
in section `DataCountSection contents None s
663679

664680

665681
(* Custom section *)

interpreter/binary/encode.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,9 @@ let encode m =
501501
| Func x -> u8 0xd2; var x; end_ ()
502502

503503
let table_segment seg =
504-
segment (vec active_elem) (vec passive_elem) seg
504+
let active (_,init) = vec active_elem init in
505+
let passive (etype,init) = elem_type etype; vec passive_elem init in
506+
segment active passive seg
505507

506508
let elem_section elems =
507509
section 9 (vec table_segment) elems (elems <> [])

interpreter/exec/eval.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -445,7 +445,7 @@ let elems_list inst init =
445445
let create_elems (inst : module_inst) (seg : table_segment) : elems_inst =
446446
match seg.it with
447447
| Active _ -> ref None
448-
| Passive init -> ref (Some (elems_list inst init))
448+
| Passive (_,init) -> ref (Some (elems_list inst init))
449449

450450
let create_data (inst : module_inst) (seg : memory_segment) : data_inst =
451451
match seg.it with
@@ -460,7 +460,7 @@ let init_func (inst : module_inst) (func : func_inst) =
460460

461461
let init_table (inst : module_inst) (seg : table_segment) =
462462
match seg.it with
463-
| Active {index; offset = const; init} ->
463+
| Active {index; offset = const; init = (_,init)} ->
464464
let tab = table inst index in
465465
let offset = i32 (eval_const inst const) const.at in
466466
let elems = elems_list inst init in

interpreter/runtime/memory.ml

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -151,23 +151,24 @@ let check_str_bounds bs a =
151151
let check_bounds mem a = if I64.gt_u a (bound mem) then raise Bounds
152152

153153
let init mem bs d s n =
154-
let n' = Int64.of_int32 n in
155-
let rec loop d s n =
156-
if n > 0l then begin
157-
check_str_bounds bs s;
158-
let b = (Char.code bs.[Int64.to_int s]) in
159-
store_byte mem d b;
154+
let load_str_byte a =
155+
try Char.code bs.[Int64.to_int a]
156+
with _ -> raise Bounds
157+
in let rec loop d s n =
158+
if I32.gt_u n 0l then begin
159+
store_byte mem d (load_str_byte s);
160160
loop (Int64.add d 1L) (Int64.add s 1L) (Int32.sub n 1l)
161161
end
162162
in loop d s n;
163+
let n' = I64_convert.extend_i32_u n in
163164
check_bounds mem (Int64.add d n');
164165
check_str_bounds bs (Int64.add s n')
165166

166167
let copy mem d s n =
167-
let n' = Int64.of_int32 n in
168-
let overlap = I64.lt_s Int64.(abs (sub d s)) n' in
168+
let n' = I64_convert.extend_i32_u n in
169+
let overlap = I64.lt_u Int64.(abs (sub d s)) n' in
169170
let rec loop d s n dx =
170-
if n > 0l then begin
171+
if I32.gt_u n 0l then begin
171172
store_byte mem d (load_byte mem s);
172173
loop (Int64.add d dx) (Int64.add s dx) (Int32.sub n 1l) dx
173174
end
@@ -180,9 +181,9 @@ let copy mem d s n =
180181

181182
let fill mem a v n =
182183
let rec loop a n =
183-
if n > 0l then begin
184+
if I32.gt_u n 0l then begin
184185
store_byte mem a v;
185186
loop (Int64.add a 1L) (Int32.sub n 1l)
186187
end
187188
in loop a n;
188-
check_bounds mem Int64.(add a (of_int32 n))
189+
check_bounds mem (Int64.add a (I64_convert.extend_i32_u n))

interpreter/runtime/table.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,9 @@ let init tab es d s n =
6464
check_bounds tab (Int32.add d n)
6565

6666
let copy tab d s n =
67-
let overlap = I32.lt_s Int32.(abs (sub d s)) n in
67+
let overlap = I32.lt_u Int32.(abs (sub d s)) n in
6868
let rec loop d s n dx =
69-
if n > 0l then begin
69+
if I32.gt_u n 0l then begin
7070
store tab d (load tab s);
7171
loop (Int32.add d dx) (Int32.add s dx) (Int32.sub n 1l) dx
7272
end

interpreter/syntax/ast.ml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ and elem' =
150150
| Null
151151
| Func of var
152152

153-
type table_segment = elem list segment
153+
type table_segment = (elem_type * (elem list)) segment
154154
type memory_segment = string segment
155155

156156

@@ -196,8 +196,8 @@ and module_' =
196196
memories : memory list;
197197
funcs : func list;
198198
start : var option;
199-
elems : elem list segment list;
200-
data : string segment list;
199+
elems : table_segment list;
200+
data : memory_segment list;
201201
imports : import list;
202202
exports : export list;
203203
}

interpreter/text/arrange.ml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,9 @@ let passive_elem el =
313313
| Func x -> Node ("ref.func", [atom var x])
314314

315315
let elems seg =
316-
segment "elem" (list active_elem) (list passive_elem) seg
316+
let active (_,init) = list active_elem init in
317+
let passive (etype,init) = atom elem_type etype :: list passive_elem init in
318+
segment "elem" active passive seg
317319

318320
let data seg =
319321
segment "data" break_bytes break_bytes seg

interpreter/text/parser.mly

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -572,6 +572,7 @@ offset :
572572
elemref :
573573
| LPAR REF_NULL RPAR { let at = at () in fun c -> Null @@ at }
574574
| LPAR REF_FUNC var RPAR { let at = at () in fun c -> Func ($3 c func) @@ at }
575+
| var { let at = at () in fun c -> Func ($1 c func) @@ at }
575576
576577
passive_elemref_list :
577578
| /* empty */ { fun c -> [] }
@@ -583,25 +584,28 @@ active_elemref_list :
583584
fun c lookup -> List.map f ($1 c lookup) }
584585
585586
elem :
586-
| LPAR ELEM bind_var_opt PASSIVE passive_elemref_list RPAR
587+
| LPAR ELEM bind_var_opt PASSIVE elem_type passive_elemref_list RPAR
587588
{ let at = at () in
588589
fun c -> ignore ($3 c anon_elem bind_elem);
589-
fun () -> Passive ($5 c) @@ at }
590+
fun () -> Passive ($5, ($6 c)) @@ at }
590591
| LPAR ELEM bind_var var offset active_elemref_list RPAR
591592
{ let at = at () in
592593
fun c -> ignore (bind_elem c $3);
593594
fun () ->
594-
Active {index = $4 c table; offset = $5 c; init = $6 c func} @@ at }
595+
let init = FuncRefType, ($6 c func) in
596+
Active {index = $4 c table; offset = $5 c; init} @@ at }
595597
| LPAR ELEM var offset active_elemref_list RPAR
596598
{ let at = at () in
597599
fun c -> ignore (anon_elem c);
598600
fun () ->
599-
Active {index = $3 c table; offset = $4 c; init = $5 c func} @@ at }
601+
let init = FuncRefType, $5 c func in
602+
Active {index = $3 c table; offset = $4 c; init} @@ at }
600603
| LPAR ELEM offset active_elemref_list RPAR /* Sugar */
601604
{ let at = at () in
602605
fun c -> ignore (anon_elem c);
603606
fun () ->
604-
Active {index = 0l @@ at; offset = $3 c; init = $4 c func} @@ at }
607+
let init = FuncRefType, $4 c func in
608+
Active {index = 0l @@ at; offset = $3 c; init} @@ at }
605609
606610
table :
607611
| LPAR TABLE bind_var_opt table_fields RPAR
@@ -623,7 +627,8 @@ table_fields :
623627
| elem_type LPAR ELEM active_elemref_list RPAR /* Sugar */
624628
{ fun c x at ->
625629
let offset = [i32_const (0l @@ at) @@ at] @@ at in
626-
let init = $4 c func in let size = Int32.of_int (List.length init) in
630+
let init' = $4 c func in let size = Int32.of_int (List.length init') in
631+
let init = FuncRefType, init' in
627632
[{ttype = TableType ({min = size; max = Some size}, $1)} @@ at],
628633
[Active {index = x; offset; init} @@ at],
629634
[], [] }

interpreter/valid/valid.ml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -431,11 +431,11 @@ let check_elemref (c : context) (el : elem) =
431431

432432
let check_elem (c : context) (seg : table_segment) =
433433
match seg.it with
434-
| Active {index; offset; init} ->
434+
| Active {index; offset; init = (_,init)} ->
435435
ignore (table c index);
436436
check_const c offset I32Type;
437437
List.iter (check_elemref c) init
438-
| Passive init ->
438+
| Passive (etype,init) ->
439439
List.iter (check_elemref c) init
440440

441441
let check_data (c : context) (seg : memory_segment) =

test/core/bulk.wast

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
(module
77
(table 3 funcref)
8-
(elem passive (ref.func 0) (ref.null) (ref.func 1))
8+
(elem passive funcref (ref.func 0) (ref.null) (ref.func 1))
99
(func)
1010
(func))
1111

@@ -182,7 +182,7 @@
182182
;; table.init
183183
(module
184184
(table 3 funcref)
185-
(elem passive
185+
(elem passive funcref
186186
(ref.func $zero) (ref.func $one) (ref.func $zero) (ref.func $one))
187187

188188
(func $zero (result i32) (i32.const 0))
@@ -227,7 +227,7 @@
227227
(module
228228
(table 1 funcref)
229229
(func $f)
230-
(elem $p passive (ref.func $f))
230+
(elem $p passive funcref (ref.func $f))
231231
(elem $a 0 (i32.const 0) $f)
232232

233233
(func (export "drop_passive") (elem.drop $p))

0 commit comments

Comments
 (0)