Skip to content

Arm64: [PAC-RET] Add Pointer Authentication support for Arm64 (Part 1/3): JIT changes#127838

Open
SwapnilGaikwad wants to merge 1 commit intodotnet:mainfrom
SwapnilGaikwad:github-jit-pac
Open

Arm64: [PAC-RET] Add Pointer Authentication support for Arm64 (Part 1/3): JIT changes#127838
SwapnilGaikwad wants to merge 1 commit intodotnet:mainfrom
SwapnilGaikwad:github-jit-pac

Conversation

@SwapnilGaikwad
Copy link
Copy Markdown
Contributor

@SwapnilGaikwad SwapnilGaikwad commented May 5, 2026

As suggested in 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).

  • 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. However it cannot pass the tests without the remaining parts. Kindly take a look at the CI status of #125436 to ensure correctness.

…/3: JIT changes)

As suggested in [comment](dotnet#125436 (comment)), this PR covers subset of changes from dotnet#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.
@github-actions github-actions Bot added the area-CodeGen-coreclr CLR JIT compiler in src/coreclr/src/jit and related components such as SuperPMI label May 5, 2026
@dotnet-policy-service dotnet-policy-service Bot added the community-contribution Indicates that the PR has been added by a community member label May 5, 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.

}
#endif // FEATURE_CFI_SUPPORT

// pac_sign_lr: 11111100: sign the return address in lr with paciasp
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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Are the Windows vs. Unix conventions for signing key different?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

AFAIK, Windows uses B key while the rest use A key for signing. The execption handling documentation and this dev blog on suggest the same.
This could potentially be a problem if an unwinder assumes the use of key. Jan mentioned here, Windows OS unwinder is used on Windows for execution. It might assume use of B key in that case. I'm not sure why it's not being flagged up by any Windows CI workflows.
Would you recommend switching to B key on Windows?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Yes, I would recommend following platform conventions.

Also, this is worth mentioning in https://github.com/dotnet/runtime/blob/main/docs/design/coreclr/botr/clr-abi.md

Copy link
Copy Markdown
Contributor

@dhartglassMSFT dhartglassMSFT May 6, 2026

Choose a reason for hiding this comment

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

Thanks for the link, yeah Swapnil and I talked offline he's going to try to switch to B-key signing for windows

also for reference here's msvc emitting sign/auth instructions if it matters
https://godbolt.org/z/dMdo7sqod

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Thank you both. I'll update to use B keys on Windows. It makes more sense and is consistent.
I'll update the original PR (#125436) first with B key changes to ensure everything passes on Windows. Then I'll port these changes here. We don't have encodings for B key instructions. I'll put them in the original PR for testing but 'll open a separate PR just to add encodings.

// Tier0 signed LR with the Tier0 caller SP before allocating its frame.
// Recreate that SP from the current Tier0 body SP so we can authenticate
// LR before the OSR prolog later re-signs it with the OSR SP via PACIASP.
genInstrWithConstant(INS_add, EA_PTRSIZE, REG_IP0, REG_SPBASE, patchpointInfo->TotalFrameSize(), REG_IP0,
Copy link
Copy Markdown
Contributor

@dhartglassMSFT dhartglassMSFT May 6, 2026

Choose a reason for hiding this comment

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

@AndyAyersMS FYI if we want a second set of eyes on the OSR change here

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.

nit - anyway if you're in here again Swapnil I'd change "authenticate LR" to "authenticate and strip LR" even though the latter is still implied, reads a little clearer to me

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks good to me.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

If I understand correctly the tier0 code will have signed LR with the SP as it is on entry, while the OSR method will now sign LR with the value of SP after the tier0 adjustment.

Would it make more sense to just skip signing in the OSR method and then authenticate after both SP adjustments have been done in the epilog?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It might be possible to avoid authenticating and re-signing with the help of phantom unwind code representing stack allocation. However, it deviates from the simple model to use signing SP from current frame. I'm happy to explore this possibility in the follow-up PR as the current one is getting trickier to maintain 🙂

Copy link
Copy Markdown
Member

@jakobbotsch jakobbotsch May 7, 2026

Choose a reason for hiding this comment

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

The OSR function inherits the frame from the tier0 function, it is part of its frame. What we have now effectively is making OSR functions deviate from all other functions since it is signing with an SP that points into the middle of its frame. I am ok with it, but you may want to make sure the OSR case is well tested.
Also we may want to eventually do some perf testing for runtime async since these prologs can be very hot for runtime async.

help of phantom unwind code representing stack allocation

I think we would emit a phantom pac_sign_lr code, before we emitted the phantom SP adjustment.

@SwapnilGaikwad SwapnilGaikwad changed the title Arm64: [PAC-RET] Add Pointer Authentication support for Arm64 (Part 1… Arm64: [PAC-RET] Add Pointer Authentication support for Arm64 (Part 1/3): JIT changes May 8, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants