https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/138884
>From b5a5ea9b2fc7a85760064994bea7153bb91b746b Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko <[email protected]> Date: Wed, 7 May 2025 16:42:00 +0300 Subject: [PATCH 1/3] [BOLT] Introduce helpers to match `MCInst`s one at a time (NFC) Introduce matchInst helper function to capture and/or match the operands of MCInst. Unlike the existing `MCPlusBuilder::MCInstMatcher` machinery, matchInst is intended for the use cases when precise control over the instruction order is required. For example, when validating PtrAuth hardening, all registers are usually considered unsafe after a function call, even though callee-saved registers should preserve their old values *under normal operation*. --- bolt/include/bolt/Core/MCInstUtils.h | 128 ++++++++++++++++++ .../Target/AArch64/AArch64MCPlusBuilder.cpp | 90 +++++------- 2 files changed, 162 insertions(+), 56 deletions(-) diff --git a/bolt/include/bolt/Core/MCInstUtils.h b/bolt/include/bolt/Core/MCInstUtils.h index 2e93dfaf4c275..40731a4fed701 100644 --- a/bolt/include/bolt/Core/MCInstUtils.h +++ b/bolt/include/bolt/Core/MCInstUtils.h @@ -149,6 +149,134 @@ static inline raw_ostream &operator<<(raw_ostream &OS, return Ref.print(OS); } +/// Instruction-matching helpers operating on a single instruction at a time. +/// +/// Unlike MCPlusBuilder::MCInstMatcher, this matchInst() function focuses on +/// the cases where a precise control over the instruction order is important: +/// +/// // Bring the short names into the local scope: +/// using namespace MCInstMatcher; +/// // Declare the registers to capture: +/// Reg Xn, Xm; +/// // Capture the 0th and 1st operands, match the 2nd operand against the +/// // just captured Xm register, match the 3rd operand against literal 0: +/// if (!matchInst(MaybeAdd, AArch64::ADDXrs, Xm, Xn, Xm, Imm(0)) +/// return AArch64::NoRegister; +/// // Match the 0th operand against Xm: +/// if (!matchInst(MaybeBr, AArch64::BR, Xm)) +/// return AArch64::NoRegister; +/// // Return the matched register: +/// return Xm.get(); +namespace MCInstMatcher { + +// The base class to match an operand of type T. +// +// The subclasses of OpMatcher are intended to be allocated on the stack and +// to only be used by passing them to matchInst() and by calling their get() +// function, thus the peculiar `mutable` specifiers: to make the calling code +// compact and readable, the templated matchInst() function has to accept both +// long-lived Imm/Reg wrappers declared as local variables (intended to capture +// the first operand's value and match the subsequent operands, whether inside +// a single instruction or across multiple instructions), as well as temporary +// wrappers around literal values to match, f.e. Imm(42) or Reg(AArch64::XZR). +template <typename T> class OpMatcher { + mutable std::optional<T> Value; + mutable std::optional<T> SavedValue; + + // Remember/restore the last Value - to be called by matchInst. + void remember() const { SavedValue = Value; } + void restore() const { Value = SavedValue; } + + template <class... OpMatchers> + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); + +protected: + OpMatcher(std::optional<T> ValueToMatch) : Value(ValueToMatch) {} + + bool matchValue(T OpValue) const { + // Check that OpValue does not contradict the existing Value. + bool MatchResult = !Value || *Value == OpValue; + // If MatchResult is false, all matchers will be reset before returning from + // matchInst, including this one, thus no need to assign conditionally. + Value = OpValue; + + return MatchResult; + } + +public: + /// Returns the captured value. + T get() const { + assert(Value.has_value()); + return *Value; + } +}; + +class Reg : public OpMatcher<MCPhysReg> { + bool matches(const MCOperand &Op) const { + if (!Op.isReg()) + return false; + + return matchValue(Op.getReg()); + } + + template <class... OpMatchers> + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); + +public: + Reg(std::optional<MCPhysReg> RegToMatch = std::nullopt) + : OpMatcher<MCPhysReg>(RegToMatch) {} +}; + +class Imm : public OpMatcher<int64_t> { + bool matches(const MCOperand &Op) const { + if (!Op.isImm()) + return false; + + return matchValue(Op.getImm()); + } + + template <class... OpMatchers> + friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...); + +public: + Imm(std::optional<int64_t> ImmToMatch = std::nullopt) + : OpMatcher<int64_t>(ImmToMatch) {} +}; + +/// Tries to match Inst and updates Ops on success. +/// +/// If Inst has the specified Opcode and its operand list prefix matches Ops, +/// this function returns true and updates Ops, otherwise false is returned and +/// values of Ops are kept as before matchInst was called. +/// +/// Please note that while Ops are technically passed by a const reference to +/// make invocations like `matchInst(MI, Opcode, Imm(42))` possible, all their +/// fields are marked mutable. +template <class... OpMatchers> +bool matchInst(const MCInst &Inst, unsigned Opcode, const OpMatchers &...Ops) { + if (Inst.getOpcode() != Opcode) + return false; + assert(sizeof...(Ops) <= Inst.getNumOperands() && + "Too many operands are matched for the Opcode"); + + // Ask each matcher to remember its current value in case of rollback. + (Ops.remember(), ...); + + // Check if all matchers match the corresponding operands. + auto It = Inst.begin(); + auto AllMatched = (Ops.matches(*(It++)) && ... && true); + + // If match failed, restore the original captured values. + if (!AllMatched) { + (Ops.restore(), ...); + return false; + } + + return true; +} + +} // namespace MCInstMatcher + } // namespace bolt } // namespace llvm diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp index 72f95cea6fa1d..c1052ad5c0ba9 100644 --- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp +++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp @@ -19,6 +19,7 @@ #include "Utils/AArch64BaseInfo.h" #include "bolt/Core/BinaryBasicBlock.h" #include "bolt/Core/BinaryFunction.h" +#include "bolt/Core/MCInstUtils.h" #include "bolt/Core/MCPlusBuilder.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/MC/MCContext.h" @@ -389,81 +390,58 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { // Iterate over the instructions of BB in reverse order, matching opcodes // and operands. - MCPhysReg TestedReg = 0; - MCPhysReg ScratchReg = 0; + auto It = BB.end(); - auto StepAndGetOpcode = [&It, &BB]() -> int { - if (It == BB.begin()) - return -1; - --It; - return It->getOpcode(); + auto StepBack = [&]() { + while (It != BB.begin()) { + --It; + if (!isCFI(*It)) + return true; + } + return false; }; - - switch (StepAndGetOpcode()) { - default: - // Not matched the branch instruction. + // Step to the last non-CFI instruction. + if (!StepBack()) return std::nullopt; - case AArch64::Bcc: - // Bcc EQ, .Lon_success - if (It->getOperand(0).getImm() != AArch64CC::EQ) - return std::nullopt; - // Not checking .Lon_success (see above). - // SUBSXrs XZR, TestedReg, ScratchReg, 0 (used by "CMP reg, reg" alias) - if (StepAndGetOpcode() != AArch64::SUBSXrs || - It->getOperand(0).getReg() != AArch64::XZR || - It->getOperand(3).getImm() != 0) + using namespace llvm::bolt::MCInstMatcher; + Reg TestedReg; + Reg ScratchReg; + + if (matchInst(*It, AArch64::Bcc, Imm(AArch64CC::EQ) /*, .Lon_success*/)) { + if (!StepBack() || !matchInst(*It, AArch64::SUBSXrs, Reg(AArch64::XZR), + TestedReg, ScratchReg, Imm(0))) return std::nullopt; - TestedReg = It->getOperand(1).getReg(); - ScratchReg = It->getOperand(2).getReg(); // Either XPAC(I|D) ScratchReg, ScratchReg // or XPACLRI - switch (StepAndGetOpcode()) { - default: + if (!StepBack()) return std::nullopt; - case AArch64::XPACLRI: + if (matchInst(*It, AArch64::XPACLRI)) { // No operands to check, but using XPACLRI forces TestedReg to be X30. - if (TestedReg != AArch64::LR) - return std::nullopt; - break; - case AArch64::XPACI: - case AArch64::XPACD: - if (It->getOperand(0).getReg() != ScratchReg || - It->getOperand(1).getReg() != ScratchReg) + if (TestedReg.get() != AArch64::LR) return std::nullopt; - break; + } else if (!matchInst(*It, AArch64::XPACI, ScratchReg, ScratchReg) && + !matchInst(*It, AArch64::XPACD, ScratchReg, ScratchReg)) { + return std::nullopt; } - // ORRXrs ScratchReg, XZR, TestedReg, 0 (used by "MOV reg, reg" alias) - if (StepAndGetOpcode() != AArch64::ORRXrs) + if (!StepBack() || !matchInst(*It, AArch64::ORRXrs, ScratchReg, + Reg(AArch64::XZR), TestedReg, Imm(0))) return std::nullopt; - if (It->getOperand(0).getReg() != ScratchReg || - It->getOperand(1).getReg() != AArch64::XZR || - It->getOperand(2).getReg() != TestedReg || - It->getOperand(3).getImm() != 0) - return std::nullopt; - - return std::make_pair(TestedReg, &*It); - case AArch64::TBZX: - // TBZX ScratchReg, 62, .Lon_success - ScratchReg = It->getOperand(0).getReg(); - if (It->getOperand(1).getImm() != 62) - return std::nullopt; - // Not checking .Lon_success (see above). + return std::make_pair(TestedReg.get(), &*It); + } - // EORXrs ScratchReg, TestedReg, TestedReg, 1 - if (StepAndGetOpcode() != AArch64::EORXrs) - return std::nullopt; - TestedReg = It->getOperand(1).getReg(); - if (It->getOperand(0).getReg() != ScratchReg || - It->getOperand(2).getReg() != TestedReg || - It->getOperand(3).getImm() != 1) + if (matchInst(*It, AArch64::TBZX, ScratchReg, Imm(62) /*, .Lon_success*/)) { + if (!StepBack() || !matchInst(*It, AArch64::EORXrs, Reg(ScratchReg), + TestedReg, TestedReg, Imm(1))) return std::nullopt; - return std::make_pair(TestedReg, &*It); + return std::make_pair(TestedReg.get(), &*It); } + + return std::nullopt; } std::optional<MCPhysReg> getAuthCheckedReg(const MCInst &Inst, >From 750f4224326073253eecd81d16a1e410b2f489ef Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko <[email protected]> Date: Tue, 6 May 2025 11:31:03 +0300 Subject: [PATCH 2/3] [BOLT] Gadget scanner: prevent false positives due to jump tables As part of PAuth hardening, AArch64 LLVM backend can use a special BR_JumpTable pseudo (enabled by -faarch64-jump-table-hardening Clang option) which is expanded in the AsmPrinter into a contiguous sequence without unsafe instructions in the middle. This commit adds another target-specific callback to MCPlusBuilder to make it possible to inhibit false positives for known-safe jump table dispatch sequences. Without special handling, the branch instruction is likely to be reported as a non-protected call (as its destination is not produced by an auth instruction, PC-relative address materialization, etc.) and possibly as a tail call being performed with unsafe link register (as the detection whether the branch instruction is a tail call is an heuristic). For now, only the specific instruction sequence used by the AArch64 LLVM backend is matched. --- bolt/include/bolt/Core/MCInstUtils.h | 9 + bolt/include/bolt/Core/MCPlusBuilder.h | 14 + bolt/lib/Core/MCInstUtils.cpp | 20 + bolt/lib/Passes/PAuthGadgetScanner.cpp | 10 + .../Target/AArch64/AArch64MCPlusBuilder.cpp | 73 ++ .../AArch64/gs-pauth-jump-table.s | 703 ++++++++++++++++++ 6 files changed, 829 insertions(+) create mode 100644 bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s diff --git a/bolt/include/bolt/Core/MCInstUtils.h b/bolt/include/bolt/Core/MCInstUtils.h index 40731a4fed701..f6ce5591ae57d 100644 --- a/bolt/include/bolt/Core/MCInstUtils.h +++ b/bolt/include/bolt/Core/MCInstUtils.h @@ -141,6 +141,15 @@ class MCInstReference { return nullptr; } + /// Returns the only preceding instruction, or std::nullopt if multiple or no + /// predecessors are possible. + /// + /// If CFG information is available, basic block boundary can be crossed, + /// provided there is exactly one predecessor. If CFG is not available, the + /// preceding instruction in the offset order is returned, unless this is the + /// first instruction of the function. + std::optional<MCInstReference> getSinglePredecessor(); + raw_ostream &print(raw_ostream &OS) const; }; diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h index ae04891e791f9..f13e41c75c2ac 100644 --- a/bolt/include/bolt/Core/MCPlusBuilder.h +++ b/bolt/include/bolt/Core/MCPlusBuilder.h @@ -14,6 +14,7 @@ #ifndef BOLT_CORE_MCPLUSBUILDER_H #define BOLT_CORE_MCPLUSBUILDER_H +#include "bolt/Core/MCInstUtils.h" #include "bolt/Core/MCPlus.h" #include "bolt/Core/Relocation.h" #include "llvm/ADT/ArrayRef.h" @@ -711,6 +712,19 @@ class MCPlusBuilder { return std::nullopt; } + /// Tests if BranchInst corresponds to an instruction sequence which is known + /// to be a safe dispatch via jump table. + /// + /// The target can decide which instruction sequences to consider "safe" from + /// the Pointer Authentication point of view, such as any jump table dispatch + /// sequence without function calls inside, any sequence which is contiguous, + /// or only some specific well-known sequences. + virtual bool + isSafeJumpTableBranchForPtrAuth(MCInstReference BranchInst) const { + llvm_unreachable("not implemented"); + return false; + } + virtual bool isTerminator(const MCInst &Inst) const; virtual bool isNoop(const MCInst &Inst) const { diff --git a/bolt/lib/Core/MCInstUtils.cpp b/bolt/lib/Core/MCInstUtils.cpp index 3cdb9673d4dc0..39bc96f087f8e 100644 --- a/bolt/lib/Core/MCInstUtils.cpp +++ b/bolt/lib/Core/MCInstUtils.cpp @@ -54,3 +54,23 @@ raw_ostream &MCInstReference::print(raw_ostream &OS) const { OS << ">"; return OS; } + +std::optional<MCInstReference> MCInstReference::getSinglePredecessor() { + if (const RefInBB *Ref = tryGetRefInBB()) { + if (Ref->It != Ref->BB->begin()) + return MCInstReference(Ref->BB, &*std::prev(Ref->It)); + + if (Ref->BB->pred_size() != 1) + return std::nullopt; + + BinaryBasicBlock *PredBB = *Ref->BB->pred_begin(); + assert(!PredBB->empty() && "Empty basic blocks are not supported yet"); + return MCInstReference(PredBB, &*PredBB->rbegin()); + } + + const RefInBF &Ref = getRefInBF(); + if (Ref.It == Ref.BF->instrs().begin()) + return std::nullopt; + + return MCInstReference(Ref.BF, std::prev(Ref.It)); +} diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp index 5d884e2d37354..3a6c3e3d925cf 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -1370,6 +1370,11 @@ shouldReportUnsafeTailCall(const BinaryContext &BC, const BinaryFunction &BF, return std::nullopt; } + if (BC.MIB->isSafeJumpTableBranchForPtrAuth(Inst)) { + LLVM_DEBUG({ dbgs() << " Safe jump table detected, skipping.\n"; }); + return std::nullopt; + } + // Returns at most one report per instruction - this is probably OK... for (auto Reg : RegsToCheck) if (!S.TrustedRegs[Reg]) @@ -1400,6 +1405,11 @@ shouldReportCallGadget(const BinaryContext &BC, const MCInstReference &Inst, if (S.SafeToDerefRegs[DestReg]) return std::nullopt; + if (BC.MIB->isSafeJumpTableBranchForPtrAuth(Inst)) { + LLVM_DEBUG({ dbgs() << " Safe jump table detected, skipping.\n"; }); + return std::nullopt; + } + return make_gadget_report(CallKind, Inst, DestReg); } diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp index c1052ad5c0ba9..751768df793ae 100644 --- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp +++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp @@ -532,6 +532,79 @@ class AArch64MCPlusBuilder : public MCPlusBuilder { return std::nullopt; } + bool + isSafeJumpTableBranchForPtrAuth(MCInstReference BranchInst) const override { + MCInstReference CurRef = BranchInst; + auto StepBack = [&]() { + do { + auto PredInst = CurRef.getSinglePredecessor(); + if (!PredInst) + return false; + CurRef = *PredInst; + } while (isCFI(CurRef)); + + return true; + }; + + // Match this contiguous sequence: + // cmp Xm, #count + // csel Xm, Xm, xzr, ls + // adrp Xn, .LJTIxyz + // add Xn, Xn, :lo12:.LJTIxyz + // ldrsw Xm, [Xn, Xm, lsl #2] + // .Ltmp: + // adr Xn, .Ltmp + // add Xm, Xn, Xm + // br Xm + + // FIXME: Check label operands of ADR/ADRP+ADD and #count operand of CMP. + + using namespace MCInstMatcher; + Reg Xm, Xn; + + if (!matchInst(CurRef, AArch64::BR, Xm) || !StepBack()) + return false; + + if (!matchInst(CurRef, AArch64::ADDXrs, Xm, Xn, Xm, Imm(0)) || !StepBack()) + return false; + + if (!matchInst(CurRef, AArch64::ADR, Xn /*, .Ltmp*/) || !StepBack()) + return false; + + if (!matchInst(CurRef, AArch64::LDRSWroX, Xm, Xn, Xm, Imm(0), Imm(1)) || + !StepBack()) + return false; + + if (matchInst(CurRef, AArch64::ADR, Xn /*, .LJTIxyz*/)) { + if (!StepBack()) + return false; + if (!matchInst(CurRef, AArch64::HINT, Imm(0)) || !StepBack()) + return false; + } else if (matchInst(CurRef, AArch64::ADDXri, Xn, + Xn /*, :lo12:.LJTIxyz*/)) { + if (!StepBack()) + return false; + if (!matchInst(CurRef, AArch64::ADRP, Xn /*, .LJTIxyz*/) || !StepBack()) + return false; + } else { + return false; + } + + if (!matchInst(CurRef, AArch64::CSELXr, Xm, Xm, Reg(AArch64::XZR), + Imm(AArch64CC::LS)) || + !StepBack()) + return false; + + if (!matchInst(CurRef, AArch64::SUBSXri, Reg(AArch64::XZR), + Xm /*, #count*/)) + return false; + + // Some platforms treat X16 and X17 as more protected registers, others + // do not make such distinction. So far, accept any registers as Xm and Xn. + + return true; + } + bool isADRP(const MCInst &Inst) const override { return Inst.getOpcode() == AArch64::ADRP; } diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s b/bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s new file mode 100644 index 0000000000000..5a42ed078e9c2 --- /dev/null +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s @@ -0,0 +1,703 @@ +// -Wl,--no-relax prevents converting ADRP+ADD pairs into NOP+ADR. +// Without -Wl,--emit-relocs BOLT refuses to create CFG information for the below functions. + +// RUN: %clang %cflags -march=armv8.3-a -Wl,--no-relax -Wl,--emit-relocs %s -o %t.exe +// RUN: llvm-bolt-binary-analysis --scanners=pauth %t.exe 2>&1 | FileCheck --check-prefixes=CHECK,CFG %s +// RUN: %clang %cflags -march=armv8.3-a -Wl,--no-relax %s -o %t.exe +// RUN: llvm-bolt-binary-analysis --scanners=pauth %t.exe 2>&1 | FileCheck --check-prefixes=CHECK,NOCFG %s + +// FIXME: Labels could be further validated. Specifically, it could be checked +// that the jump table itself is located in a read-only data section. + +// FIXME: BOLT does not reconstruct CFG correctly for jump tables yet, thus +// register state is pessimistically reset to unsafe at the beginning of +// each basic block without any predecessors. +// Until CFG reconstruction is fixed, add paciasp+autiasp instructions to +// silence "non-protected ret" false-positives and explicitly ignore +// "Warning: the function has unreachable basic blocks..." lines. + + .text + .p2align 2 + .globl good_jump_table + .type good_jump_table,@function +good_jump_table: +// CHECK-NOT: good_jump_table +// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function good_jump_table +// CHECK-NOT: good_jump_table + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + adrp x17, 4f + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size good_jump_table, .-good_jump_table + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + +// NOP (HINT #0) before ADR is correct (it can be produced by linker due to +// relaxing ADRP+ADD sequence), but other HINT instructions are not. + + .text + .p2align 2 + .globl jump_table_relaxed_adrp_add + .type jump_table_relaxed_adrp_add,@function +jump_table_relaxed_adrp_add: +// CHECK-NOT: jump_table_relaxed_adrp_add +// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function jump_table_relaxed_adrp_add +// CHECK-NOT: jump_table_relaxed_adrp_add + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + hint #0 // nop + adr x17, 4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_relaxed_adrp_add, .-jump_table_relaxed_adrp_add + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_wrong_hint + .type jump_table_wrong_hint,@function +jump_table_wrong_hint: +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_hint, basic block {{[^,]+}}, at address +// NOCFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_hint, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_hint@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + hint #20 // unknown hint + adr x17, 4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_wrong_hint, .-jump_table_wrong_hint + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + +// For now, all registers are permitted as temporary ones, not only x16 and x17. + + .text + .p2align 2 + .globl jump_table_unsafe_reg_1 + .type jump_table_unsafe_reg_1,@function +jump_table_unsafe_reg_1: +// CHECK-NOT: jump_table_unsafe_reg_1 +// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function jump_table_unsafe_reg_1 +// CHECK-NOT: jump_table_unsafe_reg_1 + paciasp + cmp x1, #0x2 + csel x1, x1, xzr, ls + adrp x17, 4f + add x17, x17, :lo12:4f + ldrsw x1, [x17, x1, lsl #2] +1: + adr x17, 1b + add x1, x17, x1 + br x1 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_unsafe_reg_1, .-jump_table_unsafe_reg_1 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_unsafe_reg_2 + .type jump_table_unsafe_reg_2,@function +jump_table_unsafe_reg_2: +// CHECK-NOT: jump_table_unsafe_reg_2 +// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function jump_table_unsafe_reg_2 +// CHECK-NOT: jump_table_unsafe_reg_2 + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + adrp x1, 4f + add x1, x1, :lo12:4f + ldrsw x16, [x1, x16, lsl #2] +1: + adr x1, 1b + add x16, x1, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_unsafe_reg_2, .-jump_table_unsafe_reg_2 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + +// FIXME: Detect possibility of jump table overflow. + .text + .p2align 2 + .globl jump_table_wrong_limit + .type jump_table_wrong_limit,@function +jump_table_wrong_limit: +// CHECK-NOT: jump_table_wrong_limit +// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function jump_table_wrong_limit +// CHECK-NOT: jump_table_wrong_limit + paciasp + cmp x16, #0x1000 + csel x16, x16, xzr, ls + adrp x17, 4f + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_wrong_limit, .-jump_table_wrong_limit + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_unrelated_inst_1 + .type jump_table_unrelated_inst_1,@function +jump_table_unrelated_inst_1: +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_unrelated_inst_1, basic block {{[^,]+}}, at address +// NOCFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_unrelated_inst_1, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_unrelated_inst_1@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: nop +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + adrp x17, 4f + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + nop + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_unrelated_inst_1, .-jump_table_unrelated_inst_1 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_unrelated_inst_2 + .type jump_table_unrelated_inst_2,@function +jump_table_unrelated_inst_2: +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_unrelated_inst_2, basic block {{[^,]+}}, at address +// NOCFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_unrelated_inst_2, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_unrelated_inst_2@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + adrp x17, 4f + add x17, x17, :lo12:4f + nop + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_unrelated_inst_2, .-jump_table_unrelated_inst_2 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_multiple_predecessors_1 + .type jump_table_multiple_predecessors_1,@function +jump_table_multiple_predecessors_1: +// NOCFG-NOT: jump_table_multiple_predecessors_1 +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_multiple_predecessors_1, basic block {{[^,]+}}, at address +// CFG-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CFG-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CFG-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_multiple_predecessors_1@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + paciasp + cbz x1, 1f // this instruction can jump to the middle of the sequence + cmp x16, #0x2 + csel x16, x16, xzr, ls + adrp x17, 4f + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b // multiple predecessors are possible + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_multiple_predecessors_1, .-jump_table_multiple_predecessors_1 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_multiple_predecessors_2 + .type jump_table_multiple_predecessors_2,@function +jump_table_multiple_predecessors_2: +// NOCFG-NOT: jump_table_multiple_predecessors_2 +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_multiple_predecessors_2, basic block {{[^,]+}}, at address +// CFG-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CFG-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CFG-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_multiple_predecessors_2@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + paciasp + cbz x1, 5f // this instruction can jump to the middle of the sequence + cmp x16, #0x2 + csel x16, x16, xzr, ls +5: + adrp x17, 4f // multiple predecessors are possible + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_multiple_predecessors_2, .-jump_table_multiple_predecessors_2 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + +// Test a few pattern violations... + + .text + .p2align 2 + .globl jump_table_wrong_reg_1 + .type jump_table_wrong_reg_1,@function +jump_table_wrong_reg_1: +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_1, basic block {{[^,]+}}, at address +// NOCFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_1, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x1 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: The 0 instructions that write to the affected registers after any authentication are: + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + adrp x17, 4f + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x1 // wrong reg +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_wrong_reg_1, .-jump_table_wrong_reg_1 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_wrong_reg_2 + .type jump_table_wrong_reg_2,@function +jump_table_wrong_reg_2: +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_2, basic block {{[^,]+}}, at address +// NOCFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_2, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x1 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_reg_2@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x1 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + adrp x17, 4f + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x1 // wrong reg + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_wrong_reg_2, .-jump_table_wrong_reg_2 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_wrong_reg_3 + .type jump_table_wrong_reg_3,@function +jump_table_wrong_reg_3: +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_3, basic block {{[^,]+}}, at address +// NOCFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_3, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_reg_3@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + adrp x17, 4f + add x17, x1, :lo12:4f // wrong reg + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_wrong_reg_3, .-jump_table_wrong_reg_3 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_wrong_reg_4 + .type jump_table_wrong_reg_4,@function +jump_table_wrong_reg_4: +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_4, basic block {{[^,]+}}, at address +// NOCFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_4, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_reg_4@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + paciasp + cmp x16, #0x2 + csel x16, x16, x1, ls // wrong reg + adrp x17, 4f + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_wrong_reg_4, .-jump_table_wrong_reg_4 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_wrong_imm_1 + .type jump_table_wrong_imm_1,@function +jump_table_wrong_imm_1: +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_imm_1, basic block {{[^,]+}}, at address +// NOCFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_imm_1, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_imm_1@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + adrp x17, 4f + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, sxtx #2] // wrong: sxtx instead of lsl +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_wrong_imm_1, .-jump_table_wrong_imm_1 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_wrong_imm_2 + .type jump_table_wrong_imm_2,@function +jump_table_wrong_imm_2: +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_imm_2, basic block {{[^,]+}}, at address +// NOCFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_imm_2, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_imm_2@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, lt // wrong: lt instead of ls + adrp x17, 4f + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_wrong_imm_2, .-jump_table_wrong_imm_2 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl jump_table_wrong_imm_3 + .type jump_table_wrong_imm_3,@function +jump_table_wrong_imm_3: +// CFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_imm_3, basic block {{[^,]+}}, at address +// NOCFG-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_imm_3, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_imm_3@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + adrp x17, 4f + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16, lsl #2 // wrong: lsl #2 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size jump_table_wrong_imm_3, .-jump_table_wrong_imm_3 + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + +// CFI instructions should be skipped and should not prevent matching +// the instruction sequence. + + .text + .p2align 2 + .globl skip_cfi_instructions + .type skip_cfi_instructions,@function +skip_cfi_instructions: + .cfi_startproc +// CHECK-NOT: skip_cfi_instructions +// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function skip_cfi_instructions +// CHECK-NOT: skip_cfi_instructions + paciasp + cmp x16, #0x2 + csel x16, x16, xzr, ls + adrp x17, 4f + .cfi_def_cfa_offset 16 // should be skipped over when matching the sequence + add x17, x17, :lo12:4f + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size skip_cfi_instructions, .-skip_cfi_instructions + .cfi_endproc + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .p2align 2 + .globl incomplete_jump_table + .type incomplete_jump_table,@function +incomplete_jump_table: +// CFG-LABEL: GS-PAUTH: non-protected call found in function incomplete_jump_table, basic block {{[^,]+}}, at address +// NOCFG-LABEL: GS-PAUTH: non-protected call found in function incomplete_jump_table, at address +// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW +// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are: +// CHECK-NEXT: 1. {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: This happens in the following basic block: +// CFG-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_incomplete_jump_table@0x{{[0-9a-f]+}} +// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16 +// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW + // Do not try to step past the start of the function. + ldrsw x16, [x17, x16, lsl #2] +1: + adr x17, 1b + add x16, x17, x16 + br x16 +2: + autiasp + ret +3: + autiasp + ret + .size incomplete_jump_table, .-incomplete_jump_table + .section .rodata,"a",@progbits + .p2align 2, 0x0 +4: + .word 2b-1b + .word 3b-1b + + .text + .globl main + .type main,@function +main: + mov x0, 0 + ret + .size main, .-main >From ca11a615dfd5e589cbc864aacbb5bc165b81ad05 Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko <[email protected]> Date: Mon, 23 Jun 2025 17:32:19 +0300 Subject: [PATCH 3/3] Update warning message in tests --- .../binary-analysis/AArch64/gs-pauth-jump-table.s | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s b/bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s index 5a42ed078e9c2..8af93ca8d9da5 100644 --- a/bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s +++ b/bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s @@ -22,7 +22,7 @@ .type good_jump_table,@function good_jump_table: // CHECK-NOT: good_jump_table -// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function good_jump_table +// CFG: GS-PAUTH: Warning: possibly imprecise CFG, the analysis quality may be degraded in this function. According to BOLT, unreachable code is found in function good_jump_table // CHECK-NOT: good_jump_table paciasp cmp x16, #0x2 @@ -56,7 +56,7 @@ good_jump_table: .type jump_table_relaxed_adrp_add,@function jump_table_relaxed_adrp_add: // CHECK-NOT: jump_table_relaxed_adrp_add -// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function jump_table_relaxed_adrp_add +// CFG: GS-PAUTH: Warning: possibly imprecise CFG, the analysis quality may be degraded in this function. According to BOLT, unreachable code is found in function jump_table_relaxed_adrp_add // CHECK-NOT: jump_table_relaxed_adrp_add paciasp cmp x16, #0x2 @@ -126,7 +126,7 @@ jump_table_wrong_hint: .type jump_table_unsafe_reg_1,@function jump_table_unsafe_reg_1: // CHECK-NOT: jump_table_unsafe_reg_1 -// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function jump_table_unsafe_reg_1 +// CFG: GS-PAUTH: Warning: possibly imprecise CFG, the analysis quality may be degraded in this function. According to BOLT, unreachable code is found in function jump_table_unsafe_reg_1 // CHECK-NOT: jump_table_unsafe_reg_1 paciasp cmp x1, #0x2 @@ -157,7 +157,7 @@ jump_table_unsafe_reg_1: .type jump_table_unsafe_reg_2,@function jump_table_unsafe_reg_2: // CHECK-NOT: jump_table_unsafe_reg_2 -// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function jump_table_unsafe_reg_2 +// CFG: GS-PAUTH: Warning: possibly imprecise CFG, the analysis quality may be degraded in this function. According to BOLT, unreachable code is found in function jump_table_unsafe_reg_2 // CHECK-NOT: jump_table_unsafe_reg_2 paciasp cmp x16, #0x2 @@ -189,7 +189,7 @@ jump_table_unsafe_reg_2: .type jump_table_wrong_limit,@function jump_table_wrong_limit: // CHECK-NOT: jump_table_wrong_limit -// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function jump_table_wrong_limit +// CFG: GS-PAUTH: Warning: possibly imprecise CFG, the analysis quality may be degraded in this function. According to BOLT, unreachable code is found in function jump_table_wrong_limit // CHECK-NOT: jump_table_wrong_limit paciasp cmp x16, #0x1000 @@ -634,7 +634,7 @@ jump_table_wrong_imm_3: skip_cfi_instructions: .cfi_startproc // CHECK-NOT: skip_cfi_instructions -// CFG: GS-PAUTH: Warning: the function has unreachable basic blocks (possibly incomplete CFG) in function skip_cfi_instructions +// CFG: GS-PAUTH: Warning: possibly imprecise CFG, the analysis quality may be degraded in this function. According to BOLT, unreachable code is found in function skip_cfi_instructions // CHECK-NOT: skip_cfi_instructions paciasp cmp x16, #0x2 _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
