Skip to content

Commit 91b67b6

Browse files
committed
[BOLT] Gadget scanner: account for BRK when searching for auth oracles
An authenticated pointer can be explicitly checked by the compiler via a sequence of instructions that executes BRK on failure. It is important to recognize such BRK instruction as checking every register (as it is expected to immediately trigger an abnormal program termination) to prevent false positive reports about authentication oracles: autia x2, x3 autia x0, x1 ; neither x0 nor x2 are checked at this point eor x16, x0, x0, lsl #1 tbz x16, #62, on_success ; marks x0 as checked ; end of BB: for x2 to be checked here, it must be checked in both ; successor basic blocks on_failure: brk 0xc470 on_success: ; x2 is checked ldr x1, [x2] ; marks x2 as checked
1 parent d20efbe commit 91b67b6

File tree

6 files changed

+75
-35
lines changed

6 files changed

+75
-35
lines changed

bolt/include/bolt/Core/MCPlusBuilder.h

+14
Original file line numberDiff line numberDiff line change
@@ -706,6 +706,20 @@ class MCPlusBuilder {
706706
return false;
707707
}
708708

709+
/// Returns true if Inst is a trap instruction.
710+
///
711+
/// Tests if Inst is an instruction that immediately causes an abnormal
712+
/// program termination, for example when a security violation is detected
713+
/// by a compiler-inserted check.
714+
///
715+
/// @note An implementation of this method should likely return false for
716+
/// calls to library functions like abort(), as it is possible that the
717+
/// execution state is partially attacker-controlled at this point.
718+
virtual bool isTrap(const MCInst &Inst) const {
719+
llvm_unreachable("not implemented");
720+
return false;
721+
}
722+
709723
virtual bool isBreakpoint(const MCInst &Inst) const {
710724
llvm_unreachable("not implemented");
711725
return false;

bolt/lib/Passes/PAuthGadgetScanner.cpp

+11-2
Original file line numberDiff line numberDiff line change
@@ -1028,6 +1028,15 @@ class DstSafetyAnalysis {
10281028
dbgs() << ")\n";
10291029
});
10301030

1031+
// If this instruction terminates the program immediately, no
1032+
// authentication oracles are possible past this point.
1033+
if (BC.MIB->isTrap(Point)) {
1034+
LLVM_DEBUG({ traceInst(BC, "Trap instruction found", Point); });
1035+
DstState Next(NumRegs, RegsToTrackInstsFor.getNumTrackedRegisters());
1036+
Next.CannotEscapeUnchecked.set();
1037+
return Next;
1038+
}
1039+
10311040
// If this instruction is reachable by the analysis, a non-empty state will
10321041
// be propagated to it sooner or later. Until then, skip computeNext().
10331042
if (Cur.empty()) {
@@ -1133,8 +1142,8 @@ class DataflowDstSafetyAnalysis
11331142
//
11341143
// A basic block without any successors, on the other hand, can be
11351144
// pessimistically initialized to everything-is-unsafe: this will naturally
1136-
// handle both return and tail call instructions and is harmless for
1137-
// internal indirect branch instructions (such as computed gotos).
1145+
// handle return, trap and tail call instructions. At the same time, it is
1146+
// harmless for internal indirect branch instructions, like computed gotos.
11381147
if (BB.succ_empty())
11391148
return createUnsafeState();
11401149

bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp

+21-3
Original file line numberDiff line numberDiff line change
@@ -386,10 +386,9 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
386386
// the list of successors of this basic block as appropriate.
387387

388388
// Any of the above code sequences assume the fall-through basic block
389-
// is a dead-end BRK instruction (any immediate operand is accepted).
389+
// is a dead-end trap instruction.
390390
const BinaryBasicBlock *BreakBB = BB.getFallthrough();
391-
if (!BreakBB || BreakBB->empty() ||
392-
BreakBB->front().getOpcode() != AArch64::BRK)
391+
if (!BreakBB || BreakBB->empty() || !isTrap(BreakBB->front()))
393392
return std::nullopt;
394393

395394
// Iterate over the instructions of BB in reverse order, matching opcodes
@@ -1745,6 +1744,25 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
17451744
Inst.addOperand(MCOperand::createImm(0));
17461745
}
17471746

1747+
bool isTrap(const MCInst &Inst) const override {
1748+
if (Inst.getOpcode() != AArch64::BRK)
1749+
return false;
1750+
// Only match the immediate values that are likely to indicate this BRK
1751+
// instruction is emitted to terminate the program immediately and not to
1752+
// be handled by a SIGTRAP handler, for example.
1753+
switch (Inst.getOperand(0).getImm()) {
1754+
case 0xc470:
1755+
case 0xc471:
1756+
case 0xc472:
1757+
case 0xc473:
1758+
// Explicit Pointer Authentication check failed, see
1759+
// AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue().
1760+
return true;
1761+
default:
1762+
return false;
1763+
}
1764+
}
1765+
17481766
bool isStorePair(const MCInst &Inst) const {
17491767
const unsigned opcode = Inst.getOpcode();
17501768

bolt/test/binary-analysis/AArch64/gs-pauth-address-checks.s

+22-22
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ resign_xpaci_good:
3131
xpaci x16
3232
cmp x0, x16
3333
b.eq 1f
34-
brk 0x1234
34+
brk 0xc471
3535
1:
3636
pacia x0, x2
3737
ret
@@ -46,7 +46,7 @@ resign_xpacd_good:
4646
xpacd x16
4747
cmp x0, x16
4848
b.eq 1f
49-
brk 0x1234
49+
brk 0xc473
5050
1:
5151
pacda x0, x2
5252
ret
@@ -117,7 +117,7 @@ resign_xpaci_unrelated_auth_and_check:
117117
xpaci x16
118118
cmp x0, x16
119119
b.eq 1f
120-
brk 0x1234
120+
brk 0xc471
121121
1:
122122
pacia x10, x2
123123
ret
@@ -139,7 +139,7 @@ resign_xpaci_wrong_pattern_1:
139139
xpaci x16
140140
cmp x0, x16
141141
b.eq 1f
142-
brk 0x1234
142+
brk 0xc471
143143
1:
144144
pacia x0, x2
145145
ret
@@ -157,7 +157,7 @@ resign_xpaci_wrong_pattern_2:
157157
xpaci x0 // x0 instead of x16
158158
cmp x0, x16
159159
b.eq 1f
160-
brk 0x1234
160+
brk 0xc471
161161
1:
162162
pacia x0, x2
163163
ret
@@ -174,7 +174,7 @@ resign_xpaci_wrong_pattern_3:
174174
xpaci x16
175175
cmp x16, x16 // x16 instead of x0
176176
b.eq 1f
177-
brk 0x1234
177+
brk 0xc471
178178
1:
179179
pacia x0, x2
180180
ret
@@ -191,7 +191,7 @@ resign_xpaci_wrong_pattern_4:
191191
xpaci x16
192192
cmp x0, x0 // x0 instead of x16
193193
b.eq 1f
194-
brk 0x1234
194+
brk 0xc471
195195
1:
196196
pacia x0, x2
197197
ret
@@ -208,7 +208,7 @@ resign_xpaci_wrong_pattern_5:
208208
mov x16, x16 // replace xpaci with a no-op instruction
209209
cmp x0, x16
210210
b.eq 1f
211-
brk 0x1234
211+
brk 0xc471
212212
1:
213213
pacia x0, x2
214214
ret
@@ -228,7 +228,7 @@ resign_xpaclri_good:
228228
xpaclri
229229
cmp x30, x16
230230
b.eq 1f
231-
brk 0x1234
231+
brk 0xc471
232232
1:
233233
pacia x30, x2
234234

@@ -246,7 +246,7 @@ xpaclri_check_keeps_lr_safe:
246246
xpaclri // clobbers LR
247247
cmp x30, x16
248248
b.eq 1f
249-
brk 0x1234 // marks LR as trusted and safe-to-dereference
249+
brk 0xc471 // marks LR as trusted and safe-to-dereference
250250
1:
251251
ret // not reporting non-protected return
252252
.size xpaclri_check_keeps_lr_safe, .-xpaclri_check_keeps_lr_safe
@@ -265,7 +265,7 @@ xpaclri_check_requires_safe_lr:
265265
xpaclri
266266
cmp x30, x16
267267
b.eq 1f
268-
brk 0x1234
268+
brk 0xc471
269269
1:
270270
ret
271271
.size xpaclri_check_requires_safe_lr, .-xpaclri_check_requires_safe_lr
@@ -283,7 +283,7 @@ resign_xpaclri_wrong_reg:
283283
xpaclri // ... but xpaclri still operates on x30
284284
cmp x20, x16
285285
b.eq 1f
286-
brk 0x1234
286+
brk 0xc471
287287
1:
288288
pacia x20, x2
289289

@@ -303,7 +303,7 @@ resign_checked_not_authenticated:
303303
xpaci x16
304304
cmp x0, x16
305305
b.eq 1f
306-
brk 0x1234
306+
brk 0xc471
307307
1:
308308
pacia x0, x2
309309
ret
@@ -323,7 +323,7 @@ resign_checked_before_authenticated:
323323
xpaci x16
324324
cmp x0, x16
325325
b.eq 1f
326-
brk 0x1234
326+
brk 0xc471
327327
1:
328328
autib x0, x1
329329
pacia x0, x2
@@ -339,7 +339,7 @@ resign_high_bits_tbz_good:
339339
autib x0, x1
340340
eor x16, x0, x0, lsl #1
341341
tbz x16, #62, 1f
342-
brk 0x1234
342+
brk 0xc471
343343
1:
344344
pacia x0, x2
345345
ret
@@ -378,7 +378,7 @@ resign_high_bits_tbz_wrong_bit:
378378
autib x0, x1
379379
eor x16, x0, x0, lsl #1
380380
tbz x16, #63, 1f
381-
brk 0x1234
381+
brk 0xc471
382382
1:
383383
pacia x0, x2
384384
ret
@@ -393,7 +393,7 @@ resign_high_bits_tbz_wrong_shift_amount:
393393
autib x0, x1
394394
eor x16, x0, x0, lsl #2
395395
tbz x16, #62, 1f
396-
brk 0x1234
396+
brk 0xc471
397397
1:
398398
pacia x0, x2
399399
ret
@@ -408,7 +408,7 @@ resign_high_bits_tbz_wrong_shift_type:
408408
autib x0, x1
409409
eor x16, x0, x0, lsr #1
410410
tbz x16, #62, 1f
411-
brk 0x1234
411+
brk 0xc471
412412
1:
413413
pacia x0, x2
414414
ret
@@ -423,7 +423,7 @@ resign_high_bits_tbz_wrong_pattern_1:
423423
autib x0, x1
424424
eor x16, x0, x0, lsl #1
425425
tbz x17, #62, 1f
426-
brk 0x1234
426+
brk 0xc471
427427
1:
428428
pacia x0, x2
429429
ret
@@ -438,7 +438,7 @@ resign_high_bits_tbz_wrong_pattern_2:
438438
autib x0, x1
439439
eor x16, x10, x0, lsl #1
440440
tbz x16, #62, 1f
441-
brk 0x1234
441+
brk 0xc471
442442
1:
443443
pacia x0, x2
444444
ret
@@ -453,7 +453,7 @@ resign_high_bits_tbz_wrong_pattern_3:
453453
autib x0, x1
454454
eor x16, x0, x10, lsl #1
455455
tbz x16, #62, 1f
456-
brk 0x1234
456+
brk 0xc471
457457
1:
458458
pacia x0, x2
459459
ret
@@ -648,7 +648,7 @@ many_checked_regs:
648648
xpacd x16 // ...
649649
cmp x2, x16 // ...
650650
b.eq 2f // end of basic block
651-
brk 0x1234
651+
brk 0xc473
652652
2:
653653
pacdza x0
654654
pacdza x1

bolt/test/binary-analysis/AArch64/gs-pauth-authentication-oracles.s

+4-5
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ good_explicit_check:
7979
autia x0, x1
8080
eor x16, x0, x0, lsl #1
8181
tbz x16, #62, 1f
82-
brk 0x1234
82+
brk 0xc470
8383
1:
8484
ret
8585
.size good_explicit_check, .-good_explicit_check
@@ -301,7 +301,7 @@ good_explicit_check_multi_bb:
301301
1:
302302
eor x16, x0, x0, lsl #1
303303
tbz x16, #62, 2f
304-
brk 0x1234
304+
brk 0xc470
305305
2:
306306
cbz x1, 3f
307307
nop
@@ -632,16 +632,15 @@ good_address_arith_nocfg:
632632
.globl good_explicit_check_unrelated_reg
633633
.type good_explicit_check_unrelated_reg,@function
634634
good_explicit_check_unrelated_reg:
635-
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function good_explicit_check_unrelated_reg, basic block {{[^,]+}}, at address
636-
// FIXME: The below instruction is not an authentication oracle
635+
// CHECK-NOT: good_explicit_check_unrelated_reg
637636
autia x2, x3 // One of possible execution paths after this instruction
638637
// ends at BRK below, thus BRK used as a trap instruction
639638
// should formally "check everything" not to introduce
640639
// false-positive here.
641640
autia x0, x1
642641
eor x16, x0, x0, lsl #1
643642
tbz x16, #62, 1f
644-
brk 0x1234
643+
brk 0xc470
645644
1:
646645
ldr x4, [x2] // Right before this instruction X2 is checked - this
647646
// should be propagated to the basic block ending with

bolt/test/binary-analysis/AArch64/gs-pauth-signing-oracles.s

+3-3
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ good_sign_auted_checked_brk:
5757
autda x0, x2
5858
eor x16, x0, x0, lsl #1
5959
tbz x16, #62, 1f
60-
brk 0x1234
60+
brk 0xc472
6161
1:
6262
pacda x0, x1
6363
ret
@@ -351,7 +351,7 @@ good_sign_auted_checked_brk_multi_bb:
351351
1:
352352
eor x16, x0, x0, lsl #1
353353
tbz x16, #62, 2f
354-
brk 0x1234
354+
brk 0xc472
355355
2:
356356
cbz x4, 3f
357357
nop
@@ -732,7 +732,7 @@ good_resign_with_increment_brk:
732732
add x0, x0, #8
733733
eor x16, x0, x0, lsl #1
734734
tbz x16, #62, 1f
735-
brk 0x1234
735+
brk 0xc472
736736
1:
737737
mov x2, x0
738738
pacda x2, x1

0 commit comments

Comments
 (0)