https://github.com/atrosinenko updated 
https://github.com/llvm/llvm-project/pull/138884

>From be7f0d86c0c879bd9ab8fa6faf34a0be2c548de9 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 50b7d56470c99..33d36cccbcfff 100644
--- a/bolt/include/bolt/Core/MCInstUtils.h
+++ b/bolt/include/bolt/Core/MCInstUtils.h
@@ -154,6 +154,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

Reply via email to