1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
| # RUN: llc -verify-machineinstrs -mtriple=aarch64-none-linux-gnu \
# RUN: -start-before aarch64-speculation-hardening -o - %s \
# RUN: | FileCheck %s --dump-input-on-failure
# Check that the speculation hardening pass generates code as expected for
# basic blocks ending with a variety of branch patterns:
# - (1) no branches (fallthrough)
# - (2) one unconditional branch
# - (3) one conditional branch + fall-through
# - (4) one conditional branch + one unconditional branch
# - other direct branches don't seem to be generated by the AArch64 codegen
--- |
define void @nobranch_fallthrough(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @uncondbranch(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @condbranch_fallthrough(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @condbranch_uncondbranch(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @indirectbranch(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
; Also check that a non-default temporary register gets picked correctly to
; transfer the SP to to and it with the taint register when the default
; temporary isn't available.
define void @indirect_call_x17(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
@g = common dso_local local_unnamed_addr global i64 (...)* null, align 8
define void @indirect_tailcall_x17(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @indirect_call_lr(i32 %a, i32 %b) speculative_load_hardening {
ret void
}
define void @RS_cannot_find_available_regs() speculative_load_hardening {
ret void
}
...
---
name: nobranch_fallthrough
tracksRegLiveness: true
body: |
; CHECK-LABEL: nobranch_fallthrough
bb.0:
successors: %bb.1
liveins: $w0, $w1
; CHECK-NOT: csel
bb.1:
liveins: $w0
RET undef $lr, implicit $w0
...
---
name: uncondbranch
tracksRegLiveness: true
body: |
; CHECK-LABEL: uncondbranch
bb.0:
successors: %bb.1
liveins: $w0, $w1
B %bb.1
; CHECK-NOT: csel
bb.1:
liveins: $w0
RET undef $lr, implicit $w0
...
---
name: condbranch_fallthrough
tracksRegLiveness: true
body: |
; CHECK-LABEL: condbranch_fallthrough
bb.0:
successors: %bb.1, %bb.2
liveins: $w0, $w1
$wzr = SUBSWrs renamable $w0, renamable $w1, 0, implicit-def $nzcv, implicit-def $nzcv
Bcc 11, %bb.2, implicit $nzcv
; CHECK: b.lt [[BB_LT_T:\.LBB[0-9_]+]]
bb.1:
liveins: $nzcv, $w0
; CHECK: csel x16, x16, xzr, ge
RET undef $lr, implicit $w0
bb.2:
liveins: $nzcv, $w0
; CHECK: csel x16, x16, xzr, lt
RET undef $lr, implicit $w0
...
---
name: condbranch_uncondbranch
tracksRegLiveness: true
body: |
; CHECK-LABEL: condbranch_uncondbranch
bb.0:
successors: %bb.1, %bb.2
liveins: $w0, $w1
$wzr = SUBSWrs renamable $w0, renamable $w1, 0, implicit-def $nzcv, implicit-def $nzcv
Bcc 11, %bb.2, implicit $nzcv
B %bb.1, implicit $nzcv
; CHECK: b.lt [[BB_LT_T:\.LBB[0-9_]+]]
bb.1:
liveins: $nzcv, $w0
; CHECK: csel x16, x16, xzr, ge
RET undef $lr, implicit $w0
bb.2:
liveins: $nzcv, $w0
; CHECK: csel x16, x16, xzr, lt
RET undef $lr, implicit $w0
...
---
name: indirectbranch
tracksRegLiveness: true
body: |
; Check that no instrumentation is done on indirect branches (for now).
; CHECK-LABEL: indirectbranch
bb.0:
successors: %bb.1, %bb.2
liveins: $x0
BR $x0
bb.1:
liveins: $x0
; CHECK-NOT: csel
RET undef $lr, implicit $x0
bb.2:
liveins: $x0
; CHECK-NOT: csel
RET undef $lr, implicit $x0
...
---
name: indirect_call_x17
tracksRegLiveness: true
body: |
bb.0:
liveins: $x17
; CHECK-LABEL: indirect_call_x17
; CHECK: mov x0, sp
; CHECK: and x0, x0, x16
; CHECK: mov sp, x0
; CHECK: blr x17
BLR killed renamable $x17, implicit-def dead $lr, implicit $sp
RET undef $lr, implicit undef $w0
...
---
name: indirect_tailcall_x17
tracksRegLiveness: true
body: |
bb.0:
liveins: $x0
; CHECK-LABEL: indirect_tailcall_x17
; CHECK: mov x1, sp
; CHECK: and x1, x1, x16
; CHECK: mov sp, x1
; CHECK: br x17
$x8 = ADRP target-flags(aarch64-page) @g
$x17 = LDRXui killed $x8, target-flags(aarch64-pageoff, aarch64-nc) @g
TCRETURNri killed $x17, 0, implicit $sp, implicit $x0
...
---
name: indirect_call_lr
tracksRegLiveness: true
body: |
bb.0:
; CHECK-LABEL: indirect_call_lr
; CHECK: mov x1, sp
; CHECK-NEXT: and x1, x1, x16
; CHECK-NEXT: mov sp, x1
; CHECK-NEXT: blr x30
liveins: $x0, $lr
BLR killed renamable $lr, implicit-def dead $lr, implicit $sp, implicit-def $sp, implicit-def $w0
$w0 = nsw ADDWri killed $w0, 1, 0
RET undef $lr, implicit $w0
...
---
name: RS_cannot_find_available_regs
tracksRegLiveness: true
body: |
bb.0:
; In the rare case when no free temporary register is available for the
; propagate taint-to-sp operation, just put in a full speculation barrier
; (isb+dsb sy) at the start of the basic block. And don't put masks on
; instructions for the rest of the basic block, since speculation in that
; basic block was already done, so no need to do masking.
; CHECK-LABEL: RS_cannot_find_available_regs
; CHECK: dsb sy
; CHECK-NEXT: isb
; CHECK-NEXT: ldr x0, [x0]
; The following 2 instructions come from propagating the taint encoded in
; sp at function entry to x16. It turns out the taint info in x16 is not
; used in this function, so those instructions could be optimized away. An
; optimization for later if it turns out this situation occurs often enough.
; CHECK-NEXT: cmp sp, #0
; CHECK-NEXT: csetm x16, ne
; CHECK-NEXT: ret
liveins: $x0, $x1, $x2, $x3, $x4, $x5, $x6, $x7, $x8, $x9, $x10, $x11, $x12, $x13, $x14, $x15, $x17, $x18, $x19, $x20, $x21, $x22, $x23, $x24, $x25, $x26, $x27, $x28, $fp, $lr
$x0 = LDRXui killed $x0, 0
RET undef $lr, implicit $x0
...
|