Thanks to visit codestin.com
Credit goes to github.com

Skip to content

[BOLT] Gadget scanner: prevent false positives due to jump tables #138884

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: users/atrosinenko/bolt-mcinstmatcher
Choose a base branch
from

Conversation

atrosinenko
Copy link
Contributor

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.

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.
@llvmbot
Copy link
Member

llvmbot commented May 7, 2025

@llvm/pr-subscribers-bolt

Author: Anatoly Trosinenko (atrosinenko)

Changes

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.


Patch is 32.03 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/138884.diff

6 Files Affected:

  • (modified) bolt/include/bolt/Core/MCInstUtils.h (+9)
  • (modified) bolt/include/bolt/Core/MCPlusBuilder.h (+14)
  • (modified) bolt/lib/Core/MCInstUtils.cpp (+20)
  • (modified) bolt/lib/Passes/PAuthGadgetScanner.cpp (+10)
  • (modified) bolt/lib/Target/AArch64/AArch64MCPlusBuilder.cpp (+73)
  • (added) bolt/test/binary-analysis/AArch64/gs-pauth-jump-table.s (+703)
diff --git a/bolt/include/bolt/Core/MCInstUtils.h b/bolt/include/bolt/Core/MCInstUtils.h
index b495eb8ef5eec..5dd0aaa48d6e7 100644
--- a/bolt/include/bolt/Core/MCInstUtils.h
+++ b/bolt/include/bolt/Core/MCInstUtils.h
@@ -158,6 +158,15 @@ class MCInstReference {
     return nullptr;
   }
 
+  /// Returns the only preceding instruction, or std::nullopt if multiple or no
+  /// predecessors are possible.
+  ///
+  /// If CFG information is available, basic block boundary can be crossed,
+  /// provided there is exactly one predecessor. If CFG is not available, the
+  /// preceding instruction in the offset order is returned, unless this is the
+  /// first instruction of the function.
+  std::optional<MCInstReference> getSinglePredecessor();
+
   raw_ostream &print(raw_ostream &OS) const;
 };
 
diff --git a/bolt/include/bolt/Core/MCPlusBuilder.h b/bolt/include/bolt/Core/MCPlusBuilder.h
index 87de6754017db..eb93d7de7fee9 100644
--- a/bolt/include/bolt/Core/MCPlusBuilder.h
+++ b/bolt/include/bolt/Core/MCPlusBuilder.h
@@ -14,6 +14,7 @@
 #ifndef BOLT_CORE_MCPLUSBUILDER_H
 #define BOLT_CORE_MCPLUSBUILDER_H
 
+#include "bolt/Core/MCInstUtils.h"
 #include "bolt/Core/MCPlus.h"
 #include "bolt/Core/Relocation.h"
 #include "llvm/ADT/ArrayRef.h"
@@ -699,6 +700,19 @@ class MCPlusBuilder {
     return std::nullopt;
   }
 
+  /// Tests if BranchInst corresponds to an instruction sequence which is known
+  /// to be a safe dispatch via jump table.
+  ///
+  /// The target can decide which instruction sequences to consider "safe" from
+  /// the Pointer Authentication point of view, such as any jump table dispatch
+  /// sequence without function calls inside, any sequence which is contiguous,
+  /// or only some specific well-known sequences.
+  virtual bool
+  isSafeJumpTableBranchForPtrAuth(MCInstReference BranchInst) const {
+    llvm_unreachable("not implemented");
+    return false;
+  }
+
   virtual bool isTerminator(const MCInst &Inst) const;
 
   virtual bool isNoop(const MCInst &Inst) const {
diff --git a/bolt/lib/Core/MCInstUtils.cpp b/bolt/lib/Core/MCInstUtils.cpp
index 40f6edd59135c..b7c6d898988af 100644
--- a/bolt/lib/Core/MCInstUtils.cpp
+++ b/bolt/lib/Core/MCInstUtils.cpp
@@ -55,3 +55,23 @@ raw_ostream &MCInstReference::print(raw_ostream &OS) const {
   OS << ">";
   return OS;
 }
+
+std::optional<MCInstReference> MCInstReference::getSinglePredecessor() {
+  if (const RefInBB *Ref = tryGetRefInBB()) {
+    if (Ref->It != Ref->BB->begin())
+      return MCInstReference(Ref->BB, &*std::prev(Ref->It));
+
+    if (Ref->BB->pred_size() != 1)
+      return std::nullopt;
+
+    BinaryBasicBlock *PredBB = *Ref->BB->pred_begin();
+    assert(!PredBB->empty() && "Empty basic blocks are not supported yet");
+    return MCInstReference(PredBB, &*PredBB->rbegin());
+  }
+
+  const RefInBF &Ref = getRefInBF();
+  if (Ref.It == Ref.BF->instrs().begin())
+    return std::nullopt;
+
+  return MCInstReference(Ref.BF, std::prev(Ref.It));
+}
diff --git a/bolt/lib/Passes/PAuthGadgetScanner.cpp b/bolt/lib/Passes/PAuthGadgetScanner.cpp
index 7b8b1513ef33c..4bd1ccb4520c1 100644
--- a/bolt/lib/Passes/PAuthGadgetScanner.cpp
+++ b/bolt/lib/Passes/PAuthGadgetScanner.cpp
@@ -1311,6 +1311,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])
@@ -1341,6 +1346,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
+        ...
[truncated]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants