https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/138884
>From cdc385aac7abe960340e26b2427ac9215e0a54fd Mon Sep 17 00:00:00 2001 From: Anatoly Trosinenko <atrosine...@accesssoftek.com> Date: Tue, 6 May 2025 11:31:03 +0300 Subject: [PATCH] [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 b495eb8ef5eec..5dd0aaa48d6e7 100644 --- a/bolt/include/bolt/Core/MCInstUtils.h +++ b/bolt/include/bolt/Core/MCInstUtils.h @@ -158,6 +158,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 87de6754017db..eb93d7de7fee9 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" @@ -699,6 +700,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 40f6edd59135c..b7c6d898988af 100644 --- a/bolt/lib/Core/MCInstUtils.cpp +++ b/bolt/lib/Core/MCInstUtils.cpp @@ -55,3 +55,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 5e08ae3fbf767..bda971bcd9343 100644 --- a/bolt/lib/Passes/PAuthGadgetScanner.cpp +++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp @@ -1328,6 +1328,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]) @@ -1358,6 +1363,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 2522de7005c64..423684f5c8cc0 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 _______________________________________________ llvm-branch-commits mailing list llvm-branch-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits