Skip to content

Commit b365cfe

Browse files
committed
[AArch64][PAC] Skip llvm.ptrauth.blend intrinsic in GVN PRE
The instruction selector on AArch64 implements a best-effort heuristic to detect the discriminator being computed by llvm.ptrauth.blend intrinsic. If such pattern is detected, then address and immediate discriminator components are emitted as two separate operands of the corresponding pseudo instruction, which is not expanded until AsmPrinter. This helps enforcing the hard-coded immediate modifier even when the address part of the discriminator can be modified by an attacker, something along the lines mov x8, x20 movk x8, #1234, #48 pacda x0, x8 // ... bl callee mov x8, x20 // address in x20 can be modified movk x8, #1234, #48 // immediate modifier is enforced pacda x0, x8 instead of reloading a previously computed discriminator value from the stack (can be modified by an attacker under Pointer Authentication threat model) or keeping it in a callee-saved register (may be spilled to the stack in callee): movk x20, #1234, #48 pacda x0, x20 // ... bl callee pacda x0, x20 // the entire discriminator can be modified
1 parent 794a84e commit b365cfe

File tree

2 files changed

+29
-31
lines changed

2 files changed

+29
-31
lines changed

llvm/lib/Transforms/Scalar/GVN.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2990,6 +2990,13 @@ bool GVNPass::performScalarPRE(Instruction *CurInst) {
29902990
if (isa<GetElementPtrInst>(CurInst))
29912991
return false;
29922992

2993+
// Don't do PRE on ptrauth_blend intrinsic: on AArch64 the instruction
2994+
// selector wants to take its operands into account when selecting the user
2995+
// of the blended discriminator, so don't hide the blend behind PHI nodes.
2996+
if (auto *II = dyn_cast<IntrinsicInst>(CurInst))
2997+
if (II->getIntrinsicID() == Intrinsic::ptrauth_blend)
2998+
return false;
2999+
29933000
if (auto *CallB = dyn_cast<CallBase>(CurInst)) {
29943001
// We don't currently value number ANY inline asm calls.
29953002
if (CallB->isInlineAsm())

llvm/test/CodeGen/AArch64/ptrauth-discriminator-components.ll

Lines changed: 22 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,7 @@ define void @test_simple(i1 %cond, ptr %storage1, ptr %storage2, i64 %a, i64 %b)
2424
; GVN-NEXT: [[ENTRY:.*:]]
2525
; GVN-NEXT: [[STORAGE1_I:%.*]] = ptrtoint ptr [[STORAGE1]] to i64
2626
; GVN-NEXT: [[STORAGE2_I:%.*]] = ptrtoint ptr [[STORAGE2]] to i64
27-
; GVN-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[ENTRY_EXIT_CRIT_EDGE:.*]]
28-
; GVN: [[ENTRY_EXIT_CRIT_EDGE]]:
29-
; GVN-NEXT: [[DOTPRE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
30-
; GVN-NEXT: [[DOTPRE1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
31-
; GVN-NEXT: br label %[[EXIT:.*]]
27+
; GVN-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[EXIT:.*]]
3228
; GVN: [[IF_THEN]]:
3329
; GVN-NEXT: [[DISCR1_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
3430
; GVN-NEXT: [[DISCR2_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
@@ -38,10 +34,10 @@ define void @test_simple(i1 %cond, ptr %storage1, ptr %storage2, i64 %a, i64 %b)
3834
; GVN-NEXT: store volatile i64 [[T2]], ptr [[STORAGE2]], align 8
3935
; GVN-NEXT: br label %[[EXIT]]
4036
; GVN: [[EXIT]]:
41-
; GVN-NEXT: [[DISCR2_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DOTPRE1]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR2_THEN]], %[[IF_THEN]] ]
42-
; GVN-NEXT: [[DISCR1_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR1_THEN]], %[[IF_THEN]] ]
43-
; GVN-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_EXIT_PRE_PHI]])
44-
; GVN-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_EXIT_PRE_PHI]])
37+
; GVN-NEXT: [[DISCR1_EXIT:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
38+
; GVN-NEXT: [[DISCR2_EXIT:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
39+
; GVN-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_EXIT]])
40+
; GVN-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_EXIT]])
4541
; GVN-NEXT: store volatile i64 [[T3]], ptr [[STORAGE1]], align 8
4642
; GVN-NEXT: store volatile i64 [[T4]], ptr [[STORAGE2]], align 8
4743
; GVN-NEXT: ret void
@@ -51,18 +47,20 @@ define void @test_simple(i1 %cond, ptr %storage1, ptr %storage2, i64 %a, i64 %b)
5147
; GVN-SCFG-NEXT: [[ENTRY:.*:]]
5248
; GVN-SCFG-NEXT: [[STORAGE1_I:%.*]] = ptrtoint ptr [[STORAGE1]] to i64
5349
; GVN-SCFG-NEXT: [[STORAGE2_I:%.*]] = ptrtoint ptr [[STORAGE2]] to i64
54-
; GVN-SCFG-NEXT: [[DISCR1_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
55-
; GVN-SCFG-NEXT: [[DISCR2_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
5650
; GVN-SCFG-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[EXIT:.*]]
5751
; GVN-SCFG: [[IF_THEN]]:
52+
; GVN-SCFG-NEXT: [[DISCR1_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
53+
; GVN-SCFG-NEXT: [[DISCR2_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
5854
; GVN-SCFG-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR1_THEN]])
5955
; GVN-SCFG-NEXT: [[T2:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR2_THEN]])
6056
; GVN-SCFG-NEXT: store volatile i64 [[T1]], ptr [[STORAGE1]], align 8
6157
; GVN-SCFG-NEXT: store volatile i64 [[T2]], ptr [[STORAGE2]], align 8
6258
; GVN-SCFG-NEXT: br label %[[EXIT]]
6359
; GVN-SCFG: [[EXIT]]:
64-
; GVN-SCFG-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_THEN]])
65-
; GVN-SCFG-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_THEN]])
60+
; GVN-SCFG-NEXT: [[DISCR1_EXIT:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
61+
; GVN-SCFG-NEXT: [[DISCR2_EXIT:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
62+
; GVN-SCFG-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_EXIT]])
63+
; GVN-SCFG-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_EXIT]])
6664
; GVN-SCFG-NEXT: store volatile i64 [[T3]], ptr [[STORAGE1]], align 8
6765
; GVN-SCFG-NEXT: store volatile i64 [[T4]], ptr [[STORAGE2]], align 8
6866
; GVN-SCFG-NEXT: ret void
@@ -100,11 +98,7 @@ define void @test_interleaved(i1 %cond, ptr %storage1, ptr %storage2, i64 %a, i6
10098
; GVN-NEXT: [[ENTRY:.*:]]
10199
; GVN-NEXT: [[STORAGE1_I:%.*]] = ptrtoint ptr [[STORAGE1]] to i64
102100
; GVN-NEXT: [[STORAGE2_I:%.*]] = ptrtoint ptr [[STORAGE2]] to i64
103-
; GVN-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[ENTRY_EXIT_CRIT_EDGE:.*]]
104-
; GVN: [[ENTRY_EXIT_CRIT_EDGE]]:
105-
; GVN-NEXT: [[DOTPRE:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
106-
; GVN-NEXT: [[DOTPRE1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
107-
; GVN-NEXT: br label %[[EXIT:.*]]
101+
; GVN-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[EXIT:.*]]
108102
; GVN: [[IF_THEN]]:
109103
; GVN-NEXT: [[DISCR1_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
110104
; GVN-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR1_THEN]])
@@ -114,10 +108,10 @@ define void @test_interleaved(i1 %cond, ptr %storage1, ptr %storage2, i64 %a, i6
114108
; GVN-NEXT: store volatile i64 [[T2]], ptr [[STORAGE2]], align 8
115109
; GVN-NEXT: br label %[[EXIT]]
116110
; GVN: [[EXIT]]:
117-
; GVN-NEXT: [[DISCR2_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DOTPRE1]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR2_THEN]], %[[IF_THEN]] ]
118-
; GVN-NEXT: [[DISCR1_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DOTPRE]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR1_THEN]], %[[IF_THEN]] ]
119-
; GVN-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_EXIT_PRE_PHI]])
120-
; GVN-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_EXIT_PRE_PHI]])
111+
; GVN-NEXT: [[DISCR1_EXIT:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
112+
; GVN-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_EXIT]])
113+
; GVN-NEXT: [[DISCR2_EXIT:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
114+
; GVN-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_EXIT]])
121115
; GVN-NEXT: store volatile i64 [[T3]], ptr [[STORAGE1]], align 8
122116
; GVN-NEXT: store volatile i64 [[T4]], ptr [[STORAGE2]], align 8
123117
; GVN-NEXT: ret void
@@ -127,23 +121,20 @@ define void @test_interleaved(i1 %cond, ptr %storage1, ptr %storage2, i64 %a, i6
127121
; GVN-SCFG-NEXT: [[ENTRY:.*:]]
128122
; GVN-SCFG-NEXT: [[STORAGE1_I:%.*]] = ptrtoint ptr [[STORAGE1]] to i64
129123
; GVN-SCFG-NEXT: [[STORAGE2_I:%.*]] = ptrtoint ptr [[STORAGE2]] to i64
130-
; GVN-SCFG-NEXT: [[DISCR1_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
131-
; GVN-SCFG-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[ENTRY_EXIT_CRIT_EDGE:.*]]
132-
; GVN-SCFG: [[ENTRY_EXIT_CRIT_EDGE]]:
133-
; GVN-SCFG-NEXT: [[DOTPRE1:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
134-
; GVN-SCFG-NEXT: br label %[[EXIT:.*]]
124+
; GVN-SCFG-NEXT: br i1 [[COND]], label %[[IF_THEN:.*]], label %[[EXIT:.*]]
135125
; GVN-SCFG: [[IF_THEN]]:
126+
; GVN-SCFG-NEXT: [[DISCR1_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
136127
; GVN-SCFG-NEXT: [[T1:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR1_THEN]])
137128
; GVN-SCFG-NEXT: [[DISCR2_THEN:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
138129
; GVN-SCFG-NEXT: [[T2:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[A]], i32 2, i64 [[DISCR2_THEN]])
139130
; GVN-SCFG-NEXT: store volatile i64 [[T1]], ptr [[STORAGE1]], align 8
140131
; GVN-SCFG-NEXT: store volatile i64 [[T2]], ptr [[STORAGE2]], align 8
141132
; GVN-SCFG-NEXT: br label %[[EXIT]]
142133
; GVN-SCFG: [[EXIT]]:
143-
; GVN-SCFG-NEXT: [[DISCR2_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DOTPRE1]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR2_THEN]], %[[IF_THEN]] ]
144-
; GVN-SCFG-NEXT: [[DISCR1_EXIT_PRE_PHI:%.*]] = phi i64 [ [[DISCR1_THEN]], %[[ENTRY_EXIT_CRIT_EDGE]] ], [ [[DISCR1_THEN]], %[[IF_THEN]] ]
145-
; GVN-SCFG-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_EXIT_PRE_PHI]])
146-
; GVN-SCFG-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_EXIT_PRE_PHI]])
134+
; GVN-SCFG-NEXT: [[DISCR1_EXIT:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE1_I]], i64 42)
135+
; GVN-SCFG-NEXT: [[T3:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR1_EXIT]])
136+
; GVN-SCFG-NEXT: [[DISCR2_EXIT:%.*]] = call i64 @llvm.ptrauth.blend(i64 [[STORAGE2_I]], i64 42)
137+
; GVN-SCFG-NEXT: [[T4:%.*]] = call i64 @llvm.ptrauth.sign(i64 [[B]], i32 2, i64 [[DISCR2_EXIT]])
147138
; GVN-SCFG-NEXT: store volatile i64 [[T3]], ptr [[STORAGE1]], align 8
148139
; GVN-SCFG-NEXT: store volatile i64 [[T4]], ptr [[STORAGE2]], align 8
149140
; GVN-SCFG-NEXT: ret void

0 commit comments

Comments
 (0)