diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst index 7f3a128cd1557..bda84669456ce 100644 --- a/clang/docs/AllocToken.rst +++ b/clang/docs/AllocToken.rst @@ -122,6 +122,35 @@ which encodes the token ID hint in the allocation function name. This ABI provides a more efficient alternative where ``-falloc-token-max`` is small. +Instrumenting Non-Standard Allocation Functions +----------------------------------------------- + +By default, AllocToken only instruments standard library allocation functions. +This simplifies adoption, as a compatible allocator only needs to provide +token-enabled variants for a well-defined set of standard functions. + +To extend instrumentation to custom allocation functions, enable broader +coverage with ``-fsanitize-alloc-token-extended``. Such functions require being +marked with the `malloc +`_ or `alloc_size +`_ attributes +(or a combination). + +For example: + +.. code-block:: c + + void *custom_malloc(size_t size) __attribute__((malloc)); + void *my_malloc(size_t size) __attribute__((alloc_size(1))); + + // Original: + ptr1 = custom_malloc(size); + ptr2 = my_malloc(size); + + // Instrumented: + ptr1 = __alloc_token_custom_malloc(size, token_id); + ptr2 = __alloc_token_my_malloc(size, token_id); + Disabling Instrumentation ------------------------- diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 7dd6a830502bd..e8255b0554da8 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -30,6 +30,7 @@ #include "clang/AST/Attr.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/NSAPI.h" +#include "clang/AST/ParentMapContext.h" #include "clang/AST/StmtVisitor.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/CodeGenOptions.h" @@ -1353,6 +1354,115 @@ void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) { CB->setMetadata(llvm::LLVMContext::MD_alloc_token, MDN); } +namespace { +/// Infer type from a simple sizeof expression. +QualType inferTypeFromSizeofExpr(const Expr *E) { + const Expr *Arg = E->IgnoreParenImpCasts(); + if (const auto *UET = dyn_cast(Arg)) { + if (UET->getKind() == UETT_SizeOf) { + if (UET->isArgumentType()) + return UET->getArgumentTypeInfo()->getType(); + else + return UET->getArgumentExpr()->getType(); + } + } + return QualType(); +} + +/// Infer type from an arithmetic expression involving a sizeof. For example: +/// +/// malloc(sizeof(MyType) + padding); // infers 'MyType' +/// malloc(sizeof(MyType) * 32); // infers 'MyType' +/// malloc(32 * sizeof(MyType)); // infers 'MyType' +/// malloc(sizeof(MyType) << 1); // infers 'MyType' +/// ... +/// +/// More complex arithmetic expressions are supported, but are a heuristic, e.g. +/// when considering allocations for structs with flexible array members: +/// +/// malloc(sizeof(HasFlexArray) + sizeof(int) * 32); // infers 'HasFlexArray' +/// +QualType inferPossibleTypeFromArithSizeofExpr(const Expr *E) { + const Expr *Arg = E->IgnoreParenImpCasts(); + // The argument is a lone sizeof expression. + if (QualType T = inferTypeFromSizeofExpr(Arg); !T.isNull()) + return T; + if (const auto *BO = dyn_cast(Arg)) { + // Argument is an arithmetic expression. Cover common arithmetic patterns + // involving sizeof. + switch (BO->getOpcode()) { + case BO_Add: + case BO_Div: + case BO_Mul: + case BO_Shl: + case BO_Shr: + case BO_Sub: + if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getLHS()); + !T.isNull()) + return T; + if (QualType T = inferPossibleTypeFromArithSizeofExpr(BO->getRHS()); + !T.isNull()) + return T; + break; + default: + break; + } + } + return QualType(); +} + +/// If the expression E is a reference to a variable, infer the type from a +/// variable's initializer if it contains a sizeof. Beware, this is a heuristic +/// and ignores if a variable is later reassigned. For example: +/// +/// size_t my_size = sizeof(MyType); +/// void *x = malloc(my_size); // infers 'MyType' +/// +QualType inferPossibleTypeFromVarInitSizeofExpr(const Expr *E) { + const Expr *Arg = E->IgnoreParenImpCasts(); + if (const auto *DRE = dyn_cast(Arg)) { + if (const auto *VD = dyn_cast(DRE->getDecl())) { + if (const Expr *Init = VD->getInit()) + return inferPossibleTypeFromArithSizeofExpr(Init); + } + } + return QualType(); +} + +/// Deduces the allocated type by checking if the allocation call's result +/// is immediately used in a cast expression. For example: +/// +/// MyType *x = (MyType *)malloc(4096); // infers 'MyType' +/// +QualType inferPossibleTypeFromCastExpr(const CallExpr *CallE, + const CastExpr *CastE) { + if (!CastE) + return QualType(); + QualType PtrType = CastE->getType(); + if (PtrType->isPointerType()) + return PtrType->getPointeeType(); + return QualType(); +} +} // end anonymous namespace + +void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, const CallExpr *E) { + QualType AllocType; + // First check arguments. + for (const Expr *Arg : E->arguments()) { + AllocType = inferPossibleTypeFromArithSizeofExpr(Arg); + if (AllocType.isNull()) + AllocType = inferPossibleTypeFromVarInitSizeofExpr(Arg); + if (!AllocType.isNull()) + break; + } + // Then check later casts. + if (AllocType.isNull()) + AllocType = inferPossibleTypeFromCastExpr(E, CurCast); + // Emit if we were able to infer the type. + if (!AllocType.isNull()) + EmitAllocToken(CB, AllocType); +} + CodeGenFunction::ComplexPairTy CodeGenFunction:: EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV, bool isInc, bool isPre) { @@ -5723,6 +5833,9 @@ LValue CodeGenFunction::EmitConditionalOperatorLValue( /// are permitted with aggregate result, including noop aggregate casts, and /// cast from scalar to union. LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { + auto RestoreCurCast = + llvm::make_scope_exit([this, Prev = CurCast] { CurCast = Prev; }); + CurCast = E; switch (E->getCastKind()) { case CK_ToVoid: case CK_BitCast: @@ -6668,16 +6781,24 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, RValue Call = EmitCall(FnInfo, Callee, ReturnValue, Args, &LocalCallOrInvoke, E == MustTailCall, E->getExprLoc()); - // Generate function declaration DISuprogram in order to be used - // in debug info about call sites. - if (CGDebugInfo *DI = getDebugInfo()) { - if (auto *CalleeDecl = dyn_cast_or_null(TargetDecl)) { + if (auto *CalleeDecl = dyn_cast_or_null(TargetDecl)) { + // Generate function declaration DISuprogram in order to be used + // in debug info about call sites. + if (CGDebugInfo *DI = getDebugInfo()) { FunctionArgList Args; QualType ResTy = BuildFunctionArgList(CalleeDecl, Args); DI->EmitFuncDeclForCallSite(LocalCallOrInvoke, DI->getFunctionType(CalleeDecl, ResTy, Args), CalleeDecl); } + if (CalleeDecl->hasAttr() || + CalleeDecl->hasAttr()) { + // Function has 'malloc' (aka. 'restrict') or 'alloc_size' attribute. + if (SanOpts.has(SanitizerKind::AllocToken)) { + // Set !alloc_token metadata. + EmitAllocToken(LocalCallOrInvoke, E); + } + } } if (CallOrInvoke) *CallOrInvoke = LocalCallOrInvoke; diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 290c2e0f1039c..31ac26662b4c8 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1371,8 +1371,16 @@ RValue CodeGenFunction::EmitBuiltinNewDeleteCall(const FunctionProtoType *Type, for (auto *Decl : Ctx.getTranslationUnitDecl()->lookup(Name)) if (auto *FD = dyn_cast(Decl)) - if (Ctx.hasSameType(FD->getType(), QualType(Type, 0))) - return EmitNewDeleteCall(*this, FD, Type, Args); + if (Ctx.hasSameType(FD->getType(), QualType(Type, 0))) { + RValue RV = EmitNewDeleteCall(*this, FD, Type, Args); + if (auto *CB = dyn_cast_if_present(RV.getScalarVal())) { + if (SanOpts.has(SanitizerKind::AllocToken)) { + // Set !alloc_token metadata. + EmitAllocToken(CB, TheCall); + } + } + return RV; + } llvm_unreachable("predeclared global operator new/delete is missing"); } diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 06d9d812bc60b..715160d067817 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -33,6 +33,7 @@ #include "clang/Basic/DiagnosticTrap.h" #include "clang/Basic/TargetInfo.h" #include "llvm/ADT/APFixedPoint.h" +#include "llvm/ADT/ScopeExit.h" #include "llvm/IR/Argument.h" #include "llvm/IR/CFG.h" #include "llvm/IR/Constants.h" @@ -2434,6 +2435,10 @@ static Value *EmitHLSLElementwiseCast(CodeGenFunction &CGF, LValue SrcVal, // have to handle a more broad range of conversions than explicit casts, as they // handle things like function to ptr-to-function decay etc. Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { + auto RestoreCurCast = + llvm::make_scope_exit([this, Prev = CGF.CurCast] { CGF.CurCast = Prev; }); + CGF.CurCast = CE; + Expr *E = CE->getSubExpr(); QualType DestTy = CE->getType(); CastKind Kind = CE->getCastKind(); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index e14e60c9bdc43..1f0be2d8756de 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -346,6 +346,10 @@ class CodeGenFunction : public CodeGenTypeCache { QualType FnRetTy; llvm::Function *CurFn = nullptr; + /// If a cast expression is being visited, this holds the current cast's + /// expression. + const CastExpr *CurCast = nullptr; + /// Save Parameter Decl for coroutine. llvm::SmallVector FnArgs; @@ -3350,6 +3354,9 @@ class CodeGenFunction : public CodeGenTypeCache { /// Emit additional metadata used by the AllocToken instrumentation. void EmitAllocToken(llvm::CallBase *CB, QualType AllocType); + /// Emit additional metadata used by the AllocToken instrumentation, + /// inferring the type from an allocation call expression. + void EmitAllocToken(llvm::CallBase *CB, const CallExpr *E); llvm::Value *GetCountedByFieldExprGEP(const Expr *Base, const FieldDecl *FD, const FieldDecl *CountDecl); diff --git a/clang/test/CodeGen/alloc-token-lower.c b/clang/test/CodeGen/alloc-token-lower.c index 75197bb3dbd44..43d9a6337b7db 100644 --- a/clang/test/CodeGen/alloc-token-lower.c +++ b/clang/test/CodeGen/alloc-token-lower.c @@ -10,7 +10,7 @@ typedef __typeof(sizeof(int)) size_t; void *malloc(size_t size); // CHECK-LABEL: @test_malloc( -// CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0) +// CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 2689373973731826898){{.*}} !alloc_token [[META_INT:![0-9]+]] void *test_malloc() { return malloc(sizeof(int)); } @@ -20,3 +20,15 @@ void *test_malloc() { void *no_sanitize_malloc(size_t size) __attribute__((no_sanitize("alloc-token"))) { return malloc(sizeof(int)); } + +// By default, we should not be touching malloc-attributed non-libcall +// functions: there might be an arbitrary number of these, and a compatible +// allocator will only implement standard allocation functions. +void *nonstandard_malloc(size_t size) __attribute__((malloc)); +// CHECK-LABEL: @test_nonlibcall_malloc( +// CHECK: call{{.*}} ptr @nonstandard_malloc(i64 noundef 4){{.*}} !alloc_token [[META_INT]] +void *test_nonlibcall_malloc() { + return nonstandard_malloc(sizeof(int)); +} + +// CHECK: [[META_INT]] = !{!"int", i1 false} diff --git a/clang/test/CodeGen/alloc-token-nonlibcalls.c b/clang/test/CodeGen/alloc-token-nonlibcalls.c new file mode 100644 index 0000000000000..da81becf4b352 --- /dev/null +++ b/clang/test/CodeGen/alloc-token-nonlibcalls.c @@ -0,0 +1,23 @@ +// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-alloc-token-extended -triple x86_64-linux-gnu -emit-llvm -disable-llvm-passes %s -o - | FileCheck --check-prefixes=CHECK,CHECK-CODEGEN %s +// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-alloc-token-extended -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,CHECK-LOWER %s +// RUN: %clang_cc1 -O -fsanitize=alloc-token -fsanitize-alloc-token-extended -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefixes=CHECK,CHECK-LOWER %s + +typedef __typeof(sizeof(int)) size_t; +typedef size_t gfp_t; + +void *custom_malloc(size_t size) __attribute__((malloc)); +void *__kmalloc(size_t size, gfp_t flags) __attribute__((alloc_size(1))); + +void *sink; + +// CHECK-LABEL: @test_nonlibcall_alloc( +// CHECK-CODEGEN: call noalias ptr @custom_malloc(i64 noundef 4){{.*}} !alloc_token [[META_INT:![0-9]+]] +// CHECK-CODEGEN: call ptr @__kmalloc(i64 noundef 4, i64 noundef 0){{.*}} !alloc_token [[META_INT]] +// CHECK-LOWER: call{{.*}} noalias ptr @__alloc_token_custom_malloc(i64 noundef 4, i64 2689373973731826898){{.*}} !alloc_token [[META_INT:![0-9]+]] +// CHECK-LOWER: call{{.*}} ptr @__alloc_token___kmalloc(i64 noundef 4, i64 noundef 0, i64 2689373973731826898){{.*}} !alloc_token [[META_INT]] +void test_nonlibcall_alloc() { + sink = custom_malloc(sizeof(int)); + sink = __kmalloc(sizeof(int), 0); +} + +// CHECK: [[META_INT]] = !{!"int", i1 false} diff --git a/clang/test/CodeGen/alloc-token.c b/clang/test/CodeGen/alloc-token.c index d1160adc060ba..3c9b4d8f0e910 100644 --- a/clang/test/CodeGen/alloc-token.c +++ b/clang/test/CodeGen/alloc-token.c @@ -2,36 +2,39 @@ typedef __typeof(sizeof(int)) size_t; -void *aligned_alloc(size_t alignment, size_t size); -void *malloc(size_t size); -void *calloc(size_t num, size_t size); -void *realloc(void *ptr, size_t size); -void *reallocarray(void *ptr, size_t nmemb, size_t size); -void *memalign(size_t alignment, size_t size); -void *valloc(size_t size); -void *pvalloc(size_t size); +void *aligned_alloc(size_t alignment, size_t size) __attribute__((malloc)); +void *malloc(size_t size) __attribute__((malloc)); +void *calloc(size_t num, size_t size) __attribute__((malloc)); +void *realloc(void *ptr, size_t size) __attribute__((malloc)); +void *reallocarray(void *ptr, size_t nmemb, size_t size) __attribute__((malloc)); +void *memalign(size_t alignment, size_t size) __attribute__((malloc)); +void *valloc(size_t size) __attribute__((malloc)); +void *pvalloc(size_t size) __attribute__((malloc)); int posix_memalign(void **memptr, size_t alignment, size_t size); void *sink; // CHECK-LABEL: define dso_local void @test_malloc_like( -// CHECK: call ptr @malloc(i64 noundef 4) -// CHECK: call ptr @calloc(i64 noundef 3, i64 noundef 4) -// CHECK: call ptr @realloc(ptr noundef {{.*}}, i64 noundef 8) -// CHECK: call ptr @reallocarray(ptr noundef {{.*}}, i64 noundef 5, i64 noundef 8) -// CHECK: call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024) -// CHECK: call align 16 ptr @memalign(i64 noundef 16, i64 noundef 256) -// CHECK: call ptr @valloc(i64 noundef 4096) -// CHECK: call ptr @pvalloc(i64 noundef 8192) +// CHECK: call noalias ptr @malloc(i64 noundef 4){{.*}} !alloc_token [[META_INT:![0-9]+]] +// CHECK: call noalias ptr @calloc(i64 noundef 3, i64 noundef 4){{.*}} !alloc_token [[META_INT]] +// CHECK: call noalias ptr @realloc(ptr noundef {{.*}}, i64 noundef 8){{.*}} !alloc_token [[META_LONG:![0-9]+]] +// CHECK: call noalias ptr @reallocarray(ptr noundef {{.*}}, i64 noundef 5, i64 noundef 8), !alloc_token [[META_LONG]] +// CHECK: call noalias align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 4){{.*}} !alloc_token [[META_INT]] +// CHECK: call noalias align 16 ptr @memalign(i64 noundef 16, i64 noundef 4){{.*}} !alloc_token [[META_INT]] +// CHECK: call noalias ptr @valloc(i64 noundef 4), !alloc_token [[META_INT]] +// CHECK: call noalias ptr @pvalloc(i64 noundef 4), !alloc_token [[META_INT]] // CHECK: call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4) void test_malloc_like() { sink = malloc(sizeof(int)); sink = calloc(3, sizeof(int)); sink = realloc(sink, sizeof(long)); sink = reallocarray(sink, 5, sizeof(long)); - sink = aligned_alloc(128, 1024); - sink = memalign(16, 256); - sink = valloc(4096); - sink = pvalloc(8192); - posix_memalign(&sink, 64, sizeof(int)); + sink = aligned_alloc(128, sizeof(int)); + sink = memalign(16, sizeof(int)); + sink = valloc(sizeof(int)); + sink = pvalloc(sizeof(int)); + posix_memalign(&sink, 64, sizeof(int)); // FIXME: support posix_memalign } + +// CHECK: [[META_INT]] = !{!"int", i1 false} +// CHECK: [[META_LONG]] = !{!"long", i1 false} diff --git a/clang/test/CodeGenCXX/alloc-token-pointer.cpp b/clang/test/CodeGenCXX/alloc-token-pointer.cpp index 4781d1b4c13f0..f12ee7a40a6b8 100644 --- a/clang/test/CodeGenCXX/alloc-token-pointer.cpp +++ b/clang/test/CodeGenCXX/alloc-token-pointer.cpp @@ -5,11 +5,13 @@ typedef __UINTPTR_TYPE__ uintptr_t; extern "C" { -void *malloc(size_t size); +void *malloc(size_t size) __attribute__((malloc)); } +void *sink; // prevent optimizations from removing the calls + // CHECK-LABEL: define dso_local noundef ptr @_Z15test_malloc_intv( -// CHECK: call ptr @malloc(i64 noundef 4) +// CHECK: call noalias ptr @malloc(i64 noundef 4){{.*}} !alloc_token [[META_INT:![0-9]+]] void *test_malloc_int() { int *a = (int *)malloc(sizeof(int)); *a = 42; @@ -17,7 +19,7 @@ void *test_malloc_int() { } // CHECK-LABEL: define dso_local noundef ptr @_Z15test_malloc_ptrv( -// CHECK: call ptr @malloc(i64 noundef 8) +// CHECK: call noalias ptr @malloc(i64 noundef 8){{.*}} !alloc_token [[META_INTPTR:![0-9]+]] int **test_malloc_ptr() { int **a = (int **)malloc(sizeof(int*)); *a = nullptr; @@ -25,7 +27,7 @@ int **test_malloc_ptr() { } // CHECK-LABEL: define dso_local noundef ptr @_Z12test_new_intv( -// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4){{.*}} !alloc_token [[META_INT:![0-9]+]] +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4){{.*}} !alloc_token [[META_INT]] int *test_new_int() { return new int; } @@ -37,7 +39,7 @@ unsigned long *test_new_ulong_array() { } // CHECK-LABEL: define dso_local noundef ptr @_Z12test_new_ptrv( -// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 8){{.*}} !alloc_token [[META_INTPTR:![0-9]+]] +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 8){{.*}} !alloc_token [[META_INTPTR]] int **test_new_ptr() { return new int*; } @@ -54,49 +56,69 @@ struct ContainsPtr { }; // CHECK-LABEL: define dso_local noundef ptr @_Z27test_malloc_struct_with_ptrv( -// CHECK: call ptr @malloc(i64 noundef 16) -ContainsPtr *test_malloc_struct_with_ptr() { - ContainsPtr *c = (ContainsPtr *)malloc(sizeof(ContainsPtr)); - return c; +// CHECK: call noalias ptr @malloc(i64 noundef 16){{.*}} !alloc_token [[META_CONTAINSPTR:![0-9]+]] +void *test_malloc_struct_with_ptr() { + return malloc(sizeof(ContainsPtr)); } // CHECK-LABEL: define dso_local noundef ptr @_Z33test_malloc_struct_array_with_ptrv( -// CHECK: call ptr @malloc(i64 noundef 160) -ContainsPtr *test_malloc_struct_array_with_ptr() { - ContainsPtr *c = (ContainsPtr *)malloc(10 * sizeof(ContainsPtr)); - return c; +// CHECK: call noalias ptr @malloc(i64 noundef 160){{.*}} !alloc_token [[META_CONTAINSPTR]] +void *test_malloc_struct_array_with_ptr() { + return malloc(10 * sizeof(ContainsPtr)); +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z31test_malloc_with_ptr_sizeof_vari( +// CHECK: call noalias ptr @malloc(i64 noundef {{.*}}){{.*}} !alloc_token [[META_CONTAINSPTR]] +void *test_malloc_with_ptr_sizeof_var(int x) { + unsigned long size = sizeof(ContainsPtr); + size *= x; + return malloc(size); +} + +// CHECK-LABEL: define dso_local noundef ptr @_Z29test_malloc_with_ptr_castonlyv( +// CHECK: call noalias ptr @malloc(i64 noundef 4096){{.*}} !alloc_token [[META_CONTAINSPTR]] +ContainsPtr *test_malloc_with_ptr_castonly() { + return (ContainsPtr *)malloc(4096); } // CHECK-LABEL: define dso_local noundef ptr @_Z32test_operatornew_struct_with_ptrv( -// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16){{.*}} !alloc_token [[META_CONTAINSPTR]] +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16){{.*}} !alloc_token [[META_CONTAINSPTR]] ContainsPtr *test_operatornew_struct_with_ptr() { ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(ContainsPtr)); + sink = ::operator new(sizeof(ContainsPtr)); return c; } // CHECK-LABEL: define dso_local noundef ptr @_Z38test_operatornew_struct_array_with_ptrv( -// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 160) +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 160){{.*}} !alloc_token [[META_CONTAINSPTR]] +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 160){{.*}} !alloc_token [[META_CONTAINSPTR]] ContainsPtr *test_operatornew_struct_array_with_ptr() { ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(ContainsPtr)); + sink = ::operator new(10 * sizeof(ContainsPtr)); return c; } // CHECK-LABEL: define dso_local noundef ptr @_Z33test_operatornew_struct_with_ptr2v( -// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16) +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16){{.*}} !alloc_token [[META_CONTAINSPTR]] +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16){{.*}} !alloc_token [[META_CONTAINSPTR]] ContainsPtr *test_operatornew_struct_with_ptr2() { ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(sizeof(*c)); + sink = ::operator new(sizeof(*c)); return c; } // CHECK-LABEL: define dso_local noundef ptr @_Z39test_operatornew_struct_array_with_ptr2v( -// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 160) +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 160){{.*}} !alloc_token [[META_CONTAINSPTR]] +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 160){{.*}} !alloc_token [[META_CONTAINSPTR]] ContainsPtr *test_operatornew_struct_array_with_ptr2() { ContainsPtr *c = (ContainsPtr *)__builtin_operator_new(10 * sizeof(*c)); + sink = ::operator new(10 * sizeof(*c)); return c; } // CHECK-LABEL: define dso_local noundef ptr @_Z24test_new_struct_with_ptrv( -// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16){{.*}} !alloc_token [[META_CONTAINSPTR:![0-9]+]] +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 16){{.*}} !alloc_token [[META_CONTAINSPTR]] ContainsPtr *test_new_struct_with_ptr() { return new ContainsPtr; } @@ -166,8 +188,8 @@ uptr *test_uintptr_isptr2() { } // CHECK: [[META_INT]] = !{!"int", i1 false} -// CHECK: [[META_ULONG]] = !{!"unsigned long", i1 false} // CHECK: [[META_INTPTR]] = !{!"int *", i1 true} +// CHECK: [[META_ULONG]] = !{!"unsigned long", i1 false} // CHECK: [[META_CONTAINSPTR]] = !{!"ContainsPtr", i1 true} // CHECK: [[META_TESTCLASS]] = !{!"TestClass", i1 false} // CHECK: [[META_VIRTUALTESTCLASS]] = !{!"VirtualTestClass", i1 true} diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp index 5914b4ca5ef23..feed808a3b89b 100644 --- a/clang/test/CodeGenCXX/alloc-token.cpp +++ b/clang/test/CodeGenCXX/alloc-token.cpp @@ -2,14 +2,14 @@ #include "../Analysis/Inputs/system-header-simulator-cxx.h" extern "C" { -void *aligned_alloc(size_t alignment, size_t size); -void *malloc(size_t size); -void *calloc(size_t num, size_t size); -void *realloc(void *ptr, size_t size); -void *reallocarray(void *ptr, size_t nmemb, size_t size); -void *memalign(size_t alignment, size_t size); -void *valloc(size_t size); -void *pvalloc(size_t size); +void *aligned_alloc(size_t alignment, size_t size) __attribute__((malloc)); +void *malloc(size_t size) __attribute__((malloc)); +void *calloc(size_t num, size_t size) __attribute__((malloc)); +void *realloc(void *ptr, size_t size) __attribute__((malloc)); +void *reallocarray(void *ptr, size_t nmemb, size_t size) __attribute__((malloc)); +void *memalign(size_t alignment, size_t size) __attribute__((malloc)); +void *valloc(size_t size) __attribute__((malloc)); +void *pvalloc(size_t size) __attribute__((malloc)); int posix_memalign(void **memptr, size_t alignment, size_t size); struct __sized_ptr_t { @@ -26,45 +26,58 @@ __sized_ptr_t __size_returning_new_aligned_hot_cold(size_t, std::align_val_t, _ void *sink; // prevent optimizations from removing the calls // CHECK-LABEL: define dso_local void @_Z16test_malloc_likev( -// CHECK: call ptr @malloc(i64 noundef 4) -// CHECK: call ptr @calloc(i64 noundef 3, i64 noundef 4) -// CHECK: call ptr @realloc(ptr noundef {{.*}}, i64 noundef 8) -// CHECK: call ptr @reallocarray(ptr noundef {{.*}}, i64 noundef 5, i64 noundef 8) -// CHECK: call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024) -// CHECK: call ptr @memalign(i64 noundef 16, i64 noundef 256) -// CHECK: call ptr @valloc(i64 noundef 4096) -// CHECK: call ptr @pvalloc(i64 noundef 8192) +// CHECK: call noalias ptr @malloc(i64 noundef 4){{.*}} !alloc_token [[META_INT:![0-9]+]] +// CHECK: call noalias ptr @calloc(i64 noundef 3, i64 noundef 4){{.*}} !alloc_token [[META_INT]] +// CHECK: call noalias ptr @realloc(ptr noundef {{.*}}, i64 noundef 8){{.*}} !alloc_token [[META_LONG:![0-9]+]] +// CHECK: call noalias ptr @reallocarray(ptr noundef {{.*}}, i64 noundef 5, i64 noundef 8), !alloc_token [[META_LONG]] +// CHECK: call noalias align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 4){{.*}} !alloc_token [[META_INT]] +// CHECK: call noalias ptr @memalign(i64 noundef 16, i64 noundef 4), !alloc_token [[META_INT]] +// CHECK: call noalias ptr @valloc(i64 noundef 4), !alloc_token [[META_INT]] +// CHECK: call noalias ptr @pvalloc(i64 noundef 4), !alloc_token [[META_INT]] // CHECK: call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4) void test_malloc_like() { sink = malloc(sizeof(int)); sink = calloc(3, sizeof(int)); sink = realloc(sink, sizeof(long)); sink = reallocarray(sink, 5, sizeof(long)); - sink = aligned_alloc(128, 1024); - sink = memalign(16, 256); - sink = valloc(4096); - sink = pvalloc(8192); - posix_memalign(&sink, 64, sizeof(int)); + sink = aligned_alloc(128, sizeof(int)); + sink = memalign(16, sizeof(int)); + sink = valloc(sizeof(int)); + sink = pvalloc(sizeof(int)); + posix_memalign(&sink, 64, sizeof(int)); // FIXME: support posix_memalign +} + +class ForwardDecl; + +// CHECK-LABEL: define dso_local void @_Z21test_malloc_like_castv( +// CHECK: call noalias ptr @malloc(i64 noundef 64){{.*}} !alloc_token [[META_INT]] +// CHECK: call noalias ptr @malloc(i64 noundef 64){{.*}} !alloc_token [[META_INT]] +// CHECK-NOT: call noalias ptr @malloc(i64 noundef 64){{.*}} !alloc_token [[META_INT]] +void test_malloc_like_cast() { + sink = (int *)malloc(64); + sink = reinterpret_cast(malloc(64)); + // Always fails to assign token ID for incomplete types. + sink = reinterpret_cast(malloc(64)); } // CHECK-LABEL: define dso_local void @_Z17test_operator_newv( -// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) -// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4){{.*}} !alloc_token [[META_INT]] +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4){{.*}} !alloc_token [[META_INT]] void test_operator_new() { sink = __builtin_operator_new(sizeof(int)); sink = ::operator new(sizeof(int)); } // CHECK-LABEL: define dso_local void @_Z25test_operator_new_nothrowv( -// CHECK: call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) -// CHECK: call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) +// CHECK: call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow){{.*}} !alloc_token [[META_INT]] +// CHECK: call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow){{.*}} !alloc_token [[META_INT]] void test_operator_new_nothrow() { sink = __builtin_operator_new(sizeof(int), std::nothrow); sink = ::operator new(sizeof(int), std::nothrow); } // CHECK-LABEL: define dso_local noundef ptr @_Z8test_newv( -// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4){{.*}} !alloc_token [[META_INT:![0-9]+]] +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4){{.*}} !alloc_token [[META_INT]] int *test_new() { return new int; } @@ -93,6 +106,7 @@ int *test_new_array_nothrow() { // CHECK: call { ptr, i64 } @__size_returning_new_aligned(i64 noundef 8, i64 noundef 32) // CHECK: call { ptr, i64 } @__size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1) void test_size_returning_new() { + // FIXME: Support __size_returning_new variants. sink = __size_returning_new(sizeof(long)).p; sink = __size_returning_new_hot_cold(sizeof(long), __hot_cold_t{1}).p; sink = __size_returning_new_aligned(sizeof(long), std::align_val_t{32}).p; @@ -138,4 +152,5 @@ TestClass *test_new_class_array() { } // CHECK: [[META_INT]] = !{!"int", i1 false} +// CHECK: [[META_LONG]] = !{!"long", i1 false} // CHECK: [[META_TESTCLASS]] = !{!"TestClass", i1 true}