Skip to content

Arm64: [PAC-RET] Add Pointer Authentication support for Arm64#125436

Open
SwapnilGaikwad wants to merge 166 commits into
dotnet:mainfrom
SwapnilGaikwad:github-add-pac
Open

Arm64: [PAC-RET] Add Pointer Authentication support for Arm64#125436
SwapnilGaikwad wants to merge 166 commits into
dotnet:mainfrom
SwapnilGaikwad:github-add-pac

Conversation

@SwapnilGaikwad

@SwapnilGaikwad SwapnilGaikwad commented Mar 11, 2026

Copy link
Copy Markdown
Contributor

This PR adds support for Pointer Authentication (PAC) on Arm64. Pointer Authentication (PAC) is an Armv8.3+ security feature designed to mitigate Return-Oriented Programming (ROP) attacks by cryptographically signing return addresses. While using PAC, we store a signed return address, instead of the plain address, on the stack and later authenticate it before returning from a function. It ensures control flow returns to the intended caller.

More details on PAC and its role in software security can be found (here).

  • The current implementation of PAC is turned off by default, but can be turned on by setting DOTNET_JitPacEnabled=1.
  • PAC protects link register (LR) by signing it in the prolog (using paciasp) before it is split, using the current SP as the modifier. It then authenticates the LR in the epilog (using autiasp) before the function returns. If the signature is invalid, the execution fails with SIGILL.
  • When the runtime needs to read or overwrite a return address during hijacking for GC, it now strips the PAC (using xpaclri) and re-signs the new target address before storing it back.

ToDos

  • Disable PAC by default before merge.
  • Restore the original frame layout that used pre-indexed variant of stp to store FP/LR.
  • Authenticate the return address instead of stripping in return address hijacking and unwinding.
  • Identify increased binary size for System.*.dll
  • Determine performance regressions using benchmarks such as OrchirdCMS.

Contributes to #109457

This PR adds support for Pointer Authentication (PAC) on Arm64. Pointer Authentication (PAC) is an Armv8.3+ security feature designed to mitigate Return-Oriented Programming (ROP) attacks by cryptographically signing return addresses. While using PAC, we store a signed return address, instead of the plain address, on the stack and later authenticate it before returning from a function. It ensures control flow returns to the intended caller.

More details on PAC and its role in software security can be found ([here](https://llsoftsec.github.io/llsoftsecbook/#sec:pointer-authentication)).

- The current implementation of PAC is turned off by default, but can be turned on by setting DOTNET_JitPacEnabled=1.
- PAC protects link register (LR) by signing it in the prolog (using `paciasp`) before it is split, using the current SP as the modifier. It then authenticates the LR in the epilog (using `autiasp`) before the function returns. If the signature is invalid, the execution fails with `SIGILL`.
- - When the runtime needs to read or overwrite a return address during hijacking for GC, it now strips the PAC (using `xpaclri`) and re-signs the new target address before storing it back.
- To simply tracking the SP in return address hijacking, we avoid using the pre-indexed variant of storing FP/LR on stack (e.g., `stp fp,lr,[sp,-#framesz]! `) to simply tracking the SP in return address hijacking. We obtain the value of SP at the time of signing the LR from the location of the current FP.  We can't use this approach when the pre-indexed `stp` is used because we don't know the`#framesz`.
- The updated prolog/epilog sequences generated by the JIT now look like:

 // Prolog
 sub     sp, sp, #framesz
 paciasp                 ; sign LR with A-key + SP
 stp     fp, lr, [sp]

 // Epilog
 ldp     fp, lr, [sp]
 autiasp                 ; authenticate LR
 add     sp, sp, #framesz
 ret

ToDos:
[] Restore the original frame layout that used pre-indexed variant of `stp` to store FP/LR.
[] Authenticate the return address instead of stripping in return address hijacking and unwinding.
[] Identify increased binary size for System.*.dll
[] Determine performance regressions using benchmarks such as OrchirdCMS.
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label Mar 11, 2026
@jkotas jkotas added area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI arch-arm64 and removed area-NativeAOT-coreclr labels Mar 11, 2026
@dotnet-policy-service

Copy link
Copy Markdown
Contributor

Tagging subscribers to this area: @JulieLeeMSFT, @jakobbotsch
See info in area-owners.md if you want to be subscribed.

@jkotas

jkotas commented Jul 3, 2026

Copy link
Copy Markdown
Member

/azp run runtime-nativeaot-outerloop

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines successfully started running 1 pipeline(s).

jkotas added a commit that referenced this pull request Jul 9, 2026
This PR covers the final subset of changes from #125436 related to
NativeAOT as suggested in
[comment](#125436 (comment)).
It follows the previous work from- #127949, #127838 and #128147.

More details on PAC and its role in software security can be found
([here](https://llsoftsec.github.io/llsoftsecbook/#sec:pointer-authentication)).

---------

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
This PR adds instruction encodings for Pointer Authentication
instructions, especially the B key variants.
Part of this is useful for #125436 and #127838.

Add encoding for:

1.  AUTIB
2.  AUTIB1716
3.  AUTIBSP
4.  AUTIBZ
5.  AUTIZB
6.  PACIB
7.  PACIB1716
8.  PACIBSP
9.  PACIBZ
10. PACIZB
11. RETAA
12. RETAB
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
As suggested in
[comment](#125436 (comment)),
this PR covers subset of changes from #125436 related to the JIT.

This PR adds support for Pointer Authentication (PAC) on Arm64. Pointer
Authentication (PAC) is an Armv8.3+ security feature designed to
mitigate Return-Oriented Programming (ROP) attacks by cryptographically
signing return addresses. While using PAC, we store a signed return
address, instead of the plain address, on the stack and later
authenticate it before returning from a function. It ensures control
flow returns to the intended caller.

More details on PAC and its role in software security can be found
([here](https://llsoftsec.github.io/llsoftsecbook/#sec:pointer-authentication)).

- The current implementation of PAC is turned off by default, but can be
turned on by setting DOTNET_JitPacEnabled=1.
- PAC protects link register (LR) by signing it in the prolog (using
`paciasp`) before it is split, using the current SP as the modifier. It
then authenticates the LR in the epilog (using `autiasp`) before the
function returns. If the signature is invalid, the execution fails with
`SIGILL`.
- Changes are limited to JIT.

**NOTE**: This PR adds part of the changes for PAC support to simply
review process as discussed
[here](#125436 (comment)).
However it cannot pass the tests without the remaining parts. Kindly
take a look at the CI status of #125436 to ensure correctness.

---------

Co-authored-by: dhartglassMSFT <dhartglass+github@microsoft.com>
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
This PR covers subset of PAL-RET changes from #125436 related to the coreclr and
VM.

More details on PAC and its role in software security can be found
([here](https://llsoftsec.github.io/llsoftsecbook/#sec:pointer-authentication)).

The feature is disabled by default so CI status of this PR won't reflect
the impact of changes.
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
…ss (#129130)

Fixes dotnet/dotnet#7079

Storing FP/LR along with the unwind info. It handle a scenario if
unwinding takes place when `lr` is clobbered, as raised
[here](#125436 (comment)).


cc: @dotnet/arm64-contrib @a74nh @jkotas @dhartglassMSFT
eiriktsarpalis pushed a commit that referenced this pull request Jul 15, 2026
This PR covers the final subset of changes from #125436 related to
NativeAOT as suggested in
[comment](#125436 (comment)).
It follows the previous work from- #127949, #127838 and #128147.

More details on PAC and its role in software security can be found
([here](https://llsoftsec.github.io/llsoftsecbook/#sec:pointer-authentication)).

---------

Co-authored-by: Jan Kotas <jkotas@microsoft.com>
@github-actions

github-actions Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Workflow state for the Holistic Review Orchestrator.

{
  "version": 5,
  "last_dispatched_commit": "c45d099ada75fe190e393ed2fb12a871fe0edf75",
  "last_dispatched_base_ref": "main",
  "last_dispatched_base_sha": "1da83b362354e3c2b75cf42153bcf1438c9fb4df",
  "last_reviewed_commit": "c45d099ada75fe190e393ed2fb12a871fe0edf75",
  "last_reviewed_base_ref": "main",
  "last_reviewed_base_sha": "1da83b362354e3c2b75cf42153bcf1438c9fb4df",
  "last_recorded_worker_run_id": "29680713400",
  "review_attempt_commit": "",
  "review_attempt_base_ref": "",
  "review_attempt_count": 0,
  "max_review_attempts": 5,
  "review_history_format": "holistic-review-disclosure-v1",
  "review_history": [
    {
      "commit": "c45d099ada75fe190e393ed2fb12a871fe0edf75",
      "review_id": 4730565546
    }
  ]
}

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Holistic Review

Motivation: This PR adds Arm64 Pointer Authentication (PAC) support to mitigate ROP attacks by signing the return address (LR) in prologs (paciasp) and authenticating it in epilogs (autiasp). It contributes to #109457. This portion of the range wires the cDAC (managed data-contract reader) and diagnostics stack-walk paths to understand PAC-signed return addresses, replacing the previous NotImplementedException stubs with real PAC-stripping logic, and toggles the JitPacEnabled platform metadata flag through the runtime.

Approach: The change is well-structured and consistent. A single Arm64PtrAuthMask (0x0000FFFFFFFFFFFF) is introduced to strip the PAC bits from the top 16 bits of a return address, and a new CodePointerUtils.StripPtrAuthFromReturnAddress helper centralizes stripping with a null guard. Call sites across ARM64Unwinder, the frame handlers, FrameHelpers.GetReturnAddress, and StackWalk_1 are updated to normalize instruction pointers via CodePointerFromAddress/AddressFromCodePointer and to strip PAC from return addresses that originate from signed frames. cdacplatformmetadata.cpp now derives HasArm64PtrAuth from the JitPacEnabled CLRConfig value for all Arm64 (not just Apple), keeping the JIT and cDAC in agreement. The ARM64Unwinder threads an isReturnAddressSigned flag so PAC is only stripped when the unwind codes indicate the return address was actually signed, which is the correct, targeted behavior.

The cDAC/diagnostics changes look correct and internally consistent: the relaxed Debug.Assert((flags & ~CodePointerFlags.HasArm64PtrAuth) == 0) assertions appropriately tolerate the new flag, and the masking value matches the 48-bit VA space assumption used elsewhere. Reading-only code paths (which is what the cDAC does) can safely strip rather than authenticate.

Summary: The cDAC and diagnostics-reader changes are a solid, self-consistent implementation of PAC-aware return-address handling. My one significant concern is the default-on flip of JitPacEnabled (see inline comment on src/coreclr/jit/jitconfigvalues.h): it enables an incomplete feature by default and contradicts both the PR description and the PR's own "Disable PAC by default before merge" checklist item, with remaining ToDos (binary-size and performance analysis) still open. Assuming the default flip is unintentional (or is separately justified and the description updated), the rest of the diff looks correct. I could not build or run tests in this environment; assessment is based on reading the diff and existing CI.

Detailed Findings

  • Feature enabled by default (blocking concern): JitPacEnabled default changed from 0 to 1 in jitconfigvalues.h (and mirrored as the default in cdacplatformmetadata.cpp). See the inline comment for details. This appears to contradict the PR's stated intent.

  • Minor style nit (non-blocking): CodePointerUtils.cs leaves a stray extra blank line before the closing brace of the class after the new StripPtrAuthFromReturnAddress method. Harmless, but worth tidying.

  • Consistency (informational): The Arm64PtrAuthMask constant is duplicated in both CodePointerUtils.cs and Legacy/ConversionExtensions.cs. This mirrors the existing duplication of Arm32ThumbBit, so it is consistent with current conventions; no change required, but a shared constant could reduce drift over time.

Note

This review was generated by this repository's Holistic Review agentic workflow to complement the built-in Copilot review.

Generated by Holistic Review · 67.9 AIC · ⌖ 10.9 AIC · ⊞ 10K

CONFIG_INTEGER(JitMaxUncheckedOffset, "JitMaxUncheckedOffset", 8)
#if defined(TARGET_ARM64)
RELEASE_CONFIG_INTEGER(JitPacEnabled, "JitPacEnabled", 0)
RELEASE_CONFIG_INTEGER(JitPacEnabled, "JitPacEnabled", 1)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This flips the JitPacEnabled default from 0 to 1, enabling Pointer Authentication by default. This directly contradicts the PR description ("turned off by default") and the checked ToDo item "Disable PAC by default before merge." It also enables a still-in-progress feature whose remaining ToDos (binary-size impact, performance regression analysis) are unchecked. The matching cdacplatformmetadata.cpp change also defaults JitPacEnabled to 1, so the JIT and the cDAC agree, but both now default the feature on. If the intent is to keep PAC off by default until the feature is complete, this should remain 0 (and the cDAC config default likewise). If the default-on is intentional, please update the PR description and ToDo checklist to match.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

arch-arm64 area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI community-contribution Indicates that the PR has been added by a community member NO-MERGE The PR is not ready for merge yet (see discussion for detailed reasons)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants