Skip to content

Commit ba63c9f

Browse files
committed
Simplify a questionable rule in DstSafetyAnalysis::getRegsMadeProtected
1 parent 0cce736 commit ba63c9f

File tree

2 files changed

+79
-40
lines changed

2 files changed

+79
-40
lines changed

bolt/lib/Passes/PAuthGadgetScanner.cpp

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -759,14 +759,24 @@ SrcSafetyAnalysis::create(BinaryFunction &BF,
759759
/// either the program should be terminated on authentication failure or
760760
/// the result of authentication should not be accessible to an attacker.
761761
///
762-
/// For that reason, a restricted set of operations is allowed on any register
763-
/// containing a value derived from the result of an authentication instruction
764-
/// until that register is either wiped or checked not to contain a result of a
765-
/// failed authentication.
762+
/// Considering the instructions in forward order as they are executed, a
763+
/// restricted set of operations can be allowed on any register containing a
764+
/// value derived from the result of an authentication instruction until that
765+
/// value is checked not to contain the result of a failed authentication.
766+
/// In DstSafetyAnalysis, these rules are adapted, so that the safety property
767+
/// for a register is computed by iterating the instructions in backward order.
768+
/// Then the resulting properties are used at authentication instruction sites
769+
/// to check output registers and report the particular instruction if it writes
770+
/// to an unsafe register.
766771
///
767-
/// Specifically, the safety property for a register is computed by iterating
768-
/// the instructions in backward order: the source register Xn of an instruction
769-
/// Inst is safe if at least one of the following is true:
772+
/// Another approach would be to simulate the above rules as-is, iterating over
773+
/// the instructions in forward direction. To make it possible to report the
774+
/// particular instructions as oracles, this would probably require tracking
775+
/// references to these instructions for each register currently containing
776+
/// sensitive data.
777+
///
778+
/// In DstSafetyAnalysis, the source register Xn of an instruction Inst is safe
779+
/// if at least one of the following is true:
770780
/// * Inst checks if Xn contains the result of a successful authentication and
771781
/// terminates the program on failure. Note that Inst can either naturally
772782
/// dereference Xn (load, branch, return, etc. instructions) or be the first
@@ -776,7 +786,7 @@ SrcSafetyAnalysis::create(BinaryFunction &BF,
776786
/// execution of Inst (temporaries are not used on AArch64 and thus not
777787
/// currently supported/allowed).
778788
/// See MCPlusBuilder::analyzeAddressArithmeticsForPtrAuth for the details.
779-
/// * Inst fully overwrites Xn with an unrelated value.
789+
/// * Inst fully overwrites Xn with a constant.
780790
struct DstState {
781791
/// The set of registers whose values cannot be inspected by an attacker in
782792
/// a way usable as an authentication oracle. The results of authentication
@@ -913,8 +923,9 @@ class DstSafetyAnalysis {
913923
}
914924

915925
/// Returns the set of registers that can be leaked by this instruction.
916-
/// This is computed similar to the set of clobbered registers, but taking
917-
/// input operands instead of outputs.
926+
/// A register is considered leaked if it has any intersection with any
927+
/// register read by Inst. This is similar to how the set of clobbered
928+
/// registers is computed, but taking input operands instead of outputs.
918929
BitVector getLeakedRegs(const MCInst &Inst) const {
919930
BitVector Leaked(NumRegs);
920931

@@ -982,17 +993,18 @@ class DstSafetyAnalysis {
982993
Regs.push_back(SrcReg);
983994
}
984995

985-
// ... the register can be overwritten in whole with an unrelated value -
986-
// for that reason, ignore the registers that are both read and written:
987-
//
988-
// movk x0, #42, lsl #16 // keeps some bits of x0
989-
// mul x1, x1, #3 // not all information is actually lost
990-
//
991-
BitVector FullyOverwrittenRegs;
992-
BC.MIB->getWrittenRegs(Inst, FullyOverwrittenRegs);
993-
FullyOverwrittenRegs.reset(LeakedRegs);
994-
for (MCPhysReg Reg : FullyOverwrittenRegs.set_bits())
995-
Regs.push_back(Reg);
996+
// ... the register can be overwritten in whole with a constant: for that
997+
// purpose, look for the instructions with no register inputs (neither
998+
// explicit nor implicit ones) and no side effects (to rule out reading
999+
// not modelled locations).
1000+
const MCInstrDesc &Desc = BC.MII->get(Inst.getOpcode());
1001+
bool HasExplicitSrcRegs = llvm::any_of(BC.MIB->useOperands(Inst),
1002+
[](auto Op) { return Op.isReg(); });
1003+
if (!Desc.hasUnmodeledSideEffects() && !HasExplicitSrcRegs &&
1004+
Desc.implicit_uses().empty()) {
1005+
for (const MCOperand &Def : BC.MIB->defOperands(Inst))
1006+
Regs.push_back(Def.getReg());
1007+
}
9961008

9971009
return Regs;
9981010
}

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

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,37 @@ bad_unknown_usage_update:
223223
ret
224224
.size bad_unknown_usage_update, .-bad_unknown_usage_update
225225

226+
.globl good_overwrite_with_constant
227+
.type good_overwrite_with_constant,@function
228+
good_overwrite_with_constant:
229+
// CHECK-NOT: good_overwrite_with_constant
230+
autia x0, x1
231+
mov x0, #42
232+
ret
233+
.size good_overwrite_with_constant, .-good_overwrite_with_constant
234+
235+
// Overwriting sensitive data by instructions with unmodelled side-effects is
236+
// explicitly rejected, even though this particular MRS is safe.
237+
.globl bad_overwrite_with_side_effects
238+
.type bad_overwrite_with_side_effects,@function
239+
bad_overwrite_with_side_effects:
240+
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function bad_overwrite_with_side_effects, basic block {{[^,]+}}, at address
241+
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
242+
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
243+
autia x0, x1
244+
mrs x0, CTR_EL0
245+
ret
246+
.size bad_overwrite_with_side_effects, .-bad_overwrite_with_side_effects
247+
248+
// Here the new value written by MUL to x0 is completely unrelated to the result
249+
// of authentication, so this is a false positive.
250+
// FIXME: Can/should we generalize overwriting by constant to handle such cases?
226251
.globl good_unknown_overwrite
227252
.type good_unknown_overwrite,@function
228253
good_unknown_overwrite:
229-
// CHECK-NOT: good_unknown_overwrite
254+
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function good_unknown_overwrite, basic block {{[^,]+}}, at address
255+
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
256+
// CHECK-NEXT: The 0 instructions that leak the affected registers are:
230257
autia x0, x1
231258
mul x0, x1, x2
232259
ret
@@ -235,15 +262,15 @@ good_unknown_overwrite:
235262
// This is a false positive: when a general-purpose register is written to as
236263
// a 32-bit register, its top 32 bits are zeroed, but according to LLVM
237264
// representation, the instruction only overwrites the Wn register.
238-
.globl good_unknown_wreg_overwrite
239-
.type good_unknown_wreg_overwrite,@function
240-
good_unknown_wreg_overwrite:
241-
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function good_unknown_wreg_overwrite, basic block {{[^,]+}}, at address
265+
.globl good_wreg_overwrite
266+
.type good_wreg_overwrite,@function
267+
good_wreg_overwrite:
268+
// CHECK-LABEL: GS-PAUTH: authentication oracle found in function good_wreg_overwrite, basic block {{[^,]+}}, at address
242269
// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: autia x0, x1
243270
autia x0, x1
244-
mul w0, w1, w2
271+
mov w0, #42
245272
ret
246-
.size good_unknown_wreg_overwrite, .-good_unknown_wreg_overwrite
273+
.size good_wreg_overwrite, .-good_wreg_overwrite
247274

248275
.globl good_address_arith
249276
.type good_address_arith,@function
@@ -435,16 +462,16 @@ bad_unknown_usage_update_multi_bb:
435462
ret
436463
.size bad_unknown_usage_update_multi_bb, .-bad_unknown_usage_update_multi_bb
437464

438-
.globl good_unknown_overwrite_multi_bb
439-
.type good_unknown_overwrite_multi_bb,@function
440-
good_unknown_overwrite_multi_bb:
441-
// CHECK-NOT: good_unknown_overwrite_multi_bb
465+
.globl good_overwrite_with_constant_multi_bb
466+
.type good_overwrite_with_constant_multi_bb,@function
467+
good_overwrite_with_constant_multi_bb:
468+
// CHECK-NOT: good_overwrite_with_constant_multi_bb
442469
autia x0, x1
443470
cbz x3, 1f
444471
1:
445-
mul x0, x1, x2
472+
mov x0, #42
446473
ret
447-
.size good_unknown_overwrite_multi_bb, .-good_unknown_overwrite_multi_bb
474+
.size good_overwrite_with_constant_multi_bb, .-good_overwrite_with_constant_multi_bb
448475

449476
.globl good_address_arith_multi_bb
450477
.type good_address_arith_multi_bb,@function
@@ -638,20 +665,20 @@ bad_unknown_usage_update_nocfg:
638665
ret
639666
.size bad_unknown_usage_update_nocfg, .-bad_unknown_usage_update_nocfg
640667

641-
.globl good_unknown_overwrite_nocfg
642-
.type good_unknown_overwrite_nocfg,@function
643-
good_unknown_overwrite_nocfg:
644-
// CHECK-NOT: good_unknown_overwrite_nocfg
668+
.globl good_overwrite_with_constant_nocfg
669+
.type good_overwrite_with_constant_nocfg,@function
670+
good_overwrite_with_constant_nocfg:
671+
// CHECK-NOT: good_overwrite_with_constant_nocfg
645672
paciasp
646673
adr x2, 1f
647674
br x2
648675
1:
649676
autia x0, x1
650-
mul x0, x1, x2
677+
mov x0, #42
651678

652679
autiasp
653680
ret
654-
.size good_unknown_overwrite_nocfg, .-good_unknown_overwrite_nocfg
681+
.size good_overwrite_with_constant_nocfg, .-good_overwrite_with_constant_nocfg
655682

656683
.globl good_address_arith_nocfg
657684
.type good_address_arith_nocfg,@function

0 commit comments

Comments
 (0)