Skip to content

Commit 66b4973

Browse files
committed
cranelift: Add WAT tests for accessing dynamic memories with the same index but different offsets
The bounds check comparison is GVN'd but we still branch on values we should know will always be true if we get this far in the code. This is actual `br_if`s in the non-Spectre code and `select_spectre_guard`s that we should know will always go a certain way if we have Spectre mitigations enabled. Improving the non-Spectre case is pretty straightforward: walk the dominator tree and remember which values we've already branched on at this point, and therefore we can simplify any further conditional branches on those same values into direct jumps. Improving the Spectre case requires something that is morally the same, but has a few snags: * We don't have actual `br_if`s to determine whether the bounds checking condition succeeded or not. We need to instead reason about dominating `select_spectre_guard; {load, store}` instruction pairs. * We have to be SUPER careful about reasoning "through" `select_spectre_guard`s. Our general rule is never to do that, since it could break the speculative execution sandboxing that the instruction is designed for.
1 parent 50a45bb commit 66b4973

2 files changed

Lines changed: 276 additions & 0 deletions

File tree

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
;;! target = "x86_64"
2+
;;!
3+
;;! optimize = true
4+
;;!
5+
;;! settings = [
6+
;;! "enable_heap_access_spectre_mitigation=false",
7+
;;! "opt_level=speed_and_size",
8+
;;! "use_egraphs=true"
9+
;;! ]
10+
;;!
11+
;;! [globals.vmctx]
12+
;;! type = "i64"
13+
;;! vmctx = true
14+
;;!
15+
;;! [globals.heap_base]
16+
;;! type = "i64"
17+
;;! load = { base = "vmctx", offset = 0 }
18+
;;!
19+
;;! [globals.heap_bound]
20+
;;! type = "i64"
21+
;;! load = { base = "vmctx", offset = 8 }
22+
;;!
23+
;;! [[heaps]]
24+
;;! base = "heap_base"
25+
;;! min_size = 0
26+
;;! offset_guard_size = 0x0000ffff
27+
;;! index_type = "i32"
28+
;;! style = { kind = "dynamic", bound = "heap_bound" }
29+
30+
(module
31+
(memory (export "memory") 0)
32+
33+
(func (export "loads") (param i32) (result i32 i32 i32)
34+
;; Within the guard region.
35+
local.get 0
36+
i32.load offset=0
37+
;; Also within the guard region, bounds check should GVN with previous.
38+
local.get 0
39+
i32.load offset=4
40+
;; Outside the guard region, needs additional bounds checks.
41+
local.get 0
42+
i32.load offset=0x000fffff
43+
)
44+
45+
;; Same as above, but for stores.
46+
(func (export "stores") (param i32 i32 i32 i32)
47+
local.get 0
48+
local.get 1
49+
i32.store offset=0
50+
local.get 0
51+
local.get 2
52+
i32.store offset=4
53+
local.get 0
54+
local.get 3
55+
i32.store offset=0x000fffff
56+
)
57+
)
58+
59+
;; function u0:0(i32, i64 vmctx) -> i32, i32, i32 fast {
60+
;; gv0 = vmctx
61+
;; gv1 = load.i64 notrap aligned gv0+8
62+
;; gv2 = load.i64 notrap aligned gv0
63+
;;
64+
;; block0(v0: i32, v1: i64):
65+
;; @0047 v6 = load.i64 notrap aligned v1+8
66+
;; @0047 v5 = uextend.i64 v0
67+
;; @0047 v7 = icmp ugt v5, v6
68+
;; @0047 brif v7, block2, block3
69+
;;
70+
;; block2 cold:
71+
;; @0047 trap heap_oob
72+
;;
73+
;; block3:
74+
;; @0047 v8 = load.i64 notrap aligned v1
75+
;; @0047 v9 = iadd v8, v5
76+
;; @0047 v10 = load.i32 little heap v9
77+
;; v2 -> v10
78+
;; @004c brif.i8 v7, block4, block5
79+
;;
80+
;; block4 cold:
81+
;; @004c trap heap_oob
82+
;;
83+
;; block5:
84+
;; v27 = iconst.i64 4
85+
;; @004c v16 = iadd.i64 v9, v27 ; v27 = 4
86+
;; @004c v17 = load.i32 little heap v16
87+
;; v3 -> v17
88+
;; @0051 v19 = iconst.i64 0x0010_0003
89+
;; @0051 v20 = uadd_overflow_trap.i64 v5, v19, heap_oob ; v19 = 0x0010_0003
90+
;; @0051 v22 = icmp ugt v20, v6
91+
;; @0051 brif v22, block6, block7
92+
;;
93+
;; block6 cold:
94+
;; @0051 trap heap_oob
95+
;;
96+
;; block7:
97+
;; @0051 v23 = load.i64 notrap aligned v1
98+
;; @0051 v24 = iadd v23, v5
99+
;; v28 = iconst.i64 0x000f_ffff
100+
;; @0051 v25 = iadd v24, v28 ; v28 = 0x000f_ffff
101+
;; @0051 v26 = load.i32 little heap v25
102+
;; v4 -> v26
103+
;; @0056 jump block1
104+
;;
105+
;; block1:
106+
;; @0056 return v10, v17, v26
107+
;; }
108+
;;
109+
;; function u0:1(i32, i32, i32, i32, i64 vmctx) fast {
110+
;; gv0 = vmctx
111+
;; gv1 = load.i64 notrap aligned gv0+8
112+
;; gv2 = load.i64 notrap aligned gv0
113+
;;
114+
;; block0(v0: i32, v1: i32, v2: i32, v3: i32, v4: i64):
115+
;; @005d v6 = load.i64 notrap aligned v4+8
116+
;; @005d v5 = uextend.i64 v0
117+
;; @005d v7 = icmp ugt v5, v6
118+
;; @005d brif v7, block2, block3
119+
;;
120+
;; block2 cold:
121+
;; @005d trap heap_oob
122+
;;
123+
;; block3:
124+
;; @005d v8 = load.i64 notrap aligned v4
125+
;; @005d v9 = iadd v8, v5
126+
;; @005d store.i32 little heap v1, v9
127+
;; @0064 brif.i8 v7, block4, block5
128+
;;
129+
;; block4 cold:
130+
;; @0064 trap heap_oob
131+
;;
132+
;; block5:
133+
;; v24 = iconst.i64 4
134+
;; @0064 v15 = iadd.i64 v9, v24 ; v24 = 4
135+
;; @0064 store.i32 little heap v2, v15
136+
;; @006b v17 = iconst.i64 0x0010_0003
137+
;; @006b v18 = uadd_overflow_trap.i64 v5, v17, heap_oob ; v17 = 0x0010_0003
138+
;; @006b v20 = icmp ugt v18, v6
139+
;; @006b brif v20, block6, block7
140+
;;
141+
;; block6 cold:
142+
;; @006b trap heap_oob
143+
;;
144+
;; block7:
145+
;; @006b v21 = load.i64 notrap aligned v4
146+
;; @006b v22 = iadd v21, v5
147+
;; v25 = iconst.i64 0x000f_ffff
148+
;; @006b v23 = iadd v22, v25 ; v25 = 0x000f_ffff
149+
;; @006b store.i32 little heap v3, v23
150+
;; @0070 jump block1
151+
;;
152+
;; block1:
153+
;; @0070 return
154+
;; }
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
;;! target = "x86_64"
2+
;;!
3+
;;! optimize = true
4+
;;!
5+
;;! settings = [
6+
;;! "enable_heap_access_spectre_mitigation=true",
7+
;;! "opt_level=speed_and_size",
8+
;;! "use_egraphs=true"
9+
;;! ]
10+
;;!
11+
;;! [globals.vmctx]
12+
;;! type = "i64"
13+
;;! vmctx = true
14+
;;!
15+
;;! [globals.heap_base]
16+
;;! type = "i64"
17+
;;! load = { base = "vmctx", offset = 0 }
18+
;;!
19+
;;! [globals.heap_bound]
20+
;;! type = "i64"
21+
;;! load = { base = "vmctx", offset = 8 }
22+
;;!
23+
;;! [[heaps]]
24+
;;! base = "heap_base"
25+
;;! min_size = 0
26+
;;! offset_guard_size = 0x0000ffff
27+
;;! index_type = "i32"
28+
;;! style = { kind = "dynamic", bound = "heap_bound" }
29+
30+
(module
31+
(memory (export "memory") 0)
32+
33+
(func (export "loads") (param i32) (result i32 i32 i32)
34+
;; Within the guard region.
35+
local.get 0
36+
i32.load offset=0
37+
;; Also within the guard region, bounds check should GVN with previous.
38+
local.get 0
39+
i32.load offset=4
40+
;; Outside the guard region, needs additional bounds checks.
41+
local.get 0
42+
i32.load offset=0x000fffff
43+
)
44+
45+
;; Same as above, but for stores.
46+
(func (export "stores") (param i32 i32 i32 i32)
47+
local.get 0
48+
local.get 1
49+
i32.store offset=0
50+
local.get 0
51+
local.get 2
52+
i32.store offset=4
53+
local.get 0
54+
local.get 3
55+
i32.store offset=0x000fffff
56+
)
57+
)
58+
59+
;; function u0:0(i32, i64 vmctx) -> i32, i32, i32 fast {
60+
;; gv0 = vmctx
61+
;; gv1 = load.i64 notrap aligned gv0+8
62+
;; gv2 = load.i64 notrap aligned gv0
63+
;;
64+
;; block0(v0: i32, v1: i64):
65+
;; @0047 v6 = load.i64 notrap aligned v1+8
66+
;; @0047 v7 = load.i64 notrap aligned v1
67+
;; @0047 v5 = uextend.i64 v0
68+
;; @0047 v10 = icmp ugt v5, v6
69+
;; @0047 v9 = iconst.i64 0
70+
;; @0047 v8 = iadd v7, v5
71+
;; @0047 v11 = select_spectre_guard v10, v9, v8 ; v9 = 0
72+
;; @0047 v12 = load.i32 little heap v11
73+
;; v2 -> v12
74+
;; v33 = iconst.i64 4
75+
;; @004c v17 = iadd v8, v33 ; v33 = 4
76+
;; @004c v20 = select_spectre_guard v10, v9, v17 ; v9 = 0
77+
;; @004c v21 = load.i32 little heap v20
78+
;; v3 -> v21
79+
;; @0051 v23 = iconst.i64 0x0010_0003
80+
;; @0051 v24 = uadd_overflow_trap v5, v23, heap_oob ; v23 = 0x0010_0003
81+
;; @0051 v30 = icmp ugt v24, v6
82+
;; v34 = iconst.i64 0x000f_ffff
83+
;; @0051 v28 = iadd v8, v34 ; v34 = 0x000f_ffff
84+
;; @0051 v31 = select_spectre_guard v30, v9, v28 ; v9 = 0
85+
;; @0051 v32 = load.i32 little heap v31
86+
;; v4 -> v32
87+
;; @0056 jump block1
88+
;;
89+
;; block1:
90+
;; @0056 return v12, v21, v32
91+
;; }
92+
;;
93+
;; function u0:1(i32, i32, i32, i32, i64 vmctx) fast {
94+
;; gv0 = vmctx
95+
;; gv1 = load.i64 notrap aligned gv0+8
96+
;; gv2 = load.i64 notrap aligned gv0
97+
;;
98+
;; block0(v0: i32, v1: i32, v2: i32, v3: i32, v4: i64):
99+
;; @005d v6 = load.i64 notrap aligned v4+8
100+
;; @005d v7 = load.i64 notrap aligned v4
101+
;; @005d v5 = uextend.i64 v0
102+
;; @005d v10 = icmp ugt v5, v6
103+
;; @005d v9 = iconst.i64 0
104+
;; @005d v8 = iadd v7, v5
105+
;; @005d v11 = select_spectre_guard v10, v9, v8 ; v9 = 0
106+
;; @005d store little heap v1, v11
107+
;; v30 = iconst.i64 4
108+
;; @0064 v16 = iadd v8, v30 ; v30 = 4
109+
;; @0064 v19 = select_spectre_guard v10, v9, v16 ; v9 = 0
110+
;; @0064 store little heap v2, v19
111+
;; @006b v21 = iconst.i64 0x0010_0003
112+
;; @006b v22 = uadd_overflow_trap v5, v21, heap_oob ; v21 = 0x0010_0003
113+
;; @006b v28 = icmp ugt v22, v6
114+
;; v31 = iconst.i64 0x000f_ffff
115+
;; @006b v26 = iadd v8, v31 ; v31 = 0x000f_ffff
116+
;; @006b v29 = select_spectre_guard v28, v9, v26 ; v9 = 0
117+
;; @006b store little heap v3, v29
118+
;; @0070 jump block1
119+
;;
120+
;; block1:
121+
;; @0070 return
122+
;; }

0 commit comments

Comments
 (0)