[AArch64] Add error handling for unsupported bases in lowerConstantPtrAuth#189474
[AArch64] Add error handling for unsupported bases in lowerConstantPtrAuth#189474oskarwirga merged 7 commits intollvm:mainfrom
Conversation
Enable LookThroughIntToPtr on stripAndAccumulateConstantOffsets so that Swift's inttoptr(add(ptrtoint(@global), offset)) pattern inside ptrauth constants is handled correctly. Without this, the base global is not resolved and the constant is emitted as a bare integer.
|
@llvm/pr-subscribers-backend-aarch64 Author: Oskar Wirga (oskarwirga) ChangesThis is part of work being done in #188378 and #188638, split out from #188650. Swift generates This PR was mostly developed with LLM assistance, but human tested on arm64e hardware. 2 Files Affected:
diff --git a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
index 2b8db27599d3c..47281791ff7a0 100644
--- a/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
+++ b/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp
@@ -2680,9 +2680,13 @@ AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {
MCContext &Ctx = OutContext;
// Figure out the base symbol and the addend, if any.
+ // LookThroughIntToPtr handles Swift patterns like:
+ // inttoptr (i64 add (i64 ptrtoint (ptr @global to i64), i64 2) to ptr)
APInt Offset(64, 0);
const Value *BaseGV = CPA.getPointer()->stripAndAccumulateConstantOffsets(
- getDataLayout(), Offset, /*AllowNonInbounds=*/true);
+ getDataLayout(), Offset, /*AllowNonInbounds=*/true,
+ /*AllowInvariantGroup=*/false, /*ExternalAnalysis=*/nullptr,
+ /*LookThroughIntToPtr=*/true);
auto *BaseGVB = dyn_cast<GlobalValue>(BaseGV);
diff --git a/llvm/test/CodeGen/AArch64/ptrauth-reloc.ll b/llvm/test/CodeGen/AArch64/ptrauth-reloc.ll
index 14f5571fc2deb..befb726bcdab8 100644
--- a/llvm/test/CodeGen/AArch64/ptrauth-reloc.ll
+++ b/llvm/test/CodeGen/AArch64/ptrauth-reloc.ll
@@ -113,6 +113,20 @@
@g.weird_ref.da.0 = constant i64 ptrtoint (ptr inttoptr (i64 ptrtoint (ptr ptrauth (ptr getelementptr (i8, ptr @g, i64 16), i32 2) to i64) to ptr) to i64)
+; Swift generates inttoptr(add(ptrtoint(@global), offset)) inside ptrauth.
+
+; CHECK-ELF-LABEL: .globl g.inttoptr_add.da.0
+; CHECK-ELF-NEXT: .p2align 3
+; CHECK-ELF-NEXT: g.inttoptr_add.da.0:
+; CHECK-ELF-NEXT: .xword (g+2)@AUTH(da,0)
+
+; CHECK-MACHO-LABEL: .globl _g.inttoptr_add.da.0
+; CHECK-MACHO-NEXT: .p2align 3
+; CHECK-MACHO-NEXT: _g.inttoptr_add.da.0:
+; CHECK-MACHO-NEXT: .quad (_g+2)@AUTH(da,0)
+
+@g.inttoptr_add.da.0 = constant ptr ptrauth (ptr inttoptr (i64 add (i64 ptrtoint (ptr @g to i64), i64 2) to ptr), i32 2)
+
; CHECK-ELF-LABEL: .globl g_weak.ref.ia.42
; CHECK-ELF-NEXT: .p2align 3
; CHECK-ELF-NEXT: g_weak.ref.ia.42:
|
efriedma-quic
left a comment
There was a problem hiding this comment.
Please add an appropriate reportFatalUsageError() call for the case where the dyn_cast<GlobalValue>() fails. I'm concerned that we're going to find more forms of constant expressions getting miscompiled.
Enable LookThroughIntToPtr on stripAndAccumulateConstantOffsets so that Swift's inttoptr(add(ptrtoint(@global), offset)) pattern inside ptrauth constants is handled correctly. Without this, the base global is not resolved and the constant is miscompiled. Also replace the silent fallback for unresolved base values with reportFatalUsageError to catch future unsupported constant expression forms.
🐧 Linux x64 Test Results
✅ The build succeeded and all tests passed. |
🪟 Windows x64 Test Results
✅ The build succeeded and all tests passed. |
ptrauth(ptr null, ...) is legitimate — the base is a ConstantPointerNull, not a failed resolution. Only fire reportFatalUsageError when stripAndAccumulateConstantOffsets leaves an unresolved ConstantExpr. Fixes ptrauth-irelative.ll test failure.
Check for known-safe types (ConstantPointerNull) and error on everything else, rather than checking for known-bad types (ConstantExpr). This catches other unhandled forms like nested ptrauth constants. Adds test cases for null pointer ptrauth and unsupported base expression error.
How does this work in Swift then? Is there downstream change? Can you point to it? |
Swift's fork does not contain this fix. I encountered this when I've updated the PR description's wording to be more precise. |
efriedma-quic
left a comment
There was a problem hiding this comment.
LGTM, but wait a bit to see if the ptrauth reviewers have additional comments.
|
@ahmedbougacha I don't think this should be merged until you've looked at this. |
|
@oskarwirga Just being curious: could you please share a minimal swift reproducer (with compiler invocation command which you use) which results in such a pattern which needs special handling? |
|
@rjmccall would you mind looking at the mentioned swift codegen issue? Is that some idiosyncrasy we don't normally generate or is there something important we've missed in upstreaming? |
ignore me - I thought this was one of the other PRs (one of the codegen ones), this one is asm printing and I assume we just don't have existing tests |
The inttoptr(add(ptrtoint)) pattern is not Swift-specific. Drop the attribution and just describe the pattern.
After some more investigation, this was indeed a non-standard application of compiler flags/custom toolchain. I encountered this @g = external global i32
@null_signed = constant ptr ptrauth (ptr null, i32 2)
@offset_signed = constant ptr ptrauth (ptr inttoptr (i64 add (i64 ptrtoint (ptr @g to i64), i64 2) to ptr), i32 2)
I've pushed a commit to remove the Swift blame (sorry Swift!) and updated the description to suit. |
|
If we don't need it, I'd rather not set LookThroughIntToPtr to true, I think? (The other change should ensure we don't miscompile.) |
I see both sides to this. I lean towards supporting this pattern mainly because the parameter already exists and its trivial to support it but since we don't have default cases leading to this pattern we'd be widening what
|
|
According to the documentation of /// If \p LookThroughIntToPtr is true then this method also looks through
/// IntToPtr and PtrToInt constant expressions. The returned pointer may not
/// have the same provenance as this value.I'm not too experienced with all that pointer provenance stuff - on one hand, we are at the very end of the pipeline at this point, and there is no LLVM IR consumers past it. On the other hand, having pointers computed with plain int storage[10];
int * __ptrauth(2, 0, 0) result = storage + 2;the following LLVM IR is produced by @storage = dso_local global [10 x i32] zeroinitializer, align 4
@result = dso_local global ptr ptrauth (ptr getelementptr (i8, ptr @storage, i64 8), i32 2), align 8Full outputtarget datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32"
target triple = "aarch64-unknown-linux-pauthtest"
@storage = dso_local global [10 x i32] zeroinitializer, align 4
@result = dso_local global ptr ptrauth (ptr getelementptr (i8, ptr @storage, i64 8), i32 2), align 8
!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7}
!llvm.ident = !{!8}
!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"ptrauth-sign-personality", i32 1}
!2 = !{i32 1, !"aarch64-elf-pauthabi-platform", i32 268435458}
!3 = !{i32 1, !"aarch64-elf-pauthabi-version", i32 1791}
!4 = !{i32 8, !"PIC Level", i32 2}
!5 = !{i32 7, !"PIE Level", i32 2}
!6 = !{i32 7, !"uwtable", i32 2}
!7 = !{i32 7, !"frame-pointer", i32 4}
!8 = !{!"clang version 23.0.0git ([email protected]:llvm/llvm-project.git 19c862dd277b386bffb18247d53c801ae441390b)"}The assembly output is as follows (both with this PR and on the mainline): Full outputReplacing the definition of @result = dso_local global ptr ptrauth (ptr getelementptr (i8, ptr null, i64 8), i32 2), align 8changes @result = dso_local global ptr ptrauth (ptr null, i32 2), align 8turns the assembly output into |
atrosinenko
left a comment
There was a problem hiding this comment.
The patch looks mostly good to me, except that I'm not sure we should actually enable LookThroughIntToPtr "just in case" (i.e. if it is not actually emitted by Swift, for example). But explicitly checking that we finally got null base instead of just assuming "anything unknown is null" is definitely important.
Considering the description, the mainline seems not to crash on ptr null bases, but an actual issue fixed by this PR seems to be even worse, as whether we would like to support the inttoptr(add(ptrtoint)) pattern or not, we definitely should not silently miscompile anything.
Per reviewer feedback: no default frontend generates the inttoptr(add(ptrtoint)) pattern inside ConstantPtrAuth, so don't widen what lowerConstantPtrAuth accepts. The reportFatalUsageError catch-all ensures any unhandled pattern errors instead of silently miscompiling.
Sounds good to me, thanks for diving into it :) I've updated the PR + desc to drop |
…rAuth (llvm#189474) This is part of work being done in llvm#188378 and llvm#188638, split out from llvm#188650. `lowerConstantPtrAuth` silently miscompiled `ConstantPtrAuth` constants with non-`GlobalValue` pointer bases — emitting `0@AUTH(da,0)` instead of erroring. Changes: - Handle `ConstantPointerNull` bases explicitly - Error via `reportFatalUsageError` on any remaining unresolved base (e.g. nested ptrauth) instead of silently miscompiling This PR was mostly developed with LLM assistance
…rAuth (llvm#189474) This is part of work being done in llvm#188378 and llvm#188638, split out from llvm#188650. `lowerConstantPtrAuth` silently miscompiled `ConstantPtrAuth` constants with non-`GlobalValue` pointer bases — emitting `0@AUTH(da,0)` instead of erroring. Changes: - Handle `ConstantPointerNull` bases explicitly - Error via `reportFatalUsageError` on any remaining unresolved base (e.g. nested ptrauth) instead of silently miscompiling This PR was mostly developed with LLM assistance
This is part of work being done in #188378 and #188638, split out from #188650.
lowerConstantPtrAuthsilently miscompiledConstantPtrAuthconstants with non-GlobalValuepointer bases — emitting0@AUTH(da,0)instead of erroring.Changes:
ConstantPointerNullbases explicitlyreportFatalUsageErroron any remaining unresolved base (e.g. nested ptrauth) instead of silently miscompilingThis PR was mostly developed with LLM assistance