Skip to content

Commit 47de958

Browse files
committed
Address review comments, add a description for FirstInstLeakingReg
1 parent eb0adff commit 47de958

File tree

3 files changed

+78
-9
lines changed

3 files changed

+78
-9
lines changed

bolt/include/bolt/Passes/PAuthGadgetScanner.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,11 @@ class ExtraInfo {
274274
virtual ~ExtraInfo() {}
275275
};
276276

277+
/// The set of instructions writing to the affected register in an unsafe
278+
/// manner.
279+
///
280+
/// This is a hint to be printed alongside the report. It should be further
281+
/// analyzed by the user.
277282
class ClobberingInfo : public ExtraInfo {
278283
SmallVector<MCInstReference> ClobberingInstrs;
279284

@@ -283,6 +288,11 @@ class ClobberingInfo : public ExtraInfo {
283288
void print(raw_ostream &OS, const MCInstReference Location) const override;
284289
};
285290

291+
/// The set of instructions leaking the authenticated pointer before the
292+
/// result of authentication was checked.
293+
///
294+
/// This is a hint to be printed alongside the report. It should be further
295+
/// analyzed by the user.
286296
class LeakageInfo : public ExtraInfo {
287297
SmallVector<MCInstReference> LeakingInstrs;
288298

bolt/lib/Passes/PAuthGadgetScanner.cpp

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -746,13 +746,18 @@ SrcSafetyAnalysis::create(BinaryFunction &BF,
746746
/// Similar to SrcState, it is the responsibility of the analysis to take
747747
/// register aliasing into account.
748748
///
749-
/// Depending on the implementation, it may be possible that an authentication
749+
/// Depending on the implementation (such as whether FEAT_FPAC is implemented
750+
/// by an AArch64 CPU or not), it may be possible that an authentication
750751
/// instruction returns an invalid pointer on failure instead of terminating
751752
/// the program immediately (assuming the program will crash as soon as that
752-
/// pointer is dereferenced). To prevent brute-forcing the correct signature,
753-
/// it should be impossible for an attacker to test if a pointer is correctly
754-
/// signed - either the program should be terminated on authentication failure
755-
/// or it should be impossible to tell whether authentication succeeded or not.
753+
/// pointer is dereferenced). Since few bits are usually allocated for the PAC
754+
/// field (such as less than 16 bits on a typical AArch64 system), an attacker
755+
/// can try every possible signature and guess the correct one if there is a
756+
/// gadget that tells whether the particular pointer has a correct signature
757+
/// (a so called "authentication oracle"). For that reason, it should be
758+
/// impossible for an attacker to test if a pointer is correctly signed -
759+
/// either the program should be terminated on authentication failure or
760+
/// the result of authentication should not be accessible to an attacker.
756761
///
757762
/// For that reason, a restricted set of operations is allowed on any register
758763
/// containing a value derived from the result of an authentication instruction
@@ -778,6 +783,14 @@ struct DstState {
778783
/// instructions should only be written to such registers.
779784
BitVector CannotEscapeUnchecked;
780785

786+
/// A vector of sets, only used on the second analysis run.
787+
/// Each element in this vector represents one of the tracked registers.
788+
/// For each such register we track the set of first instructions that leak
789+
/// the authenticated pointer before it was checked. This is intended to
790+
/// provide clues on which instruction made the particular register unsafe.
791+
///
792+
/// Please note that the mapping from MCPhysReg values to indexes in this
793+
/// vector is provided by RegsToTrackInstsFor field of DstSafetyAnalysis.
781794
std::vector<SetOfRelatedInsts> FirstInstLeakingReg;
782795

783796
/// Constructs an empty state.
@@ -1463,7 +1476,7 @@ void FunctionAnalysisContext::run() {
14631476

14641477
SmallVector<PartialReport<MCPhysReg>> UnsafeDefs;
14651478
findUnsafeDefs(UnsafeDefs);
1466-
handleSimpleReports(UnsafeUses);
1479+
handleSimpleReports(UnsafeDefs);
14671480
if (!UnsafeDefs.empty())
14681481
augmentUnsafeDefReports(UnsafeDefs);
14691482
}

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

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,11 +137,56 @@ bad_unknown_usage_read:
137137
// CHECK-NEXT: {{[0-9a-f]+}}: ldr x2, [x0]
138138
// CHECK-NEXT: {{[0-9a-f]+}}: ret
139139
autia x0, x1
140+
// Registers are not accessible to an attacker under Pointer
141+
// Authentication threat model, until spilled to memory.
142+
// Thus, reporting the below MUL instruction is a false positive, since
143+
// the next LDR instruction prevents any possible spilling of x3 unless
144+
// the authentication succeeded. Though, rejecting anything except for
145+
// a closed list of instruction types is the intended behavior of the
146+
// analysis, so this false positive is by design.
140147
mul x3, x0, x1
141148
ldr x2, [x0]
142149
ret
143150
.size bad_unknown_usage_read, .-bad_unknown_usage_read
144151

152+
.globl bad_store_to_memory_and_wait
153+
.type bad_store_to_memory_and_wait,@function
154+
bad_store_to_memory_and_wait:
155+
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_store_to_memory_and_wait, basic block {{[^,]+}}, at address
156+
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
157+
// CHECK-NEXT: The 1 instructions that leak the affected registers are:
158+
// CHECK-NEXT: 1. {{[0-9a-f]+}}: str x0, [x3]
159+
autia x0, x1
160+
cbz x3, 2f
161+
str x0, [x3]
162+
1:
163+
// The thread performs a time-consuming computation while the result of
164+
// authentication is accessible in memory.
165+
nop
166+
2:
167+
ldr x2, [x0]
168+
ret
169+
.size bad_store_to_memory_and_wait, .-bad_store_to_memory_and_wait
170+
171+
// FIXME: Known false negative: if no return instruction is reachable from a
172+
// program point (this probably implies an infinite loop), such
173+
// instruction cannot be detected as an authentication oracle.
174+
.globl bad_store_to_memory_and_hang
175+
.type bad_store_to_memory_and_hang,@function
176+
bad_store_to_memory_and_hang:
177+
// CHECK-NOT: bad_store_to_memory_and_hang
178+
autia x0, x1
179+
cbz x3, 2f
180+
str x0, [x3]
181+
1:
182+
// The thread loops indefinitely while the result of authentication
183+
// is accessible in memory.
184+
b 1b
185+
2:
186+
ldr x2, [x0]
187+
ret
188+
.size bad_store_to_memory_and_hang, .-bad_store_to_memory_and_hang
189+
145190
.globl bad_unknown_usage_subreg_read
146191
.type bad_unknown_usage_subreg_read,@function
147192
bad_unknown_usage_subreg_read:
@@ -419,6 +464,10 @@ good_address_arith_multi_bb:
419464
ret
420465
.size good_address_arith_multi_bb, .-good_address_arith_multi_bb
421466

467+
// FIXME: Most *_nocfg test cases contain paciasp+autiasp instructions even if
468+
// LR is not spilled - this is a workaround for RET instructions being
469+
// reported as non-protected, because LR state is reset at every label.
470+
422471
.globl good_ret_nocfg
423472
.type good_ret_nocfg,@function
424473
good_ret_nocfg:
@@ -454,13 +503,10 @@ good_call_nocfg:
454503
.type good_branch_nocfg,@function
455504
good_branch_nocfg:
456505
// CHECK-NOT: good_branch_nocfg
457-
paciasp
458506
adr x2, 1f
459507
br x2
460508
1:
461509
autia x0, x1
462-
autiasp // authenticate LR before tail call
463-
ldr x2, [x30] // check LR before tail call
464510
br x0
465511
.size good_branch_nocfg, .-good_branch_nocfg
466512

0 commit comments

Comments
 (0)