1 | //===--- CGPointerAuth.cpp - IR generation for pointer authentication -----===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file contains common routines relating to the emission of |
10 | // pointer authentication operations. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "CodeGenFunction.h" |
15 | #include "CodeGenModule.h" |
16 | #include "clang/CodeGen/CodeGenABITypes.h" |
17 | #include "clang/CodeGen/ConstantInitBuilder.h" |
18 | #include "llvm/Analysis/ValueTracking.h" |
19 | #include "llvm/Support/SipHash.h" |
20 | |
21 | using namespace clang; |
22 | using namespace CodeGen; |
23 | |
24 | /// Given a pointer-authentication schema, return a concrete "other" |
25 | /// discriminator for it. |
26 | llvm::ConstantInt *CodeGenModule::getPointerAuthOtherDiscriminator( |
27 | const PointerAuthSchema &Schema, GlobalDecl Decl, QualType Type) { |
28 | switch (Schema.getOtherDiscrimination()) { |
29 | case PointerAuthSchema::Discrimination::None: |
30 | return nullptr; |
31 | |
32 | case PointerAuthSchema::Discrimination::Type: |
33 | assert(!Type.isNull() && "type not provided for type-discriminated schema" ); |
34 | return llvm::ConstantInt::get( |
35 | Ty: IntPtrTy, V: getContext().getPointerAuthTypeDiscriminator(T: Type)); |
36 | |
37 | case PointerAuthSchema::Discrimination::Decl: |
38 | assert(Decl.getDecl() && |
39 | "declaration not provided for decl-discriminated schema" ); |
40 | return llvm::ConstantInt::get(Ty: IntPtrTy, |
41 | V: getPointerAuthDeclDiscriminator(GD: Decl)); |
42 | |
43 | case PointerAuthSchema::Discrimination::Constant: |
44 | return llvm::ConstantInt::get(Ty: IntPtrTy, V: Schema.getConstantDiscrimination()); |
45 | } |
46 | llvm_unreachable("bad discrimination kind" ); |
47 | } |
48 | |
49 | uint16_t CodeGen::getPointerAuthTypeDiscriminator(CodeGenModule &CGM, |
50 | QualType FunctionType) { |
51 | return CGM.getContext().getPointerAuthTypeDiscriminator(T: FunctionType); |
52 | } |
53 | |
54 | uint16_t CodeGen::getPointerAuthDeclDiscriminator(CodeGenModule &CGM, |
55 | GlobalDecl Declaration) { |
56 | return CGM.getPointerAuthDeclDiscriminator(GD: Declaration); |
57 | } |
58 | |
59 | /// Return the "other" decl-specific discriminator for the given decl. |
60 | uint16_t |
61 | CodeGenModule::getPointerAuthDeclDiscriminator(GlobalDecl Declaration) { |
62 | uint16_t &EntityHash = PtrAuthDiscriminatorHashes[Declaration]; |
63 | |
64 | if (EntityHash == 0) { |
65 | StringRef Name = getMangledName(GD: Declaration); |
66 | EntityHash = llvm::getPointerAuthStableSipHash(S: Name); |
67 | } |
68 | |
69 | return EntityHash; |
70 | } |
71 | |
72 | /// Return the abstract pointer authentication schema for a pointer to the given |
73 | /// function type. |
74 | CGPointerAuthInfo CodeGenModule::getFunctionPointerAuthInfo(QualType T) { |
75 | const auto &Schema = getCodeGenOpts().PointerAuth.FunctionPointers; |
76 | if (!Schema) |
77 | return CGPointerAuthInfo(); |
78 | |
79 | assert(!Schema.isAddressDiscriminated() && |
80 | "function pointers cannot use address-specific discrimination" ); |
81 | |
82 | llvm::Constant *Discriminator = nullptr; |
83 | if (T->isFunctionPointerType() || T->isFunctionReferenceType()) |
84 | T = T->getPointeeType(); |
85 | if (T->isFunctionType()) |
86 | Discriminator = getPointerAuthOtherDiscriminator(Schema, Decl: GlobalDecl(), Type: T); |
87 | |
88 | return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(), |
89 | /*IsaPointer=*/false, /*AuthenticatesNull=*/false, |
90 | Discriminator); |
91 | } |
92 | |
93 | llvm::Value * |
94 | CodeGenFunction::EmitPointerAuthBlendDiscriminator(llvm::Value *StorageAddress, |
95 | llvm::Value *Discriminator) { |
96 | StorageAddress = Builder.CreatePtrToInt(V: StorageAddress, DestTy: IntPtrTy); |
97 | auto Intrinsic = CGM.getIntrinsic(IID: llvm::Intrinsic::ptrauth_blend); |
98 | return Builder.CreateCall(Callee: Intrinsic, Args: {StorageAddress, Discriminator}); |
99 | } |
100 | |
101 | /// Emit the concrete pointer authentication informaton for the |
102 | /// given authentication schema. |
103 | CGPointerAuthInfo CodeGenFunction::EmitPointerAuthInfo( |
104 | const PointerAuthSchema &Schema, llvm::Value *StorageAddress, |
105 | GlobalDecl SchemaDecl, QualType SchemaType) { |
106 | if (!Schema) |
107 | return CGPointerAuthInfo(); |
108 | |
109 | llvm::Value *Discriminator = |
110 | CGM.getPointerAuthOtherDiscriminator(Schema, Decl: SchemaDecl, Type: SchemaType); |
111 | |
112 | if (Schema.isAddressDiscriminated()) { |
113 | assert(StorageAddress && |
114 | "address not provided for address-discriminated schema" ); |
115 | |
116 | if (Discriminator) |
117 | Discriminator = |
118 | EmitPointerAuthBlendDiscriminator(StorageAddress, Discriminator); |
119 | else |
120 | Discriminator = Builder.CreatePtrToInt(V: StorageAddress, DestTy: IntPtrTy); |
121 | } |
122 | |
123 | return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(), |
124 | Schema.isIsaPointer(), |
125 | Schema.authenticatesNullValues(), Discriminator); |
126 | } |
127 | |
128 | CGPointerAuthInfo |
129 | CodeGenFunction::EmitPointerAuthInfo(PointerAuthQualifier Qual, |
130 | Address StorageAddress) { |
131 | assert(Qual && "don't call this if you don't know that the Qual is present" ); |
132 | if (Qual.hasKeyNone()) |
133 | return CGPointerAuthInfo(); |
134 | |
135 | llvm::Value *Discriminator = nullptr; |
136 | if (unsigned = Qual.getExtraDiscriminator()) |
137 | Discriminator = llvm::ConstantInt::get(Ty: IntPtrTy, V: Extra); |
138 | |
139 | if (Qual.isAddressDiscriminated()) { |
140 | assert(StorageAddress.isValid() && |
141 | "address discrimination without address" ); |
142 | llvm::Value *StoragePtr = StorageAddress.emitRawPointer(CGF&: *this); |
143 | if (Discriminator) |
144 | Discriminator = |
145 | EmitPointerAuthBlendDiscriminator(StorageAddress: StoragePtr, Discriminator); |
146 | else |
147 | Discriminator = Builder.CreatePtrToInt(V: StoragePtr, DestTy: IntPtrTy); |
148 | } |
149 | |
150 | return CGPointerAuthInfo(Qual.getKey(), Qual.getAuthenticationMode(), |
151 | Qual.isIsaPointer(), Qual.authenticatesNullValues(), |
152 | Discriminator); |
153 | } |
154 | |
155 | /// Return the natural pointer authentication for values of the given |
156 | /// pointee type. |
157 | static CGPointerAuthInfo |
158 | getPointerAuthInfoForPointeeType(CodeGenModule &CGM, QualType PointeeType) { |
159 | if (PointeeType.isNull()) |
160 | return CGPointerAuthInfo(); |
161 | |
162 | // Function pointers use the function-pointer schema by default. |
163 | if (PointeeType->isFunctionType()) |
164 | return CGM.getFunctionPointerAuthInfo(T: PointeeType); |
165 | |
166 | // Normal data pointers never use direct pointer authentication by default. |
167 | return CGPointerAuthInfo(); |
168 | } |
169 | |
170 | CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForPointeeType(QualType T) { |
171 | return ::getPointerAuthInfoForPointeeType(CGM&: *this, PointeeType: T); |
172 | } |
173 | |
174 | /// Return the natural pointer authentication for values of the given |
175 | /// pointer type. |
176 | static CGPointerAuthInfo getPointerAuthInfoForType(CodeGenModule &CGM, |
177 | QualType PointerType) { |
178 | assert(PointerType->isSignableType(CGM.getContext())); |
179 | |
180 | // Block pointers are currently not signed. |
181 | if (PointerType->isBlockPointerType()) |
182 | return CGPointerAuthInfo(); |
183 | |
184 | auto PointeeType = PointerType->getPointeeType(); |
185 | |
186 | if (PointeeType.isNull()) |
187 | return CGPointerAuthInfo(); |
188 | |
189 | return ::getPointerAuthInfoForPointeeType(CGM, PointeeType); |
190 | } |
191 | |
192 | CGPointerAuthInfo CodeGenModule::getPointerAuthInfoForType(QualType T) { |
193 | return ::getPointerAuthInfoForType(CGM&: *this, PointerType: T); |
194 | } |
195 | |
196 | static std::pair<llvm::Value *, CGPointerAuthInfo> |
197 | emitLoadOfOrigPointerRValue(CodeGenFunction &CGF, const LValue &LV, |
198 | SourceLocation Loc) { |
199 | llvm::Value *Value = CGF.EmitLoadOfScalar(lvalue: LV, Loc); |
200 | CGPointerAuthInfo AuthInfo; |
201 | if (PointerAuthQualifier PtrAuth = LV.getQuals().getPointerAuth()) |
202 | AuthInfo = CGF.EmitPointerAuthInfo(Qual: PtrAuth, StorageAddress: LV.getAddress()); |
203 | else |
204 | AuthInfo = getPointerAuthInfoForType(CGM&: CGF.CGM, PointerType: LV.getType()); |
205 | return {Value, AuthInfo}; |
206 | } |
207 | |
208 | /// Retrieve a pointer rvalue and its ptrauth info. When possible, avoid |
209 | /// needlessly resigning the pointer. |
210 | std::pair<llvm::Value *, CGPointerAuthInfo> |
211 | CodeGenFunction::EmitOrigPointerRValue(const Expr *E) { |
212 | assert(E->getType()->isSignableType(getContext())); |
213 | |
214 | E = E->IgnoreParens(); |
215 | if (const auto *Load = dyn_cast<ImplicitCastExpr>(Val: E)) { |
216 | if (Load->getCastKind() == CK_LValueToRValue) { |
217 | E = Load->getSubExpr()->IgnoreParens(); |
218 | |
219 | // We're semantically required to not emit loads of certain DREs naively. |
220 | if (const auto *RefExpr = dyn_cast<DeclRefExpr>(Val: E)) { |
221 | if (ConstantEmission Result = tryEmitAsConstant(RefExpr)) { |
222 | // Fold away a use of an intermediate variable. |
223 | if (!Result.isReference()) |
224 | return {Result.getValue(), |
225 | getPointerAuthInfoForType(CGM, PointerType: RefExpr->getType())}; |
226 | |
227 | // Fold away a use of an intermediate reference. |
228 | LValue LV = Result.getReferenceLValue(CGF&: *this, RefExpr); |
229 | return emitLoadOfOrigPointerRValue(CGF&: *this, LV, Loc: RefExpr->getLocation()); |
230 | } |
231 | } |
232 | |
233 | // Otherwise, load and use the pointer |
234 | LValue LV = EmitCheckedLValue(E, TCK: CodeGenFunction::TCK_Load); |
235 | return emitLoadOfOrigPointerRValue(CGF&: *this, LV, Loc: E->getExprLoc()); |
236 | } |
237 | } |
238 | |
239 | // Fallback: just use the normal rules for the type. |
240 | llvm::Value *Value = EmitScalarExpr(E); |
241 | return {Value, getPointerAuthInfoForType(CGM, PointerType: E->getType())}; |
242 | } |
243 | |
244 | llvm::Value * |
245 | CodeGenFunction::EmitPointerAuthQualify(PointerAuthQualifier DestQualifier, |
246 | const Expr *E, |
247 | Address DestStorageAddress) { |
248 | assert(DestQualifier); |
249 | auto [Value, CurAuthInfo] = EmitOrigPointerRValue(E); |
250 | |
251 | CGPointerAuthInfo DestAuthInfo = |
252 | EmitPointerAuthInfo(Qual: DestQualifier, StorageAddress: DestStorageAddress); |
253 | return emitPointerAuthResign(Pointer: Value, PointerType: E->getType(), CurAuthInfo, NewAuthInfo: DestAuthInfo, |
254 | IsKnownNonNull: isPointerKnownNonNull(E)); |
255 | } |
256 | |
257 | llvm::Value *CodeGenFunction::EmitPointerAuthQualify( |
258 | PointerAuthQualifier DestQualifier, llvm::Value *Value, |
259 | QualType PointerType, Address DestStorageAddress, bool IsKnownNonNull) { |
260 | assert(DestQualifier); |
261 | |
262 | CGPointerAuthInfo CurAuthInfo = getPointerAuthInfoForType(CGM, PointerType); |
263 | CGPointerAuthInfo DestAuthInfo = |
264 | EmitPointerAuthInfo(Qual: DestQualifier, StorageAddress: DestStorageAddress); |
265 | return emitPointerAuthResign(Pointer: Value, PointerType, CurAuthInfo, NewAuthInfo: DestAuthInfo, |
266 | IsKnownNonNull); |
267 | } |
268 | |
269 | llvm::Value *CodeGenFunction::EmitPointerAuthUnqualify( |
270 | PointerAuthQualifier CurQualifier, llvm::Value *Value, QualType PointerType, |
271 | Address CurStorageAddress, bool IsKnownNonNull) { |
272 | assert(CurQualifier); |
273 | |
274 | CGPointerAuthInfo CurAuthInfo = |
275 | EmitPointerAuthInfo(Qual: CurQualifier, StorageAddress: CurStorageAddress); |
276 | CGPointerAuthInfo DestAuthInfo = getPointerAuthInfoForType(CGM, PointerType); |
277 | return emitPointerAuthResign(Pointer: Value, PointerType, CurAuthInfo, NewAuthInfo: DestAuthInfo, |
278 | IsKnownNonNull); |
279 | } |
280 | |
281 | static bool isZeroConstant(const llvm::Value *Value) { |
282 | if (const auto *CI = dyn_cast<llvm::ConstantInt>(Val: Value)) |
283 | return CI->isZero(); |
284 | return false; |
285 | } |
286 | |
287 | static bool equalAuthPolicies(const CGPointerAuthInfo &Left, |
288 | const CGPointerAuthInfo &Right) { |
289 | assert((Left.isSigned() || Right.isSigned()) && |
290 | "shouldn't be called if neither is signed" ); |
291 | if (Left.isSigned() != Right.isSigned()) |
292 | return false; |
293 | return Left.getKey() == Right.getKey() && |
294 | Left.getAuthenticationMode() == Right.getAuthenticationMode() && |
295 | Left.isIsaPointer() == Right.isIsaPointer() && |
296 | Left.authenticatesNullValues() == Right.authenticatesNullValues() && |
297 | Left.getDiscriminator() == Right.getDiscriminator(); |
298 | } |
299 | |
300 | // Return the discriminator or return zero if the discriminator is null. |
301 | static llvm::Value *getDiscriminatorOrZero(const CGPointerAuthInfo &Info, |
302 | CGBuilderTy &Builder) { |
303 | llvm::Value *Discriminator = Info.getDiscriminator(); |
304 | return Discriminator ? Discriminator : Builder.getSize(N: 0); |
305 | } |
306 | |
307 | llvm::Value * |
308 | CodeGenFunction::emitPointerAuthResignCall(llvm::Value *Value, |
309 | const CGPointerAuthInfo &CurAuth, |
310 | const CGPointerAuthInfo &NewAuth) { |
311 | assert(CurAuth && NewAuth); |
312 | |
313 | if (CurAuth.getAuthenticationMode() != |
314 | PointerAuthenticationMode::SignAndAuth || |
315 | NewAuth.getAuthenticationMode() != |
316 | PointerAuthenticationMode::SignAndAuth) { |
317 | llvm::Value *AuthedValue = EmitPointerAuthAuth(Info: CurAuth, Pointer: Value); |
318 | return EmitPointerAuthSign(Info: NewAuth, Pointer: AuthedValue); |
319 | } |
320 | // Convert the pointer to intptr_t before signing it. |
321 | auto *OrigType = Value->getType(); |
322 | Value = Builder.CreatePtrToInt(V: Value, DestTy: IntPtrTy); |
323 | |
324 | auto *CurKey = Builder.getInt32(C: CurAuth.getKey()); |
325 | auto *NewKey = Builder.getInt32(C: NewAuth.getKey()); |
326 | |
327 | llvm::Value *CurDiscriminator = getDiscriminatorOrZero(Info: CurAuth, Builder); |
328 | llvm::Value *NewDiscriminator = getDiscriminatorOrZero(Info: NewAuth, Builder); |
329 | |
330 | // call i64 @llvm.ptrauth.resign(i64 %pointer, |
331 | // i32 %curKey, i64 %curDiscriminator, |
332 | // i32 %newKey, i64 %newDiscriminator) |
333 | auto *Intrinsic = CGM.getIntrinsic(IID: llvm::Intrinsic::ptrauth_resign); |
334 | Value = EmitRuntimeCall( |
335 | callee: Intrinsic, args: {Value, CurKey, CurDiscriminator, NewKey, NewDiscriminator}); |
336 | |
337 | // Convert back to the original type. |
338 | Value = Builder.CreateIntToPtr(V: Value, DestTy: OrigType); |
339 | return Value; |
340 | } |
341 | |
342 | llvm::Value *CodeGenFunction::emitPointerAuthResign( |
343 | llvm::Value *Value, QualType Type, const CGPointerAuthInfo &CurAuthInfo, |
344 | const CGPointerAuthInfo &NewAuthInfo, bool IsKnownNonNull) { |
345 | // Fast path: if neither schema wants a signature, we're done. |
346 | if (!CurAuthInfo && !NewAuthInfo) |
347 | return Value; |
348 | |
349 | llvm::Value *Null = nullptr; |
350 | // If the value is obviously null, we're done. |
351 | if (auto *PointerValue = dyn_cast<llvm::PointerType>(Val: Value->getType())) { |
352 | Null = CGM.getNullPointer(T: PointerValue, QT: Type); |
353 | } else { |
354 | assert(Value->getType()->isIntegerTy()); |
355 | Null = llvm::ConstantInt::get(Ty: IntPtrTy, V: 0); |
356 | } |
357 | if (Value == Null) |
358 | return Value; |
359 | |
360 | // If both schemas sign the same way, we're done. |
361 | if (equalAuthPolicies(Left: CurAuthInfo, Right: NewAuthInfo)) { |
362 | const llvm::Value *CurD = CurAuthInfo.getDiscriminator(); |
363 | const llvm::Value *NewD = NewAuthInfo.getDiscriminator(); |
364 | if (CurD == NewD) |
365 | return Value; |
366 | |
367 | if ((CurD == nullptr && isZeroConstant(Value: NewD)) || |
368 | (NewD == nullptr && isZeroConstant(Value: CurD))) |
369 | return Value; |
370 | } |
371 | |
372 | llvm::BasicBlock *InitBB = Builder.GetInsertBlock(); |
373 | llvm::BasicBlock *ResignBB = nullptr, *ContBB = nullptr; |
374 | |
375 | // Null pointers have to be mapped to null, and the ptrauth_resign |
376 | // intrinsic doesn't do that. |
377 | if (!IsKnownNonNull && !llvm::isKnownNonZero(V: Value, Q: CGM.getDataLayout())) { |
378 | ContBB = createBasicBlock(name: "resign.cont" ); |
379 | ResignBB = createBasicBlock(name: "resign.nonnull" ); |
380 | |
381 | auto *IsNonNull = Builder.CreateICmpNE(LHS: Value, RHS: Null); |
382 | Builder.CreateCondBr(Cond: IsNonNull, True: ResignBB, False: ContBB); |
383 | EmitBlock(BB: ResignBB); |
384 | } |
385 | |
386 | // Perform the auth/sign/resign operation. |
387 | if (!NewAuthInfo) |
388 | Value = EmitPointerAuthAuth(Info: CurAuthInfo, Pointer: Value); |
389 | else if (!CurAuthInfo) |
390 | Value = EmitPointerAuthSign(Info: NewAuthInfo, Pointer: Value); |
391 | else |
392 | Value = emitPointerAuthResignCall(Value, CurAuth: CurAuthInfo, NewAuth: NewAuthInfo); |
393 | |
394 | // Clean up with a phi if we branched before. |
395 | if (ContBB) { |
396 | EmitBlock(BB: ContBB); |
397 | auto *Phi = Builder.CreatePHI(Ty: Value->getType(), NumReservedValues: 2); |
398 | Phi->addIncoming(V: Null, BB: InitBB); |
399 | Phi->addIncoming(V: Value, BB: ResignBB); |
400 | Value = Phi; |
401 | } |
402 | |
403 | return Value; |
404 | } |
405 | |
406 | void CodeGenFunction::EmitPointerAuthCopy(PointerAuthQualifier Qual, QualType T, |
407 | Address DestAddress, |
408 | Address SrcAddress) { |
409 | assert(Qual); |
410 | llvm::Value *Value = Builder.CreateLoad(Addr: SrcAddress); |
411 | |
412 | // If we're using address-discrimination, we have to re-sign the value. |
413 | if (Qual.isAddressDiscriminated()) { |
414 | CGPointerAuthInfo SrcPtrAuth = EmitPointerAuthInfo(Qual, StorageAddress: SrcAddress); |
415 | CGPointerAuthInfo DestPtrAuth = EmitPointerAuthInfo(Qual, StorageAddress: DestAddress); |
416 | Value = emitPointerAuthResign(Value, Type: T, CurAuthInfo: SrcPtrAuth, NewAuthInfo: DestPtrAuth, |
417 | /*IsKnownNonNull=*/false); |
418 | } |
419 | |
420 | Builder.CreateStore(Val: Value, Addr: DestAddress); |
421 | } |
422 | |
423 | llvm::Constant * |
424 | CodeGenModule::getConstantSignedPointer(llvm::Constant *Pointer, unsigned Key, |
425 | llvm::Constant *StorageAddress, |
426 | llvm::ConstantInt *OtherDiscriminator) { |
427 | llvm::Constant *AddressDiscriminator; |
428 | if (StorageAddress) { |
429 | assert(StorageAddress->getType() == UnqualPtrTy); |
430 | AddressDiscriminator = StorageAddress; |
431 | } else { |
432 | AddressDiscriminator = llvm::Constant::getNullValue(Ty: UnqualPtrTy); |
433 | } |
434 | |
435 | llvm::ConstantInt *IntegerDiscriminator; |
436 | if (OtherDiscriminator) { |
437 | assert(OtherDiscriminator->getType() == Int64Ty); |
438 | IntegerDiscriminator = OtherDiscriminator; |
439 | } else { |
440 | IntegerDiscriminator = llvm::ConstantInt::get(Ty: Int64Ty, V: 0); |
441 | } |
442 | |
443 | return llvm::ConstantPtrAuth::get(Ptr: Pointer, |
444 | Key: llvm::ConstantInt::get(Ty: Int32Ty, V: Key), |
445 | Disc: IntegerDiscriminator, AddrDisc: AddressDiscriminator); |
446 | } |
447 | |
448 | /// Does a given PointerAuthScheme require us to sign a value |
449 | bool CodeGenModule::shouldSignPointer(const PointerAuthSchema &Schema) { |
450 | auto AuthenticationMode = Schema.getAuthenticationMode(); |
451 | return AuthenticationMode == PointerAuthenticationMode::SignAndStrip || |
452 | AuthenticationMode == PointerAuthenticationMode::SignAndAuth; |
453 | } |
454 | |
455 | /// Sign a constant pointer using the given scheme, producing a constant |
456 | /// with the same IR type. |
457 | llvm::Constant *CodeGenModule::getConstantSignedPointer( |
458 | llvm::Constant *Pointer, const PointerAuthSchema &Schema, |
459 | llvm::Constant *StorageAddress, GlobalDecl SchemaDecl, |
460 | QualType SchemaType) { |
461 | assert(shouldSignPointer(Schema)); |
462 | llvm::ConstantInt *OtherDiscriminator = |
463 | getPointerAuthOtherDiscriminator(Schema, Decl: SchemaDecl, Type: SchemaType); |
464 | |
465 | return getConstantSignedPointer(Pointer, Key: Schema.getKey(), StorageAddress, |
466 | OtherDiscriminator); |
467 | } |
468 | |
469 | /// If applicable, sign a given constant function pointer with the ABI rules for |
470 | /// functionType. |
471 | llvm::Constant *CodeGenModule::getFunctionPointer(llvm::Constant *Pointer, |
472 | QualType FunctionType) { |
473 | assert(FunctionType->isFunctionType() || |
474 | FunctionType->isFunctionReferenceType() || |
475 | FunctionType->isFunctionPointerType()); |
476 | |
477 | if (auto PointerAuth = getFunctionPointerAuthInfo(T: FunctionType)) |
478 | return getConstantSignedPointer( |
479 | Pointer, Key: PointerAuth.getKey(), /*StorageAddress=*/nullptr, |
480 | OtherDiscriminator: cast_or_null<llvm::ConstantInt>(Val: PointerAuth.getDiscriminator())); |
481 | |
482 | return Pointer; |
483 | } |
484 | |
485 | llvm::Constant *CodeGenModule::getFunctionPointer(GlobalDecl GD, |
486 | llvm::Type *Ty) { |
487 | const auto *FD = cast<FunctionDecl>(Val: GD.getDecl()); |
488 | QualType FuncType = FD->getType(); |
489 | |
490 | // Annoyingly, K&R functions have prototypes in the clang AST, but |
491 | // expressions referring to them are unprototyped. |
492 | if (!FD->hasPrototype()) |
493 | if (const auto *Proto = FuncType->getAs<FunctionProtoType>()) |
494 | FuncType = Context.getFunctionNoProtoType(ResultTy: Proto->getReturnType(), |
495 | Info: Proto->getExtInfo()); |
496 | |
497 | return getFunctionPointer(Pointer: getRawFunctionPointer(GD, Ty), FunctionType: FuncType); |
498 | } |
499 | |
500 | CGPointerAuthInfo CodeGenModule::getMemberFunctionPointerAuthInfo(QualType FT) { |
501 | assert(FT->getAs<MemberPointerType>() && "MemberPointerType expected" ); |
502 | const auto &Schema = getCodeGenOpts().PointerAuth.CXXMemberFunctionPointers; |
503 | if (!Schema) |
504 | return CGPointerAuthInfo(); |
505 | |
506 | assert(!Schema.isAddressDiscriminated() && |
507 | "function pointers cannot use address-specific discrimination" ); |
508 | |
509 | llvm::ConstantInt *Discriminator = |
510 | getPointerAuthOtherDiscriminator(Schema, Decl: GlobalDecl(), Type: FT); |
511 | return CGPointerAuthInfo(Schema.getKey(), Schema.getAuthenticationMode(), |
512 | /* IsIsaPointer */ false, |
513 | /* AuthenticatesNullValues */ false, Discriminator); |
514 | } |
515 | |
516 | llvm::Constant *CodeGenModule::getMemberFunctionPointer(llvm::Constant *Pointer, |
517 | QualType FT) { |
518 | if (CGPointerAuthInfo PointerAuth = getMemberFunctionPointerAuthInfo(FT)) |
519 | return getConstantSignedPointer( |
520 | Pointer, Key: PointerAuth.getKey(), StorageAddress: nullptr, |
521 | OtherDiscriminator: cast_or_null<llvm::ConstantInt>(Val: PointerAuth.getDiscriminator())); |
522 | |
523 | if (const auto *MFT = dyn_cast<MemberPointerType>(Val: FT.getTypePtr())) { |
524 | if (MFT->hasPointeeToToCFIUncheckedCalleeFunctionType()) |
525 | Pointer = llvm::NoCFIValue::get(GV: cast<llvm::GlobalValue>(Val: Pointer)); |
526 | } |
527 | |
528 | return Pointer; |
529 | } |
530 | |
531 | llvm::Constant *CodeGenModule::getMemberFunctionPointer(const FunctionDecl *FD, |
532 | llvm::Type *Ty) { |
533 | QualType FT = FD->getType(); |
534 | FT = getContext().getMemberPointerType(T: FT, /*Qualifier=*/nullptr, |
535 | Cls: cast<CXXMethodDecl>(Val: FD)->getParent()); |
536 | return getMemberFunctionPointer(Pointer: getRawFunctionPointer(GD: FD, Ty), FT); |
537 | } |
538 | |
539 | std::optional<PointerAuthQualifier> |
540 | CodeGenModule::computeVTPointerAuthentication(const CXXRecordDecl *ThisClass) { |
541 | auto DefaultAuthentication = getCodeGenOpts().PointerAuth.CXXVTablePointers; |
542 | if (!DefaultAuthentication) |
543 | return std::nullopt; |
544 | const CXXRecordDecl *PrimaryBase = |
545 | Context.baseForVTableAuthentication(ThisClass); |
546 | |
547 | unsigned Key = DefaultAuthentication.getKey(); |
548 | bool AddressDiscriminated = DefaultAuthentication.isAddressDiscriminated(); |
549 | auto DefaultDiscrimination = DefaultAuthentication.getOtherDiscrimination(); |
550 | unsigned TypeBasedDiscriminator = |
551 | Context.getPointerAuthVTablePointerDiscriminator(RD: PrimaryBase); |
552 | unsigned Discriminator; |
553 | if (DefaultDiscrimination == PointerAuthSchema::Discrimination::Type) { |
554 | Discriminator = TypeBasedDiscriminator; |
555 | } else if (DefaultDiscrimination == |
556 | PointerAuthSchema::Discrimination::Constant) { |
557 | Discriminator = DefaultAuthentication.getConstantDiscrimination(); |
558 | } else { |
559 | assert(DefaultDiscrimination == PointerAuthSchema::Discrimination::None); |
560 | Discriminator = 0; |
561 | } |
562 | if (auto ExplicitAuthentication = |
563 | PrimaryBase->getAttr<VTablePointerAuthenticationAttr>()) { |
564 | auto ExplicitAddressDiscrimination = |
565 | ExplicitAuthentication->getAddressDiscrimination(); |
566 | auto ExplicitDiscriminator = |
567 | ExplicitAuthentication->getExtraDiscrimination(); |
568 | |
569 | unsigned ExplicitKey = ExplicitAuthentication->getKey(); |
570 | if (ExplicitKey == VTablePointerAuthenticationAttr::NoKey) |
571 | return std::nullopt; |
572 | |
573 | if (ExplicitKey != VTablePointerAuthenticationAttr::DefaultKey) { |
574 | if (ExplicitKey == VTablePointerAuthenticationAttr::ProcessIndependent) |
575 | Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDA; |
576 | else { |
577 | assert(ExplicitKey == |
578 | VTablePointerAuthenticationAttr::ProcessDependent); |
579 | Key = (unsigned)PointerAuthSchema::ARM8_3Key::ASDB; |
580 | } |
581 | } |
582 | |
583 | if (ExplicitAddressDiscrimination != |
584 | VTablePointerAuthenticationAttr::DefaultAddressDiscrimination) |
585 | AddressDiscriminated = |
586 | ExplicitAddressDiscrimination == |
587 | VTablePointerAuthenticationAttr::AddressDiscrimination; |
588 | |
589 | if (ExplicitDiscriminator == |
590 | VTablePointerAuthenticationAttr::TypeDiscrimination) |
591 | Discriminator = TypeBasedDiscriminator; |
592 | else if (ExplicitDiscriminator == |
593 | VTablePointerAuthenticationAttr::CustomDiscrimination) |
594 | Discriminator = ExplicitAuthentication->getCustomDiscriminationValue(); |
595 | else if (ExplicitDiscriminator == |
596 | VTablePointerAuthenticationAttr::NoExtraDiscrimination) |
597 | Discriminator = 0; |
598 | } |
599 | return PointerAuthQualifier::Create(Key, IsAddressDiscriminated: AddressDiscriminated, ExtraDiscriminator: Discriminator, |
600 | AuthenticationMode: PointerAuthenticationMode::SignAndAuth, |
601 | /* IsIsaPointer */ false, |
602 | /* AuthenticatesNullValues */ false); |
603 | } |
604 | |
605 | std::optional<PointerAuthQualifier> |
606 | CodeGenModule::getVTablePointerAuthentication(const CXXRecordDecl *Record) { |
607 | if (!Record->getDefinition() || !Record->isPolymorphic()) |
608 | return std::nullopt; |
609 | |
610 | auto Existing = VTablePtrAuthInfos.find(Val: Record); |
611 | std::optional<PointerAuthQualifier> Authentication; |
612 | if (Existing != VTablePtrAuthInfos.end()) { |
613 | Authentication = Existing->getSecond(); |
614 | } else { |
615 | Authentication = computeVTPointerAuthentication(ThisClass: Record); |
616 | VTablePtrAuthInfos.insert(KV: std::make_pair(x&: Record, y&: Authentication)); |
617 | } |
618 | return Authentication; |
619 | } |
620 | |
621 | std::optional<CGPointerAuthInfo> |
622 | CodeGenModule::getVTablePointerAuthInfo(CodeGenFunction *CGF, |
623 | const CXXRecordDecl *Record, |
624 | llvm::Value *StorageAddress) { |
625 | auto Authentication = getVTablePointerAuthentication(Record); |
626 | if (!Authentication) |
627 | return std::nullopt; |
628 | |
629 | llvm::Value *Discriminator = nullptr; |
630 | if (auto = Authentication->getExtraDiscriminator()) |
631 | Discriminator = llvm::ConstantInt::get(Ty: IntPtrTy, V: ExtraDiscriminator); |
632 | |
633 | if (Authentication->isAddressDiscriminated()) { |
634 | assert(StorageAddress && |
635 | "address not provided for address-discriminated schema" ); |
636 | if (Discriminator) |
637 | Discriminator = |
638 | CGF->EmitPointerAuthBlendDiscriminator(StorageAddress, Discriminator); |
639 | else |
640 | Discriminator = CGF->Builder.CreatePtrToInt(V: StorageAddress, DestTy: IntPtrTy); |
641 | } |
642 | |
643 | return CGPointerAuthInfo(Authentication->getKey(), |
644 | PointerAuthenticationMode::SignAndAuth, |
645 | /* IsIsaPointer */ false, |
646 | /* AuthenticatesNullValues */ false, Discriminator); |
647 | } |
648 | |
649 | llvm::Value *CodeGenFunction::authPointerToPointerCast(llvm::Value *ResultPtr, |
650 | QualType SourceType, |
651 | QualType DestType) { |
652 | CGPointerAuthInfo CurAuthInfo, NewAuthInfo; |
653 | if (SourceType->isSignableType(Ctx: getContext())) |
654 | CurAuthInfo = getPointerAuthInfoForType(CGM, PointerType: SourceType); |
655 | |
656 | if (DestType->isSignableType(Ctx: getContext())) |
657 | NewAuthInfo = getPointerAuthInfoForType(CGM, PointerType: DestType); |
658 | |
659 | if (!CurAuthInfo && !NewAuthInfo) |
660 | return ResultPtr; |
661 | |
662 | // If only one side of the cast is a function pointer, then we still need to |
663 | // resign to handle casts to/from opaque pointers. |
664 | if (!CurAuthInfo && DestType->isFunctionPointerType()) |
665 | CurAuthInfo = CGM.getFunctionPointerAuthInfo(T: SourceType); |
666 | |
667 | if (!NewAuthInfo && SourceType->isFunctionPointerType()) |
668 | NewAuthInfo = CGM.getFunctionPointerAuthInfo(T: DestType); |
669 | |
670 | return emitPointerAuthResign(Value: ResultPtr, Type: DestType, CurAuthInfo, NewAuthInfo, |
671 | /*IsKnownNonNull=*/false); |
672 | } |
673 | |
674 | Address CodeGenFunction::authPointerToPointerCast(Address Ptr, |
675 | QualType SourceType, |
676 | QualType DestType) { |
677 | CGPointerAuthInfo CurAuthInfo, NewAuthInfo; |
678 | if (SourceType->isSignableType(Ctx: getContext())) |
679 | CurAuthInfo = getPointerAuthInfoForType(CGM, PointerType: SourceType); |
680 | |
681 | if (DestType->isSignableType(Ctx: getContext())) |
682 | NewAuthInfo = getPointerAuthInfoForType(CGM, PointerType: DestType); |
683 | |
684 | if (!CurAuthInfo && !NewAuthInfo) |
685 | return Ptr; |
686 | |
687 | if (!CurAuthInfo && DestType->isFunctionPointerType()) { |
688 | // When casting a non-signed pointer to a function pointer, just set the |
689 | // auth info on Ptr to the assumed schema. The pointer will be resigned to |
690 | // the effective type when used. |
691 | Ptr.setPointerAuthInfo(CGM.getFunctionPointerAuthInfo(T: SourceType)); |
692 | return Ptr; |
693 | } |
694 | |
695 | if (!NewAuthInfo && SourceType->isFunctionPointerType()) { |
696 | NewAuthInfo = CGM.getFunctionPointerAuthInfo(T: DestType); |
697 | Ptr = Ptr.getResignedAddress(NewInfo: NewAuthInfo, CGF&: *this); |
698 | Ptr.setPointerAuthInfo(CGPointerAuthInfo()); |
699 | return Ptr; |
700 | } |
701 | |
702 | return Ptr; |
703 | } |
704 | |
705 | Address CodeGenFunction::getAsNaturalAddressOf(Address Addr, |
706 | QualType PointeeTy) { |
707 | CGPointerAuthInfo Info = |
708 | PointeeTy.isNull() ? CGPointerAuthInfo() |
709 | : CGM.getPointerAuthInfoForPointeeType(T: PointeeTy); |
710 | return Addr.getResignedAddress(NewInfo: Info, CGF&: *this); |
711 | } |
712 | |
713 | Address Address::getResignedAddress(const CGPointerAuthInfo &NewInfo, |
714 | CodeGenFunction &CGF) const { |
715 | assert(isValid() && "pointer isn't valid" ); |
716 | CGPointerAuthInfo CurInfo = getPointerAuthInfo(); |
717 | llvm::Value *Val; |
718 | |
719 | // Nothing to do if neither the current or the new ptrauth info needs signing. |
720 | if (!CurInfo.isSigned() && !NewInfo.isSigned()) |
721 | return Address(getBasePointer(), getElementType(), getAlignment(), |
722 | isKnownNonNull()); |
723 | |
724 | assert(ElementType && "Effective type has to be set" ); |
725 | assert(!Offset && "unexpected non-null offset" ); |
726 | |
727 | // If the current and the new ptrauth infos are the same and the offset is |
728 | // null, just cast the base pointer to the effective type. |
729 | if (CurInfo == NewInfo && !hasOffset()) |
730 | Val = getBasePointer(); |
731 | else |
732 | Val = CGF.emitPointerAuthResign(Value: getBasePointer(), Type: QualType(), CurAuthInfo: CurInfo, |
733 | NewAuthInfo: NewInfo, IsKnownNonNull: isKnownNonNull()); |
734 | |
735 | return Address(Val, getElementType(), getAlignment(), NewInfo, |
736 | /*Offset=*/nullptr, isKnownNonNull()); |
737 | } |
738 | |
739 | llvm::Value *Address::emitRawPointerSlow(CodeGenFunction &CGF) const { |
740 | return CGF.getAsNaturalPointerTo(Addr: *this, PointeeType: QualType()); |
741 | } |
742 | |
743 | llvm::Value *LValue::getPointer(CodeGenFunction &CGF) const { |
744 | assert(isSimple()); |
745 | return emitResignedPointer(PointeeTy: getType(), CGF); |
746 | } |
747 | |
748 | llvm::Value *LValue::emitResignedPointer(QualType PointeeTy, |
749 | CodeGenFunction &CGF) const { |
750 | assert(isSimple()); |
751 | return CGF.getAsNaturalAddressOf(Addr: Addr, PointeeTy).getBasePointer(); |
752 | } |
753 | |
754 | llvm::Value *LValue::emitRawPointer(CodeGenFunction &CGF) const { |
755 | assert(isSimple()); |
756 | return Addr.isValid() ? Addr.emitRawPointer(CGF) : nullptr; |
757 | } |
758 | |