[llvm] [BOLT] Gadget scanner: prevent false positives due to jump tables (PR #138884)
Anatoly Trosinenko via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 17 04:55:25 PDT 2026
https://github.com/atrosinenko updated https://github.com/llvm/llvm-project/pull/138884
>From 66ec90fe789a742c986c66a6b7c0dcbd863c1218 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Tue, 6 May 2025 11:31:03 +0300
Subject: [PATCH 1/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 | 705 ++++++++++++++++++
6 files changed, 831 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 291e31e0e0fdf3..a240ca07bd02cf 100644
--- a/bolt/include/bolt/Core/MCInstUtils.h
+++ b/bolt/include/bolt/Core/MCInstUtils.h
@@ -101,6 +101,15 @@ class MCInstReference {
/// this function may be called from multithreaded code.
uint64_t computeAddress(const MCCodeEmitter *Emitter = nullptr) const;
+ /// 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;
private:
diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index 79298c57985f2e..f970f1d551ca4f 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"
@@ -764,6 +765,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 f505bf73c64eb9..f07616cdb86b95 100644
--- a/bolt/lib/Core/MCInstUtils.cpp
+++ b/bolt/lib/Core/MCInstUtils.cpp
@@ -84,3 +84,23 @@ raw_ostream &MCInstReference::print(raw_ostream &OS) const {
OS << ">";
return OS;
}
+
+std::optional<MCInstReference> MCInstReference::getSinglePredecessor() {
+ if (const RefInBB *Ref = tryGetRefInBB()) {
+ if (Ref->Index != 0)
+ return MCInstReference(*Ref->BB, Ref->Index - 1);
+
+ 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 542af49e26205a..148987f89a788e 100644
--- a/bolt/lib/Passes/PAuthGadgetScanner.cpp
+++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp
@@ -1457,6 +1457,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])
@@ -1487,6 +1492,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 2a39aa63554d93..26ba07dcb14f92 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -703,6 +703,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 LowLevelInstMatcherDSL;
+ 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::NOP) || !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 00000000000000..08c08bd52f19e1
--- /dev/null
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s
@@ -0,0 +1,705 @@
+// -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=ptrauth-all %t.exe 2>&1 | FileCheck --check-prefixes=CHECK,CFG %s
+// RUN: llvm-bolt-binary-analysis --scanners=ptrauth-all --auth-traps-on-failure %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=ptrauth-all %t.exe 2>&1 | FileCheck --check-prefixes=CHECK,NOCFG %s
+// RUN: llvm-bolt-binary-analysis --scanners=ptrauth-all --auth-traps-on-failure %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, at function
+good_jump_table:
+// CHECK-NOT: 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
+ 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", at 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, at function
+jump_table_relaxed_adrp_add:
+// CHECK-NOT: 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
+ 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", at progbits
+ .p2align 2, 0x0
+4:
+ .word 2b-1b
+ .word 3b-1b
+
+ .text
+ .p2align 2
+ .globl jump_table_wrong_hint
+ .type jump_table_wrong_hint, at 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 at 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", at 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, at function
+jump_table_unsafe_reg_1:
+// CHECK-NOT: 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
+ 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", at 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, at function
+jump_table_unsafe_reg_2:
+// CHECK-NOT: 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
+ 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", at 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, at function
+jump_table_wrong_limit:
+// CHECK-NOT: 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
+ 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", at 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, at 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 at 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", at 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, at 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 at 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", at 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, at 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 at 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", at 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, at 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 at 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", at 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, at 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", at 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, at 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 at 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", at 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, at 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 at 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", at 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, at 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 at 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", at 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, at 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 at 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", at 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, at 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 at 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", at 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, at 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 at 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", at 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, at function
+skip_cfi_instructions:
+ .cfi_startproc
+// CHECK-NOT: 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
+ 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", at progbits
+ .p2align 2, 0x0
+4:
+ .word 2b-1b
+ .word 3b-1b
+
+ .text
+ .p2align 2
+ .globl incomplete_jump_table
+ .type incomplete_jump_table, at 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 at 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", at progbits
+ .p2align 2, 0x0
+4:
+ .word 2b-1b
+ .word 3b-1b
+
+ .text
+ .globl main
+ .type main, at function
+main:
+ mov x0, 0
+ ret
+ .size main, .-main
>From 211fc720d49b1f13e4dea2f2ece6b84e8737ac0d Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Tue, 15 Sep 2026 13:26:36 +0300
Subject: [PATCH 2/3] Validate jump table bounds, refactor test cases
---
bolt/include/bolt/Core/MCInstUtils.h | 64 +++
.../Target/AArch64/AArch64MCPlusBuilder.cpp | 42 +-
.../AArch64/gs-pauth-jump-table.s | 524 ++++++++++++++----
3 files changed, 498 insertions(+), 132 deletions(-)
diff --git a/bolt/include/bolt/Core/MCInstUtils.h b/bolt/include/bolt/Core/MCInstUtils.h
index a240ca07bd02cf..78e27f8363dfdc 100644
--- a/bolt/include/bolt/Core/MCInstUtils.h
+++ b/bolt/include/bolt/Core/MCInstUtils.h
@@ -11,6 +11,8 @@
#include "bolt/Core/BinaryBasicBlock.h"
#include "bolt/Core/MCPlus.h"
+#include "llvm/MC/MCExpr.h"
+#include "llvm/Support/Casting.h"
#include <map>
#include <variant>
@@ -256,6 +258,7 @@ template <typename T> class OpMatcher {
mutable std::optional<T> Value;
mutable std::optional<T> SavedValue;
+protected:
// Remember/restore the last Value - to be called by matchInst.
void remember() const { SavedValue = Value; }
void restore() const { Value = SavedValue; }
@@ -316,6 +319,67 @@ class Imm : public OpMatcher<int64_t> {
: OpMatcher<int64_t>(ImmToMatch) {}
};
+class SymbolWithSpecifier;
+
+class Symbol : public OpMatcher<const MCSymbol *> {
+ bool matches(const MCOperand &Op) const {
+ if (!Op.isExpr())
+ return false;
+
+ return matchExpr(Op.getExpr());
+ }
+
+ bool matchExpr(const MCExpr *Expr) const {
+ if (auto *SymbolRef = dyn_cast<MCSymbolRefExpr>(Expr))
+ return matchValue(&SymbolRef->getSymbol());
+
+ return false;
+ }
+
+ template <class... OpMatchers>
+ friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...);
+
+ friend class SymbolWithSpecifier;
+
+public:
+ Symbol(std::optional<MCSymbol *> SymToMatch = std::nullopt)
+ : OpMatcher<const MCSymbol *>(SymToMatch) {}
+
+ SymbolWithSpecifier withSpec(unsigned Specifier);
+};
+
+// Wrapper class to be instantiated by Symbol::withSpec().
+class SymbolWithSpecifier {
+ const Symbol &Parent;
+ const unsigned Specifier;
+
+ SymbolWithSpecifier(Symbol &Parent, unsigned Specifier)
+ : Parent(Parent), Specifier(Specifier) {}
+
+ void remember() const { Parent.remember(); }
+ void restore() const { Parent.restore(); }
+
+ bool matches(const MCOperand &Op) const {
+ if (!Op.isExpr())
+ return false;
+
+ auto *Expr = dyn_cast<MCSpecifierExpr>(Op.getExpr());
+ if (!Expr || Expr->getSpecifier() != Specifier)
+ return false;
+
+ return Parent.matchExpr(Expr->getSubExpr());
+ }
+
+ template <class... OpMatchers>
+ friend bool matchInst(const MCInst &, unsigned, const OpMatchers &...);
+
+ friend class Symbol;
+};
+
+inline SymbolWithSpecifier Symbol::withSpec(unsigned Specifier) {
+ return SymbolWithSpecifier(*this, Specifier);
+}
+
/// Tries to match Inst and updates Ops on success.
///
/// If Inst has the specified Opcode and its operand list prefix matches Ops,
diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index 26ba07dcb14f92..aaf2428c66fc9c 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -20,6 +20,7 @@
#include "Utils/AArch64BaseInfo.h"
#include "bolt/Core/BinaryBasicBlock.h"
#include "bolt/Core/BinaryFunction.h"
+#include "bolt/Core/BinarySection.h"
#include "bolt/Core/MCInstUtils.h"
#include "bolt/Core/MCPlusBuilder.h"
#include "llvm/BinaryFormat/ELF.h"
@@ -705,6 +706,9 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
bool
isSafeJumpTableBranchForPtrAuth(MCInstReference BranchInst) const override {
+ const BinaryFunction *BF = BranchInst.getFunction();
+ const BinaryContext &BC = BF->getBinaryContext();
+
MCInstReference CurRef = BranchInst;
auto StepBack = [&]() {
do {
@@ -717,7 +721,9 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
return true;
};
- // Match this contiguous sequence:
+ // Match this contiguous sequence, stepping from its last instruction
+ // to the first one:
+ //
// cmp Xm, #count
// csel Xm, Xm, xzr, ls
// adrp Xn, .LJTIxyz
@@ -728,10 +734,10 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
// add Xm, Xn, Xm
// br Xm
- // FIXME: Check label operands of ADR/ADRP+ADD and #count operand of CMP.
-
using namespace LowLevelInstMatcherDSL;
+ Imm CountBase, CountExp;
Reg Xm, Xn;
+ Symbol LJTI;
if (!matchInst(CurRef, AArch64::BR, Xm) || !StepBack())
return false;
@@ -739,6 +745,8 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
if (!matchInst(CurRef, AArch64::ADDXrs, Xm, Xn, Xm, Imm(0)) || !StepBack())
return false;
+ // The base address is not validated for now, neither are the offsets
+ // **contained** in the jump table.
if (!matchInst(CurRef, AArch64::ADR, Xn /*, .Ltmp*/) || !StepBack())
return false;
@@ -746,16 +754,19 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
!StepBack())
return false;
- if (matchInst(CurRef, AArch64::ADR, Xn /*, .LJTIxyz*/)) {
+ if (matchInst(CurRef, AArch64::ADR, Xn, LJTI.withSpec(AArch64::S_ABS))) {
if (!StepBack())
return false;
if (!matchInst(CurRef, AArch64::NOP) || !StepBack())
return false;
- } else if (matchInst(CurRef, AArch64::ADDXri, Xn,
- Xn /*, :lo12:.LJTIxyz*/)) {
+ } else if (matchInst(CurRef, AArch64::ADDXri, Xn, Xn,
+ LJTI.withSpec(AArch64::S_LO12))) {
if (!StepBack())
return false;
- if (!matchInst(CurRef, AArch64::ADRP, Xn /*, .LJTIxyz*/) || !StepBack())
+
+ if (!matchInst(CurRef, AArch64::ADRP, Xn,
+ LJTI.withSpec(AArch64::S_ABS_PAGE)) ||
+ !StepBack())
return false;
} else {
return false;
@@ -766,8 +777,21 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
!StepBack())
return false;
- if (!matchInst(CurRef, AArch64::SUBSXri, Reg(AArch64::XZR),
- Xm /*, #count*/))
+ if (!matchInst(CurRef, AArch64::SUBSXri, Reg(AArch64::XZR), Xm, CountBase,
+ CountExp))
+ return false;
+
+ // Make sure the jump table is located inside a read-only section.
+
+ uint64_t JumpTableSize = 4 * (CountBase.get() << CountExp.get());
+ ErrorOr<uint64_t> JumpTableAddress = BC.getSymbolValue(*LJTI.get());
+ // For now, conservatively reject symbols with non-zero offsets.
+ if (!JumpTableAddress || LJTI.get()->getOffset() != 0)
+ return false;
+ auto BinarySection = BC.getSectionForAddress(*JumpTableAddress);
+
+ if (!BinarySection || BinarySection->isWritable() ||
+ !BinarySection->containsAddress(*JumpTableAddress + JumpTableSize - 1))
return false;
// Some platforms treat X16 and X17 as more protected registers, others
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 08c08bd52f19e1..bb6fda5158274f 100644
--- a/bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s
+++ b/bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s
@@ -1,31 +1,26 @@
// -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.
+//
+// Note: for simplicity, matching well-known jump table sequences is not
+// supported without relocations (i.e. without passing `-Wl,--emit-relocs`
+// option to clang).
// RUN: %clang %cflags -march=armv8.3-a -Wl,--no-relax -Wl,--emit-relocs %s -o %t.exe
-// RUN: llvm-bolt-binary-analysis --scanners=ptrauth-all %t.exe 2>&1 | FileCheck --check-prefixes=CHECK,CFG %s
-// RUN: llvm-bolt-binary-analysis --scanners=ptrauth-all --auth-traps-on-failure %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=ptrauth-all %t.exe 2>&1 | FileCheck --check-prefixes=CHECK,NOCFG %s
-// RUN: llvm-bolt-binary-analysis --scanners=ptrauth-all --auth-traps-on-failure %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.
+// RUN: llvm-bolt-binary-analysis --scanners=ptrauth-all %t.exe 2>&1 | FileCheck --implicit-check-not=GS-PAUTH %s
+// RUN: llvm-bolt-binary-analysis --scanners=ptrauth-all --auth-traps-on-failure %t.exe 2>&1 | FileCheck --implicit-check-not=GS-PAUTH %s
// 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.
+// each basic block without any predecessors known to BOLT.
// 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.
+// silence "non-protected ret" false-positives and explicitly check that
+// "imprecise CFG" warning is produced.
.text
.p2align 2
- .globl good_jump_table
- .type good_jump_table, at function
-good_jump_table:
-// CHECK-NOT: 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
+ .globl good_anonymous_jump_table
+ .type good_anonymous_jump_table, at function
+good_anonymous_jump_table:
+// CHECK: 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_anonymous_jump_table
paciasp
cmp x16, #0x2
csel x16, x16, xzr, ls
@@ -42,13 +37,161 @@ good_jump_table:
3:
autiasp
ret
- .size good_jump_table, .-good_jump_table
+ .size good_anonymous_jump_table, .-good_anonymous_jump_table
+ .section .rodata,"a", at progbits
+ .p2align 2, 0x0
+4:
+ .word 2b-1b
+ .word 3b-1b
+
+ .text
+ .p2align 2
+ .globl good_local_jump_table
+ .type good_local_jump_table, at function
+good_local_jump_table:
+// CHECK: 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_local_jump_table
+ paciasp
+ cmp x16, #0x2
+ csel x16, x16, xzr, ls
+ adrp x17, .LJTIlocal
+ add x17, x17, :lo12:.LJTIlocal
+ ldrsw x16, [x17, x16, lsl #2]
+1:
+ adr x17, 1b
+ add x16, x17, x16
+ br x16
+2:
+ autiasp
+ ret
+3:
+ autiasp
+ ret
+ .size good_local_jump_table, .-good_local_jump_table
+ .section .rodata,"a", at progbits
+ .p2align 2, 0x0
+ .local .LJTIlocal
+ .type .LJTIlocal, at object
+.LJTIlocal:
+ .word 2b-1b
+ .word 3b-1b
+ .size .LJTIlocal, .-.LJTIlocal
+
+ .text
+ .p2align 2
+ .globl good_global_jump_table
+ .type good_global_jump_table, at function
+good_global_jump_table:
+// CHECK: 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_global_jump_table
+ paciasp
+ cmp x16, #0x2
+ csel x16, x16, xzr, ls
+ adrp x17, LJTIglobal
+ add x17, x17, :lo12:LJTIglobal
+ ldrsw x16, [x17, x16, lsl #2]
+1:
+ adr x17, 1b
+ add x16, x17, x16
+ br x16
+2:
+ autiasp
+ ret
+3:
+ autiasp
+ ret
+ .size good_global_jump_table, .-good_global_jump_table
+ .section .rodata,"a", at progbits
+ .p2align 2, 0x0
+ .globl LJTIglobal
+ .type LJTIglobal, at object
+LJTIglobal:
+ .word 2b-1b
+ .word 3b-1b
+ .size LJTIglobal, .-LJTIglobal
+
+ .text
+ .p2align 2
+ .globl mismatched_adrp_add_syms
+ .type mismatched_adrp_add_syms, at function
+mismatched_adrp_add_syms:
+// CHECK-LABEL: GS-PAUTH: Warning: possibly imprecise CFG, the analysis quality may be degraded in this function. According to BOLT, unreachable code is found in function mismatched_adrp_add_syms
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function mismatched_adrp_add_syms, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function mismatched_adrp_add_syms, basic block {{[^,]+}}, 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
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_mismatched_adrp_add_syms at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-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:5f
+ ldrsw x16, [x17, x16, lsl #2]
+1:
+ adr x17, 1b
+ add x16, x17, x16
+ br x16
+2:
+ autiasp
+ ret
+3:
+ autiasp
+ ret
+ .size mismatched_adrp_add_syms, .-mismatched_adrp_add_syms
.section .rodata,"a", at progbits
.p2align 2, 0x0
+4:
+ .word 2b-1b
+5:
+ .word 3b-1b
+
+ .text
+ .p2align 2
+ .globl writable_jump_table
+ .type writable_jump_table, at function
+writable_jump_table:
+// CHECK-LABEL: GS-PAUTH: Warning: possibly imprecise CFG, the analysis quality may be degraded in this function. According to BOLT, unreachable code is found in function writable_jump_table
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function writable_jump_table, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function writable_jump_table, basic block {{[^,]+}}, 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
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_writable_jump_table at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-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
+ br x16
+2:
+ autiasp
+ ret
+3:
+ autiasp
+ ret
+ .size writable_jump_table, .-writable_jump_table
+ .section .data,"a", at 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.
@@ -57,9 +200,7 @@ good_jump_table:
.globl jump_table_relaxed_adrp_add
.type jump_table_relaxed_adrp_add, at function
jump_table_relaxed_adrp_add:
-// CHECK-NOT: 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
+// CHECK: 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
paciasp
cmp x16, #0x2
csel x16, x16, xzr, ls
@@ -88,15 +229,19 @@ jump_table_relaxed_adrp_add:
.globl jump_table_wrong_hint
.type jump_table_wrong_hint, at 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-LABEL: 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_hint
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_wrong_hint, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_hint, basic block {{[^,]+}}, 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 at 0x{{[0-9a-f]+}}
-// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
-// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_hint at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
paciasp
cmp x16, #0x2
csel x16, x16, xzr, ls
@@ -127,9 +272,7 @@ jump_table_wrong_hint:
.globl jump_table_unsafe_reg_1
.type jump_table_unsafe_reg_1, at function
jump_table_unsafe_reg_1:
-// CHECK-NOT: 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
+// CHECK: 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
paciasp
cmp x1, #0x2
csel x1, x1, xzr, ls
@@ -158,9 +301,7 @@ jump_table_unsafe_reg_1:
.globl jump_table_unsafe_reg_2
.type jump_table_unsafe_reg_2, at function
jump_table_unsafe_reg_2:
-// CHECK-NOT: 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
+// CHECK: 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
paciasp
cmp x16, #0x2
csel x16, x16, xzr, ls
@@ -184,15 +325,69 @@ jump_table_unsafe_reg_2:
.word 2b-1b
.word 3b-1b
-// FIXME: Detect possibility of jump table overflow.
+// Basic checks are performed by gadget scanner to make sure the entire jump table
+// fits in a read-only section. As the limit is technically specified via two
+// immediate operands of CMP instruction (aliased to SUBS), test both.
+
+ .text
+ .p2align 2
+ .globl jump_table_overflow_unscaled
+ .type jump_table_overflow_unscaled, at function
+jump_table_overflow_unscaled:
+// CHECK-LABEL: 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_overflow_unscaled
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_overflow_unscaled, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_overflow_unscaled, basic block {{[^,]+}}, 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
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_overflow_unscaled at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+ paciasp
+ cmp x16, #0x100
+ 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_overflow_unscaled, .-jump_table_overflow_unscaled
+ .section .rodata,"a", at progbits
+ .p2align 2, 0x0
+4:
+ .word 2b-1b
+ .word 3b-1b
+
.text
.p2align 2
- .globl jump_table_wrong_limit
- .type jump_table_wrong_limit, at function
-jump_table_wrong_limit:
-// CHECK-NOT: 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
+ .globl jump_table_overflow_scaled
+ .type jump_table_overflow_scaled, at function
+jump_table_overflow_scaled:
+// CHECK-LABEL: 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_overflow_scaled
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_overflow_scaled, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_overflow_scaled, basic block {{[^,]+}}, 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
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_overflow_scaled at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
paciasp
cmp x16, #0x1000
csel x16, x16, xzr, ls
@@ -209,7 +404,7 @@ jump_table_wrong_limit:
3:
autiasp
ret
- .size jump_table_wrong_limit, .-jump_table_wrong_limit
+ .size jump_table_overflow_scaled, .-jump_table_overflow_scaled
.section .rodata,"a", at progbits
.p2align 2, 0x0
4:
@@ -221,16 +416,20 @@ jump_table_wrong_limit:
.globl jump_table_unrelated_inst_1
.type jump_table_unrelated_inst_1, at 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-LABEL: 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_unrelated_inst_1
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_unrelated_inst_1, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_unrelated_inst_1, basic block {{[^,]+}}, 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 at 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
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_unrelated_inst_1 at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: nop
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
paciasp
cmp x16, #0x2
csel x16, x16, xzr, ls
@@ -260,15 +459,19 @@ jump_table_unrelated_inst_1:
.globl jump_table_unrelated_inst_2
.type jump_table_unrelated_inst_2, at 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-LABEL: 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_unrelated_inst_2
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_unrelated_inst_2, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_unrelated_inst_2, basic block {{[^,]+}}, 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 at 0x{{[0-9a-f]+}}
-// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
-// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_unrelated_inst_2 at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
paciasp
cmp x16, #0x2
csel x16, x16, xzr, ls
@@ -298,15 +501,19 @@ jump_table_unrelated_inst_2:
.globl jump_table_multiple_predecessors_1
.type jump_table_multiple_predecessors_1, at 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 at 0x{{[0-9a-f]+}}
-// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
-// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+// CHECK-LABEL: 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_multiple_predecessors_1
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_multiple_predecessors_1, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_multiple_predecessors_1, basic block {{[^,]+}}, 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
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_multiple_predecessors_1 at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-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
@@ -336,15 +543,19 @@ jump_table_multiple_predecessors_1:
.globl jump_table_multiple_predecessors_2
.type jump_table_multiple_predecessors_2, at 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 at 0x{{[0-9a-f]+}}
-// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
-// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+// CHECK-LABEL: 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_multiple_predecessors_2
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_multiple_predecessors_2, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_multiple_predecessors_2, basic block {{[^,]+}}, 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
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_multiple_predecessors_2 at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-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
@@ -377,8 +588,12 @@ jump_table_multiple_predecessors_2:
.globl jump_table_wrong_reg_1
.type jump_table_wrong_reg_1, at 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-LABEL: 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_reg_1
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_wrong_reg_1, basic block {{[^,]+}}, at address
+// CHECK-NEXT: The instruction is {{[0-9a-f]+}}: br x1 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: The 1 instructions that write to the affected registers after any authentication are:
+// CHECK-NEXT: 1. {{[0-9a-f]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_1, basic block {{[^,]+}}, 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
@@ -409,15 +624,19 @@ jump_table_wrong_reg_1:
.globl jump_table_wrong_reg_2
.type jump_table_wrong_reg_2, at 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-LABEL: 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_reg_2
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_wrong_reg_2, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_2, basic block {{[^,]+}}, 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 at 0x{{[0-9a-f]+}}
-// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x1
-// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_reg_2 at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x1
+// CHECK-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
paciasp
cmp x16, #0x2
csel x16, x16, xzr, ls
@@ -446,15 +665,19 @@ jump_table_wrong_reg_2:
.globl jump_table_wrong_reg_3
.type jump_table_wrong_reg_3, at 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-LABEL: 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_reg_3
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_wrong_reg_3, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_3, basic block {{[^,]+}}, 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 at 0x{{[0-9a-f]+}}
-// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
-// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_reg_3 at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
paciasp
cmp x16, #0x2
csel x16, x16, xzr, ls
@@ -483,15 +706,19 @@ jump_table_wrong_reg_3:
.globl jump_table_wrong_reg_4
.type jump_table_wrong_reg_4, at 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-LABEL: 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_reg_4
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_wrong_reg_4, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_reg_4, basic block {{[^,]+}}, 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 at 0x{{[0-9a-f]+}}
-// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
-// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_reg_4 at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
paciasp
cmp x16, #0x2
csel x16, x16, x1, ls // wrong reg
@@ -520,15 +747,19 @@ jump_table_wrong_reg_4:
.globl jump_table_wrong_imm_1
.type jump_table_wrong_imm_1, at 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-LABEL: 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_imm_1
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_wrong_imm_1, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_imm_1, basic block {{[^,]+}}, 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 at 0x{{[0-9a-f]+}}
-// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
-// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_imm_1 at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
paciasp
cmp x16, #0x2
csel x16, x16, xzr, ls
@@ -557,21 +788,25 @@ jump_table_wrong_imm_1:
.globl jump_table_wrong_imm_2
.type jump_table_wrong_imm_2, at 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-LABEL: 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_imm_2
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_wrong_imm_2, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_imm_2, basic block {{[^,]+}}, 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 at 0x{{[0-9a-f]+}}
-// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
-// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_imm_2 at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
paciasp
cmp x16, #0x2
- csel x16, x16, xzr, lt // wrong: lt instead of ls
+ csel x16, x16, xzr, ls
adrp x17, 4f
add x17, x17, :lo12:4f
- ldrsw x16, [x17, x16, lsl #2]
+ ldrsw x16, [x17, x16, lsl #0] // wrong shift amount
1:
adr x17, 1b
add x16, x17, x16
@@ -594,15 +829,60 @@ jump_table_wrong_imm_2:
.globl jump_table_wrong_imm_3
.type jump_table_wrong_imm_3, at 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-LABEL: 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_imm_3
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_wrong_imm_3, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_imm_3, basic block {{[^,]+}}, 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 at 0x{{[0-9a-f]+}}
-// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
-// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_imm_3 at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-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_3, .-jump_table_wrong_imm_3
+ .section .rodata,"a", at progbits
+ .p2align 2, 0x0
+4:
+ .word 2b-1b
+ .word 3b-1b
+
+ .text
+ .p2align 2
+ .globl jump_table_wrong_imm_4
+ .type jump_table_wrong_imm_4, at function
+jump_table_wrong_imm_4:
+// CHECK-LABEL: 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_imm_4
+// CHECK-LABEL: GS-PAUTH: untrusted link register found before tail call in function jump_table_wrong_imm_4, basic block {{[^,]+}}, 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]+}}: paciasp
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function jump_table_wrong_imm_4, basic block {{[^,]+}}, 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
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_jump_table_wrong_imm_4 at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
paciasp
cmp x16, #0x2
csel x16, x16, xzr, ls
@@ -619,7 +899,7 @@ jump_table_wrong_imm_3:
3:
autiasp
ret
- .size jump_table_wrong_imm_3, .-jump_table_wrong_imm_3
+ .size jump_table_wrong_imm_4, .-jump_table_wrong_imm_4
.section .rodata,"a", at progbits
.p2align 2, 0x0
4:
@@ -635,9 +915,7 @@ jump_table_wrong_imm_3:
.type skip_cfi_instructions, at function
skip_cfi_instructions:
.cfi_startproc
-// CHECK-NOT: 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
+// CHECK: 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
paciasp
cmp x16, #0x2
csel x16, x16, xzr, ls
@@ -668,15 +946,15 @@ skip_cfi_instructions:
.globl incomplete_jump_table
.type incomplete_jump_table, at 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-LABEL: GS-PAUTH: Warning: possibly imprecise CFG, the analysis quality may be degraded in this function. According to BOLT, unreachable code is found in function incomplete_jump_table
+// CHECK-LABEL: GS-PAUTH: non-protected call found in function incomplete_jump_table, basic block {{[^,]+}}, 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 at 0x{{[0-9a-f]+}}
-// CFG-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
-// CFG-NEXT: {{[0-9a-f]+}}: br x16 # UNKNOWN CONTROL FLOW
+// CHECK-NEXT: This happens in the following basic block:
+// CHECK-NEXT: {{[0-9a-f]+}}: adr x17, __ENTRY_incomplete_jump_table at 0x{{[0-9a-f]+}}
+// CHECK-NEXT: {{[0-9a-f]+}}: add x16, x17, x16
+// CHECK-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:
>From 65db753540301fff9e0405b2535e431507b18cb1 Mon Sep 17 00:00:00 2001
From: Anatoly Trosinenko <atrosinenko at accesssoftek.com>
Date: Thu, 17 Sep 2026 14:13:40 +0300
Subject: [PATCH 3/3] Access CountExp operand with AArch64_AM::getShiftValue
---
bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
index aaf2428c66fc9c..1468ab9fb34a36 100644
--- a/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
+++ b/bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp
@@ -783,7 +783,8 @@ class AArch64MCPlusBuilder : public MCPlusBuilder {
// Make sure the jump table is located inside a read-only section.
- uint64_t JumpTableSize = 4 * (CountBase.get() << CountExp.get());
+ uint64_t JumpTableSize =
+ 4 * (CountBase.get() << AArch64_AM::getShiftValue(CountExp.get()));
ErrorOr<uint64_t> JumpTableAddress = BC.getSymbolValue(*LJTI.get());
// For now, conservatively reject symbols with non-zero offsets.
if (!JumpTableAddress || LJTI.get()->getOffset() != 0)
More information about the llvm-commits
mailing list