From 75bfb7a26113faf6de6595cadb41accc4d3f9937 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Thu, 4 Sep 2025 11:43:19 +0200 Subject: [PATCH 01/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?= =?UTF-8?q?changes=20to=20main=20this=20commit=20is=20based=20on?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.8-beta.1 [skip ci] --- clang/docs/AllocToken.rst | 172 +++++++ clang/docs/ReleaseNotes.rst | 4 + clang/docs/UsersManual.rst | 2 + clang/include/clang/Basic/CodeGenOptions.def | 2 + clang/include/clang/Basic/CodeGenOptions.h | 3 + clang/include/clang/Basic/Sanitizers.def | 3 + clang/include/clang/Driver/Options.td | 17 + clang/include/clang/Driver/SanitizerArgs.h | 4 +- clang/lib/CodeGen/BackendUtil.cpp | 20 + clang/lib/CodeGen/CGExpr.cpp | 16 + clang/lib/CodeGen/CGExprCXX.cpp | 15 +- clang/lib/CodeGen/CodeGenFunction.cpp | 2 + clang/lib/CodeGen/CodeGenFunction.h | 3 + clang/lib/Driver/SanitizerArgs.cpp | 31 +- clang/lib/Driver/ToolChains/BareMetal.cpp | 1 + clang/lib/Driver/ToolChains/Clang.cpp | 2 + clang/lib/Driver/ToolChains/Linux.cpp | 1 + clang/lib/Frontend/CompilerInvocation.cpp | 14 + clang/lib/Frontend/InitPreprocessor.cpp | 2 + clang/test/CodeGen/alloc-token-ignorelist.c | 27 + clang/test/CodeGen/alloc-token.c | 45 ++ clang/test/CodeGenCXX/alloc-token.cpp | 157 ++++++ clang/test/Driver/fsanitize-alloc-token.c | 43 ++ clang/test/Preprocessor/alloc_token.cpp | 10 + llvm/docs/LangRef.rst | 3 + llvm/docs/ReleaseNotes.md | 4 + llvm/include/llvm/Bitcode/LLVMBitCodes.h | 1 + llvm/include/llvm/IR/Attributes.td | 3 + .../Transforms/Instrumentation/AllocToken.h | 46 ++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp | 2 + llvm/lib/Bitcode/Writer/BitcodeWriter.cpp | 2 + llvm/lib/Passes/PassBuilder.cpp | 1 + llvm/lib/Passes/PassRegistry.def | 1 + .../Transforms/Instrumentation/AllocToken.cpp | 484 ++++++++++++++++++ .../Transforms/Instrumentation/CMakeLists.txt | 1 + llvm/lib/Transforms/Utils/CodeExtractor.cpp | 1 + llvm/test/Bitcode/attributes.ll | 6 + llvm/test/Bitcode/compatibility.ll | 8 +- llvm/test/Instrumentation/AllocToken/basic.ll | 84 +++ .../AllocToken/extralibfuncs.ll | 32 ++ llvm/test/Instrumentation/AllocToken/fast.ll | 39 ++ .../test/Instrumentation/AllocToken/ignore.ll | 30 ++ .../test/Instrumentation/AllocToken/invoke.ll | 86 ++++ .../Instrumentation/AllocToken/nonlibcalls.ll | 63 +++ .../test/Instrumentation/AllocToken/remark.ll | 27 + llvm/test/Transforms/Inline/attributes.ll | 42 ++ llvm/utils/emacs/llvm-mode.el | 2 +- .../lib/Transforms/Instrumentation/BUILD.gn | 1 + llvm/utils/llvm.grm | 1 + llvm/utils/vim/syntax/llvm.vim | 1 + .../vscode/llvm/syntaxes/ll.tmLanguage.yaml | 1 + 51 files changed, 1555 insertions(+), 13 deletions(-) create mode 100644 clang/docs/AllocToken.rst create mode 100644 clang/test/CodeGen/alloc-token-ignorelist.c create mode 100644 clang/test/CodeGen/alloc-token.c create mode 100644 clang/test/CodeGenCXX/alloc-token.cpp create mode 100644 clang/test/Driver/fsanitize-alloc-token.c create mode 100644 clang/test/Preprocessor/alloc_token.cpp create mode 100644 llvm/include/llvm/Transforms/Instrumentation/AllocToken.h create mode 100644 llvm/lib/Transforms/Instrumentation/AllocToken.cpp create mode 100644 llvm/test/Instrumentation/AllocToken/basic.ll create mode 100644 llvm/test/Instrumentation/AllocToken/extralibfuncs.ll create mode 100644 llvm/test/Instrumentation/AllocToken/fast.ll create mode 100644 llvm/test/Instrumentation/AllocToken/ignore.ll create mode 100644 llvm/test/Instrumentation/AllocToken/invoke.ll create mode 100644 llvm/test/Instrumentation/AllocToken/nonlibcalls.ll create mode 100644 llvm/test/Instrumentation/AllocToken/remark.ll diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst new file mode 100644 index 0000000000000..a7bb8877f371b --- /dev/null +++ b/clang/docs/AllocToken.rst @@ -0,0 +1,172 @@ +================= +Allocation Tokens +================= + +.. contents:: + :local: + +Introduction +============ + +Clang provides support for allocation tokens to enable allocator-level heap +organization strategies. Clang assigns mode-dependent token IDs to allocation +calls; the runtime behavior depends entirely on the implementation of a +compatible memory allocator. + +Possible allocator strategies include: + +* **Security Hardening**: Placing allocations into separate, isolated heap + partitions. For example, separating pointer-containing types from raw data + can mitigate exploits that rely on overflowing a primitive buffer to corrupt + object metadata. + +* **Memory Layout Optimization**: Grouping related allocations to improve data + locality and cache utilization. + +* **Custom Allocation Policies**: Applying different management strategies to + different partitions. + +Token Assignment Mode +===================== + +The default mode to calculate tokens is: + +* *TypeHash* (mode=2): This mode assigns a token ID based on the hash of + the allocated type's name. + +Other token ID assignment modes are supported, but they may be subject to +change or removal. These may (experimentally) be selected with ``-mllvm +-alloc-token-mode=``: + +* *Random* (mode=1): This mode assigns a statically-determined random token ID + to each allocation site. + +* *Increment* (mode=0): This mode assigns a simple, incrementally increasing + token ID to each allocation site. + +Allocation Token Instrumentation +================================ + +To enable instrumentation of allocation functions, code can be compiled with +the ``-fsanitize=alloc-token`` flag: + +.. code-block:: console + + % clang++ -fsanitize=alloc-token example.cc + +The instrumentation transforms allocation calls to include a token ID. For +example: + +.. code-block:: c + + // Original: + ptr = malloc(size); + + // Instrumented: + ptr = __alloc_token_malloc(size, token_id); + +In addition, it is typically recommended to configure the following: + +* ``-falloc-token-max=`` + Configures the maximum number of tokens. No max by default (tokens bounded + by ``UINT64_MAX``). + + .. code-block:: console + + % clang++ -fsanitize=alloc-token -falloc-token-max=512 example.cc + +Runtime Interface +----------------- + +A compatible runtime must be provided that implements the token-enabled +allocation functions. The instrumentation generates calls to functions that +take a final ``uint64_t token_id`` argument. + +.. code-block:: c + + // C standard library functions + void *__alloc_token_malloc(size_t size, uint64_t token_id); + void *__alloc_token_calloc(size_t count, size_t size, uint64_t token_id); + void *__alloc_token_realloc(void *ptr, size_t size, uint64_t token_id); + // ... + + // C++ operators (mangled names) + // operator new(size_t, uint64_t) + void *__alloc_token_Znwm(size_t size, uint64_t token_id); + // operator new[](size_t, uint64_t) + void *__alloc_token_Znam(size_t size, uint64_t token_id); + // ... other variants like nothrow, etc., are also instrumented. + +Fast ABI +-------- + +An alternative ABI can be enabled with ``-fsanitize-alloc-token-fast-abi``, +which encodes the token ID hint in the allocation function name. + +.. code-block:: c + + void *__alloc_token_0_malloc(size_t size); + void *__alloc_token_1_malloc(size_t size); + void *__alloc_token_2_malloc(size_t size); + ... + void *__alloc_token_0_Znwm(size_t size); + void *__alloc_token_1_Znwm(size_t size); + void *__alloc_token_2_Znwm(size_t size); + ... + +This ABI provides a more efficient alternative where +``-falloc-token-max`` is small. + +Disabling Instrumentation +------------------------- + +To exclude specific functions from instrumentation, you can use the +``no_sanitize("alloc-token")`` attribute: + +.. code-block:: c + + __attribute__((no_sanitize("alloc-token"))) + void* custom_allocator(size_t size) { + return malloc(size); // Uses original malloc + } + +Note: Independent of any given allocator support, the instrumentation aims to +remain performance neutral. As such, ``no_sanitize("alloc-token")`` +functions may be inlined into instrumented functions and vice-versa. If +correctness is affected, such functions should explicitly be marked +``noinline``. + +The ``__attribute__((disable_sanitizer_instrumentation))`` is also supported to +disable this and other sanitizer instrumentations. + +Suppressions File (Ignorelist) +------------------------------ + +AllocToken respects the ``src`` and ``fun`` entity types in the +:doc:`SanitizerSpecialCaseList`, which can be used to omit specified source +files or functions from instrumentation. + +.. code-block:: bash + + # Exclude specific source files + src:third_party/allocator.c + # Exclude function name patterns + fun:*custom_malloc* + fun:LowLevel::* + +.. code-block:: console + + % clang++ -fsanitize=alloc-token -fsanitize-ignorelist=my_ignorelist.txt example.cc + +Conditional Compilation with ``__SANITIZE_ALLOC_TOKEN__`` +----------------------------------------------------------- + +In some cases, one may need to execute different code depending on whether +AllocToken instrumentation is enabled. The ``__SANITIZE_ALLOC_TOKEN__`` macro +can be used for this purpose. + +.. code-block:: c + + #ifdef __SANITIZE_ALLOC_TOKEN__ + // Code specific to -fsanitize=alloc-token builds + #endif diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index dd53b4d46f3cc..193b356631995 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -203,11 +203,15 @@ Non-comprehensive list of changes in this release Currently, the use of ``__builtin_dedup_pack`` is limited to template arguments and base specifiers, it also must be used within a template context. +- Introduce support for allocation tokens to enable allocator-level heap + organization strategies. A feature to instrument all allocation functions + with a token ID can be enabled via the ``-fsanitize=alloc-token`` flag. New Compiler Flags ------------------ - New option ``-fno-sanitize-debug-trap-reasons`` added to disable emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). - New option ``-fsanitize-debug-trap-reasons=`` added to control emitting trap reasons into the debug info when compiling with trapping UBSan (e.g. ``-fsanitize-trap=undefined``). +- New options for enabling allocation token instrumentation: ``-fsanitize=alloc-token``, ``-falloc-token-max=``, ``-fsanitize-alloc-token-fast-abi``, ``-fsanitize-alloc-token-extended``. Lanai Support diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 0e85c8109fd5e..f1bd348e4e22d 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2194,6 +2194,8 @@ are listed below. protection against stack-based memory corruption errors. - ``-fsanitize=realtime``: :doc:`RealtimeSanitizer`, a real-time safety checker. + - ``-fsanitize=alloc-token``: :doc:`AllocToken`, + allocation token instrumentation (requires compatible allocator). There are more fine-grained checks available: see the :ref:`list ` of specific kinds of diff --git a/clang/include/clang/Basic/CodeGenOptions.def b/clang/include/clang/Basic/CodeGenOptions.def index fda0da99b60c0..b54876fdf29dd 100644 --- a/clang/include/clang/Basic/CodeGenOptions.def +++ b/clang/include/clang/Basic/CodeGenOptions.def @@ -306,6 +306,8 @@ CODEGENOPT(SanitizeBinaryMetadataCovered, 1, 0, Benign) ///< Emit PCs for covere CODEGENOPT(SanitizeBinaryMetadataAtomics, 1, 0, Benign) ///< Emit PCs for atomic operations. CODEGENOPT(SanitizeBinaryMetadataUAR, 1, 0, Benign) ///< Emit PCs for start of functions ///< that are subject for use-after-return checking. +CODEGENOPT(SanitizeAllocTokenFastABI, 1, 0, Benign) ///< Use the AllocToken fast ABI. +CODEGENOPT(SanitizeAllocTokenExtended, 1, 0, Benign) ///< Extend coverage to custom allocation functions. CODEGENOPT(SanitizeStats , 1, 0, Benign) ///< Collect statistics for sanitizers. ENUM_CODEGENOPT(SanitizeDebugTrapReasons, SanitizeDebugTrapReasonKind, 2, SanitizeDebugTrapReasonKind::Detailed, Benign) ///< Control how "trap reasons" are emitted in debug info CODEGENOPT(SimplifyLibCalls , 1, 1, Benign) ///< Set when -fbuiltin is enabled. diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index 5d5cf250b56b9..eb283a27ed95a 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -447,6 +447,9 @@ class CodeGenOptions : public CodeGenOptionsBase { std::optional AllowRuntimeCheckSkipHotCutoff; + /// Maximum number of allocation tokens (0 = no max). + std::optional AllocTokenMax; + /// List of backend command-line options for -fembed-bitcode. std::vector CmdArgs; diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def index 1d0e97cc7fb4c..da85431625026 100644 --- a/clang/include/clang/Basic/Sanitizers.def +++ b/clang/include/clang/Basic/Sanitizers.def @@ -195,6 +195,9 @@ SANITIZER_GROUP("bounds", Bounds, ArrayBounds | LocalBounds) // Scudo hardened allocator SANITIZER("scudo", Scudo) +// AllocToken +SANITIZER("alloc-token", AllocToken) + // Magic group, containing all sanitizers. For example, "-fno-sanitize=all" // can be used to disable all the sanitizers. SANITIZER_GROUP("all", All, ~SanitizerMask()) diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 49e917a6a0786..064d55d14dcc0 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2730,8 +2730,25 @@ def fsanitize_skip_hot_cutoff_EQ "(0.0 [default] = skip none; 1.0 = skip all). " "Argument format: =,=,...">; +defm sanitize_alloc_token_fast_abi : BoolOption<"f", "sanitize-alloc-token-fast-abi", + CodeGenOpts<"SanitizeAllocTokenFastABI">, DefaultFalse, + PosFlag, + NegFlag>, + Group; +defm sanitize_alloc_token_extended : BoolOption<"f", "sanitize-alloc-token-extended", + CodeGenOpts<"SanitizeAllocTokenExtended">, DefaultFalse, + PosFlag, + NegFlag, + BothFlags<[], [ClangOption], " extended coverage to custom allocation functions">>, + Group; + } // end -f[no-]sanitize* flags +def falloc_token_max_EQ : Joined<["-"], "falloc-token-max=">, + Group, Visibility<[ClangOption, CC1Option, CLOption]>, + MetaVarName<"">, + HelpText<"Limit to maximum N allocation tokens (0 = no max)">; + def fallow_runtime_check_skip_hot_cutoff_EQ : Joined<["-"], "fallow-runtime-check-skip-hot-cutoff=">, Group, diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index 2b72268c8606c..ed51009654d4e 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -13,6 +13,7 @@ #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h" +#include #include #include @@ -73,8 +74,9 @@ class SanitizerArgs { bool HwasanUseAliases = false; llvm::AsanDetectStackUseAfterReturnMode AsanUseAfterReturn = llvm::AsanDetectStackUseAfterReturnMode::Invalid; - std::string MemtagMode; + bool AllocTokenFastABI = false; + bool AllocTokenExtended = false; public: /// Parses the sanitizer arguments from an argument list. diff --git a/clang/lib/CodeGen/BackendUtil.cpp b/clang/lib/CodeGen/BackendUtil.cpp index 3f095c03397fd..8b297134de4e7 100644 --- a/clang/lib/CodeGen/BackendUtil.cpp +++ b/clang/lib/CodeGen/BackendUtil.cpp @@ -59,11 +59,13 @@ #include "llvm/TargetParser/Triple.h" #include "llvm/Transforms/HipStdPar/HipStdPar.h" #include "llvm/Transforms/IPO/EmbedBitcodePass.h" +#include "llvm/Transforms/IPO/InferFunctionAttrs.h" #include "llvm/Transforms/IPO/LowerTypeTests.h" #include "llvm/Transforms/IPO/ThinLTOBitcodeWriter.h" #include "llvm/Transforms/InstCombine/InstCombine.h" #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" #include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h" +#include "llvm/Transforms/Instrumentation/AllocToken.h" #include "llvm/Transforms/Instrumentation/BoundsChecking.h" #include "llvm/Transforms/Instrumentation/DataFlowSanitizer.h" #include "llvm/Transforms/Instrumentation/GCOVProfiler.h" @@ -231,6 +233,14 @@ class EmitAssemblyHelper { }; } // namespace +static AllocTokenOptions getAllocTokenOptions(const CodeGenOptions &CGOpts) { + AllocTokenOptions Opts; + Opts.MaxTokens = CGOpts.AllocTokenMax; + Opts.Extended = CGOpts.SanitizeAllocTokenExtended; + Opts.FastABI = CGOpts.SanitizeAllocTokenFastABI; + return Opts; +} + static SanitizerCoverageOptions getSancovOptsFromCGOpts(const CodeGenOptions &CGOpts) { SanitizerCoverageOptions Opts; @@ -784,6 +794,16 @@ static void addSanitizers(const Triple &TargetTriple, if (LangOpts.Sanitize.has(SanitizerKind::DataFlow)) { MPM.addPass(DataFlowSanitizerPass(LangOpts.NoSanitizeFiles)); } + + if (LangOpts.Sanitize.has(SanitizerKind::AllocToken)) { + if (Level == OptimizationLevel::O0) { + // The default pass builder only infers libcall function attrs when + // optimizing, so we insert it here because we need it for accurate + // memory allocation function detection. + MPM.addPass(InferFunctionAttrsPass()); + } + MPM.addPass(AllocTokenPass(getAllocTokenOptions(CodeGenOpts))); + } }; if (ClSanitizeOnOptimizerEarlyEP) { PB.registerOptimizerEarlyEPCallback( diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 26fba751e6f9d..42dc7e5c91adb 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1272,6 +1272,22 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound, EmitCheck(std::make_pair(Check, CheckKind), CheckHandler, StaticData, Index); } +void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB, + QualType AllocType) { + assert(SanOpts.has(SanitizerKind::AllocToken) && + "Only needed with -fsanitize=alloc-token"); + + PrintingPolicy Policy(CGM.getContext().getLangOpts()); + Policy.SuppressTagKeyword = true; + Policy.FullyQualifiedName = true; + std::string TypeName = AllocType.getCanonicalType().getAsString(Policy); + auto *TypeMDS = llvm::MDString::get(CGM.getLLVMContext(), TypeName); + + // Format: !{} + auto *MDN = llvm::MDNode::get(CGM.getLLVMContext(), {TypeMDS}); + CB->setMetadata("alloc_token_hint", MDN); +} + CodeGenFunction::ComplexPairTy CodeGenFunction:: EmitComplexPrePostIncDec(const UnaryOperator *E, LValue LV, bool isInc, bool isPre) { diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 1e4c72a210f9a..6bf3332b425fa 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1707,11 +1707,16 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { RValue RV = EmitNewDeleteCall(*this, allocator, allocatorType, allocatorArgs); - // Set !heapallocsite metadata on the call to operator new. - if (getDebugInfo()) - if (auto *newCall = dyn_cast(RV.getScalarVal())) - getDebugInfo()->addHeapAllocSiteMetadata(newCall, allocType, - E->getExprLoc()); + if (auto *newCall = dyn_cast(RV.getScalarVal())) { + if (auto *CGDI = getDebugInfo()) { + // Set !heapallocsite metadata on the call to operator new. + CGDI->addHeapAllocSiteMetadata(newCall, allocType, E->getExprLoc()); + } + if (SanOpts.has(SanitizerKind::AllocToken)) { + // Set !alloc_token_hint metadata. + EmitAllocTokenHint(newCall, allocType); + } + } // If this was a call to a global replaceable allocation function that does // not take an alignment argument, the allocator is known to produce diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index b2fe9171372d8..acf8de4dee147 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -846,6 +846,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy, Fn->addFnAttr(llvm::Attribute::SanitizeNumericalStability); if (SanOpts.hasOneOf(SanitizerKind::Memory | SanitizerKind::KernelMemory)) Fn->addFnAttr(llvm::Attribute::SanitizeMemory); + if (SanOpts.has(SanitizerKind::AllocToken)) + Fn->addFnAttr(llvm::Attribute::SanitizeAllocToken); } if (SanOpts.has(SanitizerKind::SafeStack)) Fn->addFnAttr(llvm::Attribute::SafeStack); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 123cb4f51f828..fd7ec36183c2d 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3348,6 +3348,9 @@ class CodeGenFunction : public CodeGenTypeCache { SanitizerAnnotateDebugInfo(ArrayRef Ordinals, SanitizerHandler Handler); + /// Emit additional metadata used by the AllocToken instrumentation. + void EmitAllocTokenHint(llvm::CallBase *CB, QualType AllocType); + llvm::Value *GetCountedByFieldExprGEP(const Expr *Base, const FieldDecl *FD, const FieldDecl *CountDecl); diff --git a/clang/lib/Driver/SanitizerArgs.cpp b/clang/lib/Driver/SanitizerArgs.cpp index 7ce1afe6f2e6a..5dd48f53b9069 100644 --- a/clang/lib/Driver/SanitizerArgs.cpp +++ b/clang/lib/Driver/SanitizerArgs.cpp @@ -61,8 +61,9 @@ static const SanitizerMask RecoverableByDefault = SanitizerKind::ImplicitConversion | SanitizerKind::Nullability | SanitizerKind::FloatDivideByZero | SanitizerKind::ObjCCast | SanitizerKind::Vptr; -static const SanitizerMask Unrecoverable = - SanitizerKind::Unreachable | SanitizerKind::Return; +static const SanitizerMask Unrecoverable = SanitizerKind::Unreachable | + SanitizerKind::Return | + SanitizerKind::AllocToken; static const SanitizerMask AlwaysRecoverable = SanitizerKind::KernelAddress | SanitizerKind::KernelHWAddress | SanitizerKind::KCFI; @@ -84,7 +85,8 @@ static const SanitizerMask CFIClasses = static const SanitizerMask CompatibleWithMinimalRuntime = TrappingSupported | SanitizerKind::Scudo | SanitizerKind::ShadowCallStack | SanitizerKind::MemtagStack | SanitizerKind::MemtagHeap | - SanitizerKind::MemtagGlobals | SanitizerKind::KCFI; + SanitizerKind::MemtagGlobals | SanitizerKind::KCFI | + SanitizerKind::AllocToken; enum CoverageFeature { CoverageFunc = 1 << 0, @@ -203,6 +205,7 @@ static void addDefaultIgnorelists(const Driver &D, SanitizerMask Kinds, {"tysan_blacklist.txt", SanitizerKind::Type}, {"dfsan_abilist.txt", SanitizerKind::DataFlow}, {"cfi_ignorelist.txt", SanitizerKind::CFI}, + {"alloc_token_ignorelist.txt", SanitizerKind::AllocToken}, {"ubsan_ignorelist.txt", SanitizerKind::Undefined | SanitizerKind::Vptr | SanitizerKind::Integer | SanitizerKind::Nullability | @@ -650,7 +653,12 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, std::make_pair(SanitizerKind::KCFI, SanitizerKind::Function), std::make_pair(SanitizerKind::Realtime, SanitizerKind::Address | SanitizerKind::Thread | - SanitizerKind::Undefined | SanitizerKind::Memory)}; + SanitizerKind::Undefined | SanitizerKind::Memory), + std::make_pair(SanitizerKind::AllocToken, + SanitizerKind::Address | SanitizerKind::HWAddress | + SanitizerKind::KernelAddress | + SanitizerKind::KernelHWAddress | + SanitizerKind::Memory)}; // Enable toolchain specific default sanitizers if not explicitly disabled. SanitizerMask Default = TC.getDefaultSanitizers() & ~AllRemove; @@ -1159,6 +1167,15 @@ SanitizerArgs::SanitizerArgs(const ToolChain &TC, !TC.getTriple().isAndroid() && !TC.getTriple().isOSFuchsia(); } + if (AllAddedKinds & SanitizerKind::AllocToken) { + AllocTokenFastABI = Args.hasFlag( + options::OPT_fsanitize_alloc_token_fast_abi, + options::OPT_fno_sanitize_alloc_token_fast_abi, AllocTokenFastABI); + AllocTokenExtended = Args.hasFlag( + options::OPT_fsanitize_alloc_token_extended, + options::OPT_fno_sanitize_alloc_token_extended, AllocTokenExtended); + } + LinkRuntimes = Args.hasFlag(options::OPT_fsanitize_link_runtime, options::OPT_fno_sanitize_link_runtime, !Args.hasArg(options::OPT_r)); @@ -1527,6 +1544,12 @@ void SanitizerArgs::addArgs(const ToolChain &TC, const llvm::opt::ArgList &Args, Sanitizers.has(SanitizerKind::Address)) CmdArgs.push_back("-fno-assume-sane-operator-new"); + // Flags for -fsanitize=alloc-token. + if (AllocTokenFastABI) + CmdArgs.push_back("-fsanitize-alloc-token-fast-abi"); + if (AllocTokenExtended) + CmdArgs.push_back("-fsanitize-alloc-token-extended"); + // libFuzzer wants to intercept calls to certain library functions, so the // following -fno-builtin-* flags force the compiler to emit interposable // libcalls to these functions. Other sanitizers effectively do the same thing diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp index 9b7f58c392885..3319cdd93b5e2 100644 --- a/clang/lib/Driver/ToolChains/BareMetal.cpp +++ b/clang/lib/Driver/ToolChains/BareMetal.cpp @@ -726,6 +726,7 @@ SanitizerMask BareMetal::getSupportedSanitizers() const { Res |= SanitizerKind::SafeStack; Res |= SanitizerKind::Thread; Res |= SanitizerKind::Scudo; + Res |= SanitizerKind::AllocToken; if (IsX86_64 || IsAArch64 || IsRISCV64) { Res |= SanitizerKind::HWAddress; Res |= SanitizerKind::KernelHWAddress; diff --git a/clang/lib/Driver/ToolChains/Clang.cpp b/clang/lib/Driver/ToolChains/Clang.cpp index 21e45c6b56bbb..94b9f1ce3e260 100644 --- a/clang/lib/Driver/ToolChains/Clang.cpp +++ b/clang/lib/Driver/ToolChains/Clang.cpp @@ -7663,6 +7663,8 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA, // features enabled through -Xclang -target-feature flags. SanitizeArgs.addArgs(TC, Args, CmdArgs, InputType); + Args.AddLastArg(CmdArgs, options::OPT_falloc_token_max_EQ); + #if CLANG_ENABLE_CIR // Forward -mmlir arguments to to the MLIR option parser. for (const Arg *A : Args.filtered(options::OPT_mmlir)) { diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp index 16e35b08cfbd6..76070b063678f 100644 --- a/clang/lib/Driver/ToolChains/Linux.cpp +++ b/clang/lib/Driver/ToolChains/Linux.cpp @@ -819,6 +819,7 @@ SanitizerMask Linux::getSupportedSanitizers() const { Res |= SanitizerKind::KernelAddress; Res |= SanitizerKind::Vptr; Res |= SanitizerKind::SafeStack; + Res |= SanitizerKind::AllocToken; if (IsX86_64 || IsMIPS64 || IsAArch64 || IsLoongArch64) Res |= SanitizerKind::DataFlow; if (IsX86_64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch || IsPowerPC64 || diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index 8411d00cc7812..eab2da4ddcc98 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -1857,6 +1857,10 @@ void CompilerInvocationBase::GenerateCodeGenArgs(const CodeGenOptions &Opts, serializeSanitizerKinds(Opts.SanitizeAnnotateDebugInfo)) GenerateArg(Consumer, OPT_fsanitize_annotate_debug_info_EQ, Sanitizer); + if (Opts.AllocTokenMax) + GenerateArg(Consumer, OPT_falloc_token_max_EQ, + std::to_string(*Opts.AllocTokenMax)); + if (!Opts.EmitVersionIdentMetadata) GenerateArg(Consumer, OPT_Qn); @@ -2367,6 +2371,16 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, } } + if (const auto *Arg = Args.getLastArg(options::OPT_falloc_token_max_EQ)) { + StringRef S = Arg->getValue(); + uint64_t Value = 0; + if (S.getAsInteger(0, Value)) { + Diags.Report(diag::err_drv_invalid_value) << Arg->getAsString(Args) << S; + } else { + Opts.AllocTokenMax = Value; + } + } + Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true); if (!LangOpts->CUDAIsDevice) diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp index 4865c0b889044..28a173f9fe426 100644 --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -1525,6 +1525,8 @@ static void InitializePredefinedMacros(const TargetInfo &TI, Builder.defineMacro("__SANITIZE_HWADDRESS__"); if (LangOpts.Sanitize.has(SanitizerKind::Thread)) Builder.defineMacro("__SANITIZE_THREAD__"); + if (LangOpts.Sanitize.has(SanitizerKind::AllocToken)) + Builder.defineMacro("__SANITIZE_ALLOC_TOKEN__"); // Target OS macro definitions. if (PPOpts.DefineTargetOSMacros) { diff --git a/clang/test/CodeGen/alloc-token-ignorelist.c b/clang/test/CodeGen/alloc-token-ignorelist.c new file mode 100644 index 0000000000000..f6f82fe3b83e6 --- /dev/null +++ b/clang/test/CodeGen/alloc-token-ignorelist.c @@ -0,0 +1,27 @@ +// Test AllocToken respects ignorelist for functions and files. +// +// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ALLOW +// +// RUN: echo "fun:excluded_by_all" > %t.func.ignorelist +// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.func.ignorelist -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-FUN +// +// RUN: echo "src:%s" | sed -e 's/\\/\\\\/g' > %t.file.ignorelist +// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.file.ignorelist -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-SRC + +extern void* malloc(unsigned long size); + +// CHECK-LABEL: define{{.*}} @excluded_by_all( +void* excluded_by_all(unsigned long size) { + // CHECK-ALLOW: call ptr @__alloc_token_malloc( + // CHECK-FUN: call ptr @malloc( + // CHECK-SRC: call ptr @malloc( + return malloc(size); +} + +// CHECK-LABEL: define{{.*}} @excluded_by_src( +void* excluded_by_src(unsigned long size) { + // CHECK-ALLOW: call ptr @__alloc_token_malloc( + // CHECK-FUN: call ptr @__alloc_token_malloc( + // CHECK-SRC: call ptr @malloc( + return malloc(size); +} diff --git a/clang/test/CodeGen/alloc-token.c b/clang/test/CodeGen/alloc-token.c new file mode 100644 index 0000000000000..de9b3f48c995f --- /dev/null +++ b/clang/test/CodeGen/alloc-token.c @@ -0,0 +1,45 @@ +// RUN: %clang_cc1 -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s + +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); +int posix_memalign(void **memptr, size_t alignment, size_t size); + +void *sink; + +// CHECK-LABEL: @test_malloc_like( +void test_malloc_like() { + // FIXME: Should not be token ID 0! Currently fail to infer the type. + // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0) + sink = malloc(sizeof(int)); + // CHECK: call{{.*}} ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0) + sink = calloc(3, sizeof(int)); + // CHECK: call{{.*}} ptr @__alloc_token_realloc(ptr noundef {{[^,]*}}, i64 noundef 8, i64 0) + sink = realloc(sink, sizeof(long)); + // CHECK: call{{.*}} ptr @__alloc_token_reallocarray(ptr noundef {{[^,]*}}, i64 noundef 5, i64 noundef 8, i64 0) + sink = reallocarray(sink, 5, sizeof(long)); + // CHECK: call{{.*}} i32 @__alloc_token_posix_memalign(ptr noundef {{[^,]*}}, i64 noundef 64, i64 noundef 4, i64 0) + posix_memalign(&sink, 64, sizeof(int)); + // CHECK: call align 128{{.*}} ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0) + sink = aligned_alloc(128, 1024); + // CHECK: call align 16{{.*}} ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0) + sink = memalign(16, 256); + // CHECK: call{{.*}} ptr @__alloc_token_valloc(i64 noundef 4096, i64 0) + sink = valloc(4096); + // CHECK: call{{.*}} ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0) + sink = pvalloc(8192); +} + +// CHECK-LABEL: @no_sanitize_malloc( +void *no_sanitize_malloc(size_t size) __attribute__((no_sanitize("alloc-token"))) { + // CHECK: call ptr @malloc( + return malloc(size); +} diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp new file mode 100644 index 0000000000000..180c771e43ae9 --- /dev/null +++ b/clang/test/CodeGenCXX/alloc-token.cpp @@ -0,0 +1,157 @@ +// RUN: %clang_cc1 -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s + +#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); +int posix_memalign(void **memptr, size_t alignment, size_t size); + +struct __sized_ptr_t { + void *p; + size_t n; +}; +enum class __hot_cold_t : uint8_t; +__sized_ptr_t __size_returning_new(size_t size); +__sized_ptr_t __size_returning_new_hot_cold(size_t, __hot_cold_t); +__sized_ptr_t __size_returning_new_aligned(size_t, std::align_val_t); +__sized_ptr_t __size_returning_new_aligned_hot_cold(size_t, std::align_val_t, __hot_cold_t); +} + +void *sink; // prevent optimizations from removing the calls + +// CHECK-LABEL: @_Z16test_malloc_likev( +void test_malloc_like() { + // FIXME: Should not be token ID 0! Currently fail to infer the type. + // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0) + sink = malloc(sizeof(int)); + // CHECK: call{{.*}} ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0) + sink = calloc(3, sizeof(int)); + // CHECK: call{{.*}} ptr @__alloc_token_realloc(ptr noundef {{[^,]*}}, i64 noundef 8, i64 0) + sink = realloc(sink, sizeof(long)); + // CHECK: call{{.*}} ptr @__alloc_token_reallocarray(ptr noundef {{[^,]*}}, i64 noundef 5, i64 noundef 8, i64 0) + sink = reallocarray(sink, 5, sizeof(long)); + // CHECK: call{{.*}} i32 @__alloc_token_posix_memalign(ptr noundef {{[^,]*}}, i64 noundef 64, i64 noundef 4, i64 0) + posix_memalign(&sink, 64, sizeof(int)); + // CHECK: call align 128{{.*}} ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0) + sink = aligned_alloc(128, 1024); + // CHECK: call{{.*}} ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0) + sink = memalign(16, 256); + // CHECK: call{{.*}} ptr @__alloc_token_valloc(i64 noundef 4096, i64 0) + sink = valloc(4096); + // CHECK: call{{.*}} ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0) + sink = pvalloc(8192); +} + +// CHECK-LABEL: @_Z17test_operator_newv( +void test_operator_new() { + // FIXME: This should not be token ID 0! + // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0) + sink = __builtin_operator_new(sizeof(int)); + // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0) + sink = ::operator new(sizeof(int)); +} + +// CHECK-LABEL: @_Z25test_operator_new_nothrowv( +void test_operator_new_nothrow() { + // FIXME: This should not be token ID 0! + // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0) + sink = __builtin_operator_new(sizeof(int), std::nothrow); + // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0) + sink = ::operator new(sizeof(int), std::nothrow); +} + +// CHECK-LABEL: @_Z8test_newv( +int *test_new() { + // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + return new int; +} + +// CHECK-LABEL: @_Z14test_new_arrayv( +int *test_new_array() { + // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + return new int[10]; +} + +// CHECK-LABEL: @_Z16test_new_nothrowv( +int *test_new_nothrow() { + // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + return new (std::nothrow) int; +} + +// CHECK-LABEL: @_Z22test_new_array_nothrowv( +int *test_new_array_nothrow() { + // CHECK: call {{.*}} ptr @__alloc_token_ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + return new (std::nothrow) int[10]; +} + +// CHECK-LABEL: @_Z15no_sanitize_newv( +__attribute__((no_sanitize("alloc-token"))) int *no_sanitize_new() { + // CHECK: call {{.*}} ptr @_Znwm(i64 noundef 4) + return new int; +} + +// CHECK-LABEL: @_Z23test_size_returning_newv( +void test_size_returning_new() { + // FIXME: This should not be token ID 0! + // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new(i64 noundef 8, i64 0) + // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1, i64 0) + // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_aligned(i64 noundef 8, i64 noundef 32, i64 0) + // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1, i64 0) + 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; + sink = __size_returning_new_aligned_hot_cold(sizeof(long), std::align_val_t{32}, __hot_cold_t{1}).p; +} + +class TestClass { +public: + virtual void Foo(); + virtual ~TestClass(); + int data[16]; +}; + +void may_throw(); + +// CHECK-LABEL: @_Z27test_exception_handling_newv( +TestClass *test_exception_handling_new() { + try { + // CHECK: invoke {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}) + // CHECK-NEXT: !alloc_token_hint + TestClass *obj = new TestClass(); + may_throw(); + return obj; + } catch (...) { + return nullptr; + } +} + +// CHECK-LABEL: @_Z14test_new_classv( +TestClass *test_new_class() { + // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + TestClass *obj = new TestClass(); + obj->data[0] = 42; + return obj; +} + +// CHECK-LABEL: @_Z20test_new_class_arrayv( +TestClass *test_new_class_array() { + // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + TestClass* arr = new TestClass[10]; + arr[0].data[0] = 123; + return arr; +} + +// CHECK-LABEL: @_Z21test_delete_unchangedPiS_( +void test_delete_unchanged(int *x, int *y) { + // CHECK: call void @_ZdlPvm + // CHECK: call void @_ZdaPv + delete x; + delete [] y; +} diff --git a/clang/test/Driver/fsanitize-alloc-token.c b/clang/test/Driver/fsanitize-alloc-token.c new file mode 100644 index 0000000000000..2964f60c4f26f --- /dev/null +++ b/clang/test/Driver/fsanitize-alloc-token.c @@ -0,0 +1,43 @@ +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-TOKEN-ALLOC +// CHECK-TOKEN-ALLOC: "-fsanitize=alloc-token" + +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fno-sanitize=alloc-token %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-NO-TOKEN-ALLOC +// CHECK-NO-TOKEN-ALLOC-NOT: "-fsanitize=alloc-token" + +// RUN: %clang --target=x86_64-linux-gnu -flto -fvisibility=hidden -fno-sanitize-ignorelist -fsanitize=alloc-token,undefined,cfi %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-COMPATIBLE +// CHECK-COMPATIBLE: "-fsanitize={{.*}}alloc-token" + +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-minimal-runtime %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-MINIMAL +// CHECK-MINIMAL: "-fsanitize=alloc-token" +// CHECK-MINIMAL: "-fsanitize-minimal-runtime" + +// RUN: %clang --target=arm-arm-non-eabi -fsanitize=alloc-token %s -### 2>&1 | FileCheck %s -check-prefix=CHECK-BAREMETAL +// RUN: %clang --target=aarch64-none-elf -fsanitize=alloc-token %s -### 2>&1 | FileCheck %s -check-prefix=CHECK-BAREMETAL +// CHECK-BAREMETAL: "-fsanitize=alloc-token" + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=alloc-token,address %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE-ADDRESS +// CHECK-INCOMPATIBLE-ADDRESS: error: invalid argument '-fsanitize=alloc-token' not allowed with '-fsanitize=address' + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=alloc-token,memory %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE-MEMORY +// CHECK-INCOMPATIBLE-MEMORY: error: invalid argument '-fsanitize=alloc-token' not allowed with '-fsanitize=memory' + +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-trap=alloc-token %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE-TRAP +// CHECK-INCOMPATIBLE-TRAP: error: unsupported argument 'alloc-token' to option '-fsanitize-trap=' + +// RUN: not %clang --target=x86_64-linux-gnu %s -fsanitize=alloc-token -fsanitize-recover=alloc-token -### 2>&1 | FileCheck %s --check-prefix=CHECK-INCOMPATIBLE-RECOVER +// CHECK-INCOMPATIBLE-RECOVER: unsupported argument 'alloc-token' to option '-fsanitize-recover=' + +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-alloc-token-fast-abi %s -### 2>&1 | FileCheck -check-prefix=CHECK-FASTABI %s +// CHECK-FASTABI: "-fsanitize-alloc-token-fast-abi" +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-alloc-token-fast-abi -fno-sanitize-alloc-token-fast-abi %s -### 2>&1 | FileCheck -check-prefix=CHECK-NOFASTABI %s +// CHECK-NOFASTABI-NOT: "-fsanitize-alloc-token-fast-abi" + +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-alloc-token-extended %s -### 2>&1 | FileCheck -check-prefix=CHECK-EXTENDED %s +// CHECK-EXTENDED: "-fsanitize-alloc-token-extended" +// RUN: %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -fsanitize-alloc-token-extended -fno-sanitize-alloc-token-extended %s -### 2>&1 | FileCheck -check-prefix=CHECK-NOEXTENDED %s +// CHECK-NOEXTENDED-NOT: "-fsanitize-alloc-token-extended" + +// RUN: %clang --target=x86_64-linux-gnu -falloc-token-max=0 -falloc-token-max=42 %s -### 2>&1 | FileCheck -check-prefix=CHECK-MAX %s +// CHECK-MAX: "-falloc-token-max=42" +// RUN: not %clang --target=x86_64-linux-gnu -fsanitize=alloc-token -falloc-token-max=-1 %s 2>&1 | FileCheck -check-prefix=CHECK-INVALID-MAX %s +// CHECK-INVALID-MAX: error: invalid value diff --git a/clang/test/Preprocessor/alloc_token.cpp b/clang/test/Preprocessor/alloc_token.cpp new file mode 100644 index 0000000000000..0c51bfb9405f2 --- /dev/null +++ b/clang/test/Preprocessor/alloc_token.cpp @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -E -fsanitize=alloc-token %s -o - | FileCheck --check-prefix=CHECK-SANITIZE %s +// RUN: %clang_cc1 -E %s -o - | FileCheck --check-prefix=CHECK-DEFAULT %s + +#if __SANITIZE_ALLOC_TOKEN__ +// CHECK-SANITIZE: has_sanitize_alloc_token +int has_sanitize_alloc_token(); +#else +// CHECK-DEFAULT: no_sanitize_alloc_token +int no_sanitize_alloc_token(); +#endif diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index e64b9343b7622..4791527a4b86b 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -2427,6 +2427,9 @@ For example: if the attributed function is called during invocation of a function attributed with ``sanitize_realtime``. This attribute is incompatible with the ``sanitize_realtime`` attribute. +``sanitize_alloc_token`` + This attributes indicates that implicit allocation token instrumentation + is enabled for this function. ``speculative_load_hardening`` This attribute indicates that `Speculative Load Hardening `_ diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index ff92d7390ecfd..eae9a73bedc34 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -166,6 +166,10 @@ Changes to Sanitizers Other Changes ------------- +* Introduces the `AllocToken` pass, an instrumentation pass designed to provide + tokens to memory allocators enabling various heap organization strategies, + such as heap partitioning. + External Open Source Projects Using LLVM {{env.config.release}} =============================================================== diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index 1c7d3462b6bae..464f475098ec5 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -800,6 +800,7 @@ enum AttributeKindCodes { ATTR_KIND_SANITIZE_TYPE = 101, ATTR_KIND_CAPTURES = 102, ATTR_KIND_DEAD_ON_RETURN = 103, + ATTR_KIND_SANITIZE_ALLOC_TOKEN = 104, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td index ef816fb86ed1d..8e7d9dcebfe2a 100644 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -342,6 +342,9 @@ def SanitizeRealtime : EnumAttr<"sanitize_realtime", IntersectPreserve, [FnAttr] /// during a real-time sanitized function (see `sanitize_realtime`). def SanitizeRealtimeBlocking : EnumAttr<"sanitize_realtime_blocking", IntersectPreserve, [FnAttr]>; +/// Allocation token instrumentation is on. +def SanitizeAllocToken : EnumAttr<"sanitize_alloc_token", IntersectPreserve, [FnAttr]>; + /// Speculative Load Hardening is enabled. /// /// Note that this uses the default compatibility (always compatible during diff --git a/llvm/include/llvm/Transforms/Instrumentation/AllocToken.h b/llvm/include/llvm/Transforms/Instrumentation/AllocToken.h new file mode 100644 index 0000000000000..b1391cb04302c --- /dev/null +++ b/llvm/include/llvm/Transforms/Instrumentation/AllocToken.h @@ -0,0 +1,46 @@ +//===- AllocToken.h - Allocation token instrumentation --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file declares the AllocTokenPass, an instrumentation pass that +// replaces allocation calls with ones including an allocation token. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_TRANSFORMS_INSTRUMENTATION_ALLOCTOKEN_H +#define LLVM_TRANSFORMS_INSTRUMENTATION_ALLOCTOKEN_H + +#include "llvm/IR/Analysis.h" +#include "llvm/IR/PassManager.h" +#include + +namespace llvm { + +class Module; + +struct AllocTokenOptions { + std::optional MaxTokens; + bool FastABI = false; + bool Extended = false; + AllocTokenOptions() = default; +}; + +/// A module pass that rewrites heap allocations to use token-enabled +/// allocation functions based on various source-level properties. +class AllocTokenPass : public PassInfoMixin { +public: + LLVM_ABI explicit AllocTokenPass(AllocTokenOptions Opts = {}); + LLVM_ABI PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM); + static bool isRequired() { return true; } + +private: + const AllocTokenOptions Options; +}; + +} // namespace llvm + +#endif // LLVM_TRANSFORMS_INSTRUMENTATION_ALLOCTOKEN_H diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index 22a0d0ffdbaab..67ad4a2655ecd 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -2203,6 +2203,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) { return Attribute::SanitizeRealtime; case bitc::ATTR_KIND_SANITIZE_REALTIME_BLOCKING: return Attribute::SanitizeRealtimeBlocking; + case bitc::ATTR_KIND_SANITIZE_ALLOC_TOKEN: + return Attribute::SanitizeAllocToken; case bitc::ATTR_KIND_SPECULATIVE_LOAD_HARDENING: return Attribute::SpeculativeLoadHardening; case bitc::ATTR_KIND_SWIFT_ERROR: diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index a1d5b36bde64d..742c1836c32cd 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -883,6 +883,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) { return bitc::ATTR_KIND_STRUCT_RET; case Attribute::SanitizeAddress: return bitc::ATTR_KIND_SANITIZE_ADDRESS; + case Attribute::SanitizeAllocToken: + return bitc::ATTR_KIND_SANITIZE_ALLOC_TOKEN; case Attribute::SanitizeHWAddress: return bitc::ATTR_KIND_SANITIZE_HWADDRESS; case Attribute::SanitizeThread: diff --git a/llvm/lib/Passes/PassBuilder.cpp b/llvm/lib/Passes/PassBuilder.cpp index d75304b5e11f6..221c6a6363613 100644 --- a/llvm/lib/Passes/PassBuilder.cpp +++ b/llvm/lib/Passes/PassBuilder.cpp @@ -237,6 +237,7 @@ #include "llvm/Transforms/IPO/WholeProgramDevirt.h" #include "llvm/Transforms/InstCombine/InstCombine.h" #include "llvm/Transforms/Instrumentation/AddressSanitizer.h" +#include "llvm/Transforms/Instrumentation/AllocToken.h" #include "llvm/Transforms/Instrumentation/BoundsChecking.h" #include "llvm/Transforms/Instrumentation/CGProfile.h" #include "llvm/Transforms/Instrumentation/ControlHeightReduction.h" diff --git a/llvm/lib/Passes/PassRegistry.def b/llvm/lib/Passes/PassRegistry.def index 4b462b9c6845c..00896b2a8f3fc 100644 --- a/llvm/lib/Passes/PassRegistry.def +++ b/llvm/lib/Passes/PassRegistry.def @@ -124,6 +124,7 @@ MODULE_PASS("openmp-opt", OpenMPOptPass()) MODULE_PASS("openmp-opt-postlink", OpenMPOptPass(ThinOrFullLTOPhase::FullLTOPostLink)) MODULE_PASS("partial-inliner", PartialInlinerPass()) +MODULE_PASS("alloc-token", AllocTokenPass()) MODULE_PASS("pgo-icall-prom", PGOIndirectCallPromotion()) MODULE_PASS("pgo-instr-gen", PGOInstrumentationGen()) MODULE_PASS("pgo-instr-use", PGOInstrumentationUse()) diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp new file mode 100644 index 0000000000000..f0f7f14448dc8 --- /dev/null +++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp @@ -0,0 +1,484 @@ +//===- AllocToken.cpp - Allocation token instrumentation ------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file implements AllocToken, an instrumentation pass that +// replaces allocation calls with token-enabled versions. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Instrumentation/AllocToken.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Analysis/MemoryBuiltins.h" +#include "llvm/Analysis/OptimizationRemarkEmitter.h" +#include "llvm/Analysis/TargetLibraryInfo.h" +#include "llvm/IR/Analysis.h" +#include "llvm/IR/Attributes.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/DerivedTypes.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/GlobalValue.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Metadata.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" +#include "llvm/IR/Type.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/xxhash.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace llvm; + +#define DEBUG_TYPE "alloc-token" + +namespace { + +//===--- Constants --------------------------------------------------------===// + +enum class TokenMode : unsigned { + /// Incrementally increasing token ID. + Increment = 0, + + /// Simple mode that returns a statically-assigned random token ID. + Random = 1, + + /// Token ID based on allocated type hash. + TypeHash = 2, + + // Mode count - keep last + ModeCount +}; + +//===--- Command-line options ---------------------------------------------===// + +struct ModeParser : public cl::parser { + ModeParser(cl::Option &O) : cl::parser(O) {} + bool parse(cl::Option &O, StringRef ArgName, StringRef Arg, unsigned &Value) { + if (cl::parser::parse(O, ArgName, Arg, Value)) + return true; + if (Value >= static_cast(TokenMode::ModeCount)) + return O.error("'" + Arg + "' value invalid"); + return false; + } +}; + +cl::opt + ClMode("alloc-token-mode", cl::desc("Token assignment mode"), cl::Hidden, + cl::init(static_cast(TokenMode::TypeHash))); + +cl::opt ClFuncPrefix("alloc-token-prefix", + cl::desc("The allocation function prefix"), + cl::Hidden, cl::init("__alloc_token_")); + +cl::opt ClMaxTokens("alloc-token-max", + cl::desc("Maximum number of tokens (0 = no max)"), + cl::Hidden, cl::init(0)); + +cl::opt + ClFastABI("alloc-token-fast-abi", + cl::desc("The token ID is encoded in the function name"), + cl::Hidden, cl::init(false)); + +// Instrument libcalls only by default - compatible allocators only need to take +// care of providing standard allocation functions. With extended coverage, also +// instrument non-libcall allocation function calls with !alloc_token_hint +// metadata. +cl::opt + ClExtended("alloc-token-extended", + cl::desc("Extend coverage to custom allocation functions"), + cl::Hidden, cl::init(false)); + +// C++ defines ::operator new (and variants) as replaceable (vs. standard +// library versions), which are nobuiltin, and are therefore not covered by +// isAllocationFn(). Cover by default, as users of AllocToken are already +// required to provide token-aware allocation functions (no defaults). +cl::opt ClCoverReplaceableNew("alloc-token-cover-replaceable-new", + cl::desc("Cover replaceable operator new"), + cl::Hidden, cl::init(true)); + +// strdup-family functions only operate on strings, covering them does not make +// sense in most cases. +cl::opt + ClCoverStrdup("alloc-token-cover-strdup", + cl::desc("Cover strdup-family allocation functions"), + cl::Hidden, cl::init(false)); + +cl::opt ClFallbackToken( + "alloc-token-fallback", + cl::desc("The default fallback token where none could be determined"), + cl::Hidden, cl::init(0)); + +//===--- Statistics -------------------------------------------------------===// + +STATISTIC(NumFunctionsInstrumented, "Functions instrumented"); +STATISTIC(NumAllocations, "Allocations found"); + +//===----------------------------------------------------------------------===// + +/// Returns the !alloc_token_hint metadata if available. +/// +/// Expected format is: !{} +MDNode *getAllocTokenHintMetadata(const CallBase &CB) { + MDNode *Ret = CB.getMetadata("alloc_token_hint"); + if (!Ret) + return nullptr; + assert(Ret->getNumOperands() == 1 && "bad !alloc_token_hint"); + assert(isa(Ret->getOperand(0))); + return Ret; +} + +class ModeBase { +public: + explicit ModeBase(uint64_t MaxTokens) : MaxTokens(MaxTokens) {} + +protected: + uint64_t boundedToken(uint64_t Val) const { + return MaxTokens ? Val % MaxTokens : Val; + } + + const uint64_t MaxTokens; +}; + +/// Implementation for TokenMode::Increment. +class IncrementMode : public ModeBase { +public: + using ModeBase::ModeBase; + + uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &) { + return boundedToken(Counter++); + } + +private: + uint64_t Counter = 0; +}; + +/// Implementation for TokenMode::Random. +class RandomMode : public ModeBase { +public: + RandomMode(uint64_t MaxTokens, std::unique_ptr RNG) + : ModeBase(MaxTokens), RNG(std::move(RNG)) {} + uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &) { + return boundedToken((*RNG)()); + } + +private: + std::unique_ptr RNG; +}; + +/// Implementation for TokenMode::TypeHash. The implementation ensures +/// hashes are stable across different compiler invocations. Uses xxHash as the +/// hash function. +class TypeHashMode : public ModeBase { +public: + using ModeBase::ModeBase; + + uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &ORE) { + if (MDNode *N = getAllocTokenHintMetadata(CB)) { + MDString *S = cast(N->getOperand(0)); + return boundedToken(xxHash64(S->getString())); + } + remarkNoHint(CB, ORE); + return ClFallbackToken; + } + + /// Remark that there was no precise type information. + void remarkNoHint(const CallBase &CB, OptimizationRemarkEmitter &ORE) { + ORE.emit([&] { + ore::NV FuncNV("Function", CB.getParent()->getParent()); + const Function *Callee = CB.getCalledFunction(); + ore::NV CalleeNV("Callee", Callee ? Callee->getName() : ""); + return OptimizationRemark(DEBUG_TYPE, "NoAllocTokenHint", &CB) + << "Call to '" << CalleeNV << "' in '" << FuncNV + << "' without source-level type token"; + }); + } +}; + +// Apply opt overrides. +AllocTokenOptions &&transformOptionsFromCl(AllocTokenOptions &&Opts) { + if (!Opts.MaxTokens.has_value()) + Opts.MaxTokens = ClMaxTokens; + Opts.FastABI |= ClFastABI; + Opts.Extended |= ClExtended; + return std::move(Opts); +} + +class AllocToken { +public: + explicit AllocToken(AllocTokenOptions Opts, Module &M, + ModuleAnalysisManager &MAM) + : Options(transformOptionsFromCl(std::move(Opts))), Mod(M), + FAM(MAM.getResult(M).getManager()), + Mode(IncrementMode(*Options.MaxTokens)) { + switch (static_cast(ClMode.getValue())) { + case TokenMode::Increment: + break; + case TokenMode::Random: + Mode.emplace(*Options.MaxTokens, M.createRNG(DEBUG_TYPE)); + break; + case TokenMode::TypeHash: + Mode.emplace(*Options.MaxTokens); + break; + case TokenMode::ModeCount: + llvm_unreachable(""); + break; + } + } + + bool instrumentFunction(Function &F); + +private: + /// Returns true for !isAllocationFn() functions that are also eligible for + /// instrumentation. + bool isInstrumentableLibFunc(LibFunc Func) const; + + /// Returns true for isAllocationFn() functions that we should ignore. + bool ignoreInstrumentableLibFunc(LibFunc Func) const; + + /// Replace a call/invoke with a call/invoke to the allocation function + /// with token ID. + void replaceAllocationCall(CallBase *CB, LibFunc Func, + OptimizationRemarkEmitter &ORE, + const TargetLibraryInfo &TLI); + + /// Return replacement function for a LibFunc that takes a token ID. + FunctionCallee getTokenAllocFunction(const CallBase &CB, uint64_t TokenID, + LibFunc OriginalFunc); + + /// Return the token ID from metadata in the call. + uint64_t getToken(const CallBase &CB, OptimizationRemarkEmitter &ORE) { + return std::visit([&](auto &&Mode) { return Mode(CB, ORE); }, Mode); + } + + const AllocTokenOptions Options; + Module &Mod; + FunctionAnalysisManager &FAM; + // Cache for replacement functions. + DenseMap, FunctionCallee> TokenAllocFunctions; + // Selected mode. + std::variant Mode; +}; + +bool AllocToken::instrumentFunction(Function &F) { + // Do not apply any instrumentation for naked functions. + if (F.hasFnAttribute(Attribute::Naked)) + return false; + if (F.hasFnAttribute(Attribute::DisableSanitizerInstrumentation)) + return false; + // Don't touch available_externally functions, their actual body is elsewhere. + if (F.getLinkage() == GlobalValue::AvailableExternallyLinkage) + return false; + // Only instrument functions that have the sanitize_alloc_token attribute. + if (!F.hasFnAttribute(Attribute::SanitizeAllocToken)) + return false; + + auto &ORE = FAM.getResult(F); + auto &TLI = FAM.getResult(F); + SmallVector, 4> AllocCalls; + + // Collect all allocation calls to avoid iterator invalidation. + for (Instruction &I : instructions(F)) { + auto *CB = dyn_cast(&I); + if (!CB) + continue; + const Function *Callee = CB->getCalledFunction(); + if (!Callee) + continue; + // Ignore nobuiltin of the CallBase, so that we can cover nobuiltin libcalls + // if requested via isInstrumentableLibFunc(). Note that isAllocationFn() is + // returning false for nobuiltin calls. + LibFunc Func; + if (TLI.getLibFunc(*Callee, Func)) { + if (ignoreInstrumentableLibFunc(Func)) + continue; + if (isInstrumentableLibFunc(Func) || isAllocationFn(CB, &TLI)) + AllocCalls.emplace_back(CB, Func); + } else if (Options.Extended && getAllocTokenHintMetadata(*CB)) { + AllocCalls.emplace_back(CB, NotLibFunc); + } + } + + bool Modified = false; + + if (!AllocCalls.empty()) { + for (auto &[CB, Func] : AllocCalls) { + replaceAllocationCall(CB, Func, ORE, TLI); + } + NumAllocations += AllocCalls.size(); + NumFunctionsInstrumented++; + Modified = true; + } + + return Modified; +} + +bool AllocToken::isInstrumentableLibFunc(LibFunc Func) const { + switch (Func) { + case LibFunc_posix_memalign: + case LibFunc_size_returning_new: + case LibFunc_size_returning_new_hot_cold: + case LibFunc_size_returning_new_aligned: + case LibFunc_size_returning_new_aligned_hot_cold: + return true; + case LibFunc_Znwj: + case LibFunc_ZnwjRKSt9nothrow_t: + case LibFunc_ZnwjSt11align_val_t: + case LibFunc_ZnwjSt11align_val_tRKSt9nothrow_t: + case LibFunc_Znwm: + case LibFunc_Znwm12__hot_cold_t: + case LibFunc_ZnwmRKSt9nothrow_t: + case LibFunc_ZnwmRKSt9nothrow_t12__hot_cold_t: + case LibFunc_ZnwmSt11align_val_t: + case LibFunc_ZnwmSt11align_val_t12__hot_cold_t: + case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t: + case LibFunc_ZnwmSt11align_val_tRKSt9nothrow_t12__hot_cold_t: + case LibFunc_Znaj: + case LibFunc_ZnajRKSt9nothrow_t: + case LibFunc_ZnajSt11align_val_t: + case LibFunc_ZnajSt11align_val_tRKSt9nothrow_t: + case LibFunc_Znam: + case LibFunc_Znam12__hot_cold_t: + case LibFunc_ZnamRKSt9nothrow_t: + case LibFunc_ZnamRKSt9nothrow_t12__hot_cold_t: + case LibFunc_ZnamSt11align_val_t: + case LibFunc_ZnamSt11align_val_t12__hot_cold_t: + case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t: + case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t: + return ClCoverReplaceableNew; + default: + return false; + } +} + +bool AllocToken::ignoreInstrumentableLibFunc(LibFunc Func) const { + switch (Func) { + case LibFunc_strdup: + case LibFunc_dunder_strdup: + case LibFunc_strndup: + case LibFunc_dunder_strndup: + return !ClCoverStrdup; + default: + return false; + } +} + +void AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func, + OptimizationRemarkEmitter &ORE, + const TargetLibraryInfo &TLI) { + uint64_t TokenID = getToken(*CB, ORE); + + FunctionCallee TokenAlloc = getTokenAllocFunction(*CB, TokenID, Func); + if (!TokenAlloc) + return; + + IRBuilder<> IRB(CB); + + // Original args. + SmallVector NewArgs{CB->args()}; + if (!Options.FastABI) { + // Add token ID. + NewArgs.push_back( + ConstantInt::get(Type::getInt64Ty(Mod.getContext()), TokenID)); + } + assert(TokenAlloc.getFunctionType()->getNumParams() == NewArgs.size()); + + // Preserve invoke vs call semantics for exception handling. + CallBase *NewCall; + if (auto *II = dyn_cast(CB)) { + NewCall = IRB.CreateInvoke(TokenAlloc, II->getNormalDest(), + II->getUnwindDest(), NewArgs); + } else { + NewCall = IRB.CreateCall(TokenAlloc, NewArgs); + cast(NewCall)->setTailCall(CB->isTailCall()); + } + NewCall->setCallingConv(CB->getCallingConv()); + NewCall->copyMetadata(*CB); + NewCall->setAttributes(CB->getAttributes()); + + // Replace all uses and delete the old call. + CB->replaceAllUsesWith(NewCall); + CB->eraseFromParent(); +} + +FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB, + uint64_t TokenID, + LibFunc OriginalFunc) { + std::optional> Key; + if (OriginalFunc != NotLibFunc) { + Key = std::make_pair(OriginalFunc, Options.FastABI ? TokenID : 0); + auto It = TokenAllocFunctions.find(*Key); + if (LLVM_LIKELY(It != TokenAllocFunctions.end())) + return It->second; + } + + const Function *Callee = CB.getCalledFunction(); + if (!Callee) + return FunctionCallee(); + const FunctionType *OldFTy = Callee->getFunctionType(); + if (OldFTy->isVarArg()) + return FunctionCallee(); + // Copy params, and append token ID type. + LLVMContext &C = Mod.getContext(); + Type *RetTy = OldFTy->getReturnType(); + SmallVector NewParams{OldFTy->params()}; + std::string TokenAllocName = ClFuncPrefix; + if (Options.FastABI) { + TokenAllocName += utostr(TokenID) + "_"; + } else { + NewParams.push_back(Type::getInt64Ty(C)); // token ID + } + FunctionType *NewFTy = FunctionType::get(RetTy, NewParams, false); + // Remove leading '_' - we add our own. + StringRef No_ = Callee->getName().drop_while([](char C) { return C == '_'; }); + TokenAllocName += No_; + FunctionCallee TokenAlloc = Mod.getOrInsertFunction(TokenAllocName, NewFTy); + if (Function *F = dyn_cast(TokenAlloc.getCallee())) + F->copyAttributesFrom(Callee); // preserve attrs + + if (Key.has_value()) + TokenAllocFunctions[*Key] = TokenAlloc; + return TokenAlloc; +} + +} // namespace + +AllocTokenPass::AllocTokenPass(AllocTokenOptions Opts) + : Options(std::move(Opts)) {} + +PreservedAnalyses AllocTokenPass::run(Module &M, ModuleAnalysisManager &MAM) { + AllocToken Pass(Options, M, MAM); + bool Modified = false; + + for (Function &F : M) { + if (LLVM_LIKELY(F.empty())) + continue; // declaration + Modified |= Pass.instrumentFunction(F); + } + + return Modified ? PreservedAnalyses::none() : PreservedAnalyses::all(); +} diff --git a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt index 15fd421a41b0f..80576c61fd80c 100644 --- a/llvm/lib/Transforms/Instrumentation/CMakeLists.txt +++ b/llvm/lib/Transforms/Instrumentation/CMakeLists.txt @@ -1,5 +1,6 @@ add_llvm_component_library(LLVMInstrumentation AddressSanitizer.cpp + AllocToken.cpp BoundsChecking.cpp CGProfile.cpp ControlHeightReduction.cpp diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp index bbd1ed6a3ab2d..5ba6f95f5fae8 100644 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -970,6 +970,7 @@ Function *CodeExtractor::constructFunctionDeclaration( case Attribute::SanitizeMemTag: case Attribute::SanitizeRealtime: case Attribute::SanitizeRealtimeBlocking: + case Attribute::SanitizeAllocToken: case Attribute::SpeculativeLoadHardening: case Attribute::StackProtect: case Attribute::StackProtectReq: diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll index 8c1a76365e1b4..aef7810fe2c3b 100644 --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -516,6 +516,11 @@ define void @f93() sanitize_realtime_blocking { ret void; } +; CHECK: define void @f_sanitize_alloc_token() #55 +define void @f_sanitize_alloc_token() sanitize_alloc_token { + ret void; +} + ; CHECK: define void @f87() [[FNRETTHUNKEXTERN:#[0-9]+]] define void @f87() fn_ret_thunk_extern { ret void } @@ -627,6 +632,7 @@ define void @dead_on_return(ptr dead_on_return %p) { ; CHECK: attributes #52 = { nosanitize_bounds } ; CHECK: attributes #53 = { sanitize_realtime } ; CHECK: attributes #54 = { sanitize_realtime_blocking } +; CHECK: attributes #55 = { sanitize_alloc_token } ; CHECK: attributes [[FNRETTHUNKEXTERN]] = { fn_ret_thunk_extern } ; CHECK: attributes [[SKIPPROFILE]] = { skipprofile } ; CHECK: attributes [[OPTDEBUG]] = { optdebug } diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll index 0b5ce08c00a23..e21786e5ee330 100644 --- a/llvm/test/Bitcode/compatibility.ll +++ b/llvm/test/Bitcode/compatibility.ll @@ -1718,7 +1718,7 @@ exit: ; CHECK: select <2 x i1> , <2 x i8> , <2 x i8> call void @f.nobuiltin() builtin - ; CHECK: call void @f.nobuiltin() #54 + ; CHECK: call void @f.nobuiltin() #55 call fastcc noalias ptr @f.noalias() noinline ; CHECK: call fastcc noalias ptr @f.noalias() #12 @@ -2151,6 +2151,9 @@ declare void @f.sanitize_realtime() sanitize_realtime declare void @f.sanitize_realtime_blocking() sanitize_realtime_blocking ; CHECK: declare void @f.sanitize_realtime_blocking() #53 +declare void @f.sanitize_alloc_token() sanitize_alloc_token +; CHECK: declare void @f.sanitize_alloc_token() #54 + ; CHECK: declare nofpclass(snan) float @nofpclass_snan(float nofpclass(snan)) declare nofpclass(snan) float @nofpclass_snan(float nofpclass(snan)) @@ -2284,7 +2287,8 @@ define float @nofpclass_callsites(float %arg, { float } %arg1) { ; CHECK: attributes #51 = { sanitize_numerical_stability } ; CHECK: attributes #52 = { sanitize_realtime } ; CHECK: attributes #53 = { sanitize_realtime_blocking } -; CHECK: attributes #54 = { builtin } +; CHECK: attributes #54 = { sanitize_alloc_token } +; CHECK: attributes #55 = { builtin } ;; Metadata diff --git a/llvm/test/Instrumentation/AllocToken/basic.ll b/llvm/test/Instrumentation/AllocToken/basic.ll new file mode 100644 index 0000000000000..94f5ef7ac5511 --- /dev/null +++ b/llvm/test/Instrumentation/AllocToken/basic.ll @@ -0,0 +1,84 @@ +; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -S | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare ptr @malloc(i64) +declare ptr @calloc(i64, i64) +declare ptr @realloc(ptr, i64) +declare ptr @_Znwm(i64) +declare ptr @_Znam(i64) +declare void @free(ptr) +declare void @_ZdlPv(ptr) +declare i32 @foobar(i64) + +; Test basic allocation call rewriting +; CHECK-LABEL: @test_basic_rewriting +define ptr @test_basic_rewriting() sanitize_alloc_token { +entry: + ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_malloc(i64 64, i64 0) + ; CHECK: call ptr @__alloc_token_calloc(i64 8, i64 8, i64 1) + ; CHECK: call ptr @__alloc_token_realloc(ptr [[PTR1]], i64 128, i64 2) + ; CHECK-NOT: call ptr @malloc( + ; CHECK-NOT: call ptr @calloc( + ; CHECK-NOT: call ptr @realloc( + %ptr1 = call ptr @malloc(i64 64) + %ptr2 = call ptr @calloc(i64 8, i64 8) + %ptr3 = call ptr @realloc(ptr %ptr1, i64 128) + ret ptr %ptr3 +} + +; Test C++ operator rewriting +; CHECK-LABEL: @test_cpp_operators +define ptr @test_cpp_operators() sanitize_alloc_token { +entry: + ; CHECK: call ptr @__alloc_token_Znwm(i64 32, i64 3) + ; CHECK: call ptr @__alloc_token_Znam(i64 64, i64 4) + ; CHECK-NOT: call ptr @_Znwm( + ; CHECK-NOT: call ptr @_Znam( + %ptr1 = call ptr @_Znwm(i64 32) + %ptr2 = call ptr @_Znam(i64 64) + ret ptr %ptr1 +} + +; Functions without sanitize_alloc_token do not get instrumented +; CHECK-LABEL: @without_attribute +define ptr @without_attribute() { +entry: + ; CHECK: call ptr @malloc(i64 16) + ; CHECK-NOT: call ptr @__alloc_token_malloc + %ptr = call ptr @malloc(i64 16) + ret ptr %ptr +} + +; Test that free/delete are untouched +; CHECK-LABEL: @test_free_untouched +define void @test_free_untouched(ptr %ptr) sanitize_alloc_token { +entry: + ; CHECK: call void @free(ptr %ptr) + ; CHECK: call void @_ZdlPv(ptr %ptr) + ; CHECK-NOT: call ptr @__alloc_token_ + call void @free(ptr %ptr) + call void @_ZdlPv(ptr %ptr) + ret void +} + +; Non-allocation functions are untouched +; CHECK-LABEL: @no_allocations +define i32 @no_allocations(i32 %x) sanitize_alloc_token { +entry: + ; CHECK: call i32 @foobar + ; CHECK-NOT: call i32 @__alloc_token_ + %result = call i32 @foobar(i64 42) + ret i32 %result +} + +; Test that tail calls are preserved +; CHECK-LABEL: @test_tail_call_preserved +define ptr @test_tail_call_preserved() sanitize_alloc_token { +entry: + ; CHECK: tail call ptr @__alloc_token_malloc(i64 42, i64 5) + ; CHECK-NOT: tail call ptr @malloc( + %result = tail call ptr @malloc(i64 42) + ret ptr %result +} diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll new file mode 100644 index 0000000000000..37c6d6463ffd2 --- /dev/null +++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll @@ -0,0 +1,32 @@ +; Test for special libfuncs not automatically considered allocation functions. +; +; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare {ptr, i64} @__size_returning_new(i64) + +; CHECK-LABEL: @test_extra_libfuncs +define ptr @test_extra_libfuncs() sanitize_alloc_token { +entry: + ; CHECK: call {{.*}} @__alloc_token_size_returning_new( + %srn = call {ptr, i64} @__size_returning_new(i64 10), !alloc_token_hint !0 + %ptr1 = extractvalue {ptr, i64} %srn, 0 + ret ptr %ptr1 +} + +declare ptr @_Znwm(i64) nobuiltin allocsize(0) +declare ptr @_Znam(i64) nobuiltin allocsize(0) + +; CHECK-LABEL: @test_replaceable_new +define ptr @test_replaceable_new() sanitize_alloc_token { +entry: + ; CHECK: call ptr @__alloc_token_Znwm( + %ptr1 = call ptr @_Znwm(i64 32), !alloc_token_hint !0 + ; CHECK: call ptr @__alloc_token_Znam( + %ptr2 = call ptr @_Znam(i64 64), !alloc_token_hint !0 + ret ptr %ptr1 +} + +!0 = !{!"int"} diff --git a/llvm/test/Instrumentation/AllocToken/fast.ll b/llvm/test/Instrumentation/AllocToken/fast.ll new file mode 100644 index 0000000000000..c691cdcdc37c6 --- /dev/null +++ b/llvm/test/Instrumentation/AllocToken/fast.ll @@ -0,0 +1,39 @@ +; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -alloc-token-fast-abi -alloc-token-max=3 -S | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare ptr @malloc(i64) +declare ptr @calloc(i64, i64) +declare ptr @realloc(ptr, i64) +declare ptr @_Znwm(i64) +declare ptr @_Znam(i64) + +; Test basic allocation call rewriting +; CHECK-LABEL: @test_basic_rewriting +define ptr @test_basic_rewriting() sanitize_alloc_token { +entry: + ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_0_malloc(i64 64) + ; CHECK: call ptr @__alloc_token_1_calloc(i64 8, i64 8) + ; CHECK: call ptr @__alloc_token_2_realloc(ptr [[PTR1]], i64 128) + ; CHECK-NOT: call ptr @malloc( + ; CHECK-NOT: call ptr @calloc( + ; CHECK-NOT: call ptr @realloc( + %ptr1 = call ptr @malloc(i64 64) + %ptr2 = call ptr @calloc(i64 8, i64 8) + %ptr3 = call ptr @realloc(ptr %ptr1, i64 128) + ret ptr %ptr3 +} + +; Test C++ operator rewriting +; CHECK-LABEL: @test_cpp_operators +define ptr @test_cpp_operators() sanitize_alloc_token { +entry: + ; CHECK: call ptr @__alloc_token_0_Znwm(i64 32) + ; CHECK: call ptr @__alloc_token_1_Znam(i64 64) + ; CHECK-NOT: call ptr @_Znwm( + ; CHECK-NOT: call ptr @_Znam( + %ptr1 = call ptr @_Znwm(i64 32) + %ptr2 = call ptr @_Znam(i64 64) + ret ptr %ptr1 +} diff --git a/llvm/test/Instrumentation/AllocToken/ignore.ll b/llvm/test/Instrumentation/AllocToken/ignore.ll new file mode 100644 index 0000000000000..65921685d70a0 --- /dev/null +++ b/llvm/test/Instrumentation/AllocToken/ignore.ll @@ -0,0 +1,30 @@ +; Test for all allocation functions that should be ignored by default. +; +; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s --check-prefixes=CHECK,DEFAULT +; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-cover-strdup -S | FileCheck %s --check-prefixes=CHECK,COVER + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare ptr @strdup(ptr) +declare ptr @__strdup(ptr) +declare ptr @strndup(ptr, i64) +declare ptr @__strndup(ptr, i64) + +; CHECK-LABEL: @test_ignorable_allocation_functions +define ptr @test_ignorable_allocation_functions(ptr %ptr) sanitize_alloc_token { +entry: + ; COVER: call ptr @__alloc_token_strdup( + ; DEFAULT: call ptr @strdup( + %ptr1 = call ptr @strdup(ptr %ptr) + ; COVER: call ptr @__alloc_token_strdup( + ; DEFAULT: call ptr @__strdup( + %ptr2 = call ptr @__strdup(ptr %ptr) + ; COVER: call ptr @__alloc_token_strndup( + ; DEFAULT: call ptr @strndup( + %ptr3 = call ptr @strndup(ptr %ptr, i64 42) + ; COVER: call ptr @__alloc_token_strndup( + ; DEFAULT: call ptr @__strndup( + %ptr4 = call ptr @__strndup(ptr %ptr, i64 42) + ret ptr %ptr1 +} diff --git a/llvm/test/Instrumentation/AllocToken/invoke.ll b/llvm/test/Instrumentation/AllocToken/invoke.ll new file mode 100644 index 0000000000000..243462a54968f --- /dev/null +++ b/llvm/test/Instrumentation/AllocToken/invoke.ll @@ -0,0 +1,86 @@ +; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -S | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; CHECK-LABEL: @test_invoke_malloc +define ptr @test_invoke_malloc() sanitize_alloc_token personality ptr @__gxx_personality_v0 { +entry: + ; CHECK: invoke ptr @__alloc_token_malloc(i64 64, i64 0) + ; CHECK-NEXT: to label %normal unwind label %cleanup + ; CHECK-NOT: call ptr @__alloc_token_malloc + ; CHECK-NOT: call ptr @malloc + %ptr = invoke ptr @malloc(i64 64) to label %normal unwind label %cleanup + +normal: + ret ptr %ptr + +cleanup: + %lp = landingpad { ptr, i32 } cleanup + ret ptr null +} + +; CHECK-LABEL: @test_invoke_operator_new +define ptr @test_invoke_operator_new() sanitize_alloc_token personality ptr @__gxx_personality_v0 { +entry: + ; CHECK: invoke ptr @__alloc_token_Znwm(i64 32, i64 1) + ; CHECK-NEXT: to label %normal unwind label %cleanup + ; CHECK-NOT: call ptr @__alloc_token_Znwm + ; CHECK-NOT: call ptr @_Znwm + %ptr = invoke ptr @_Znwm(i64 32) to label %normal unwind label %cleanup + +normal: + ret ptr %ptr + +cleanup: + %lp = landingpad { ptr, i32 } cleanup + ret ptr null +} + +; Test complex exception flow with multiple invoke allocations +; CHECK-LABEL: @test_complex_invoke_flow +define ptr @test_complex_invoke_flow() sanitize_alloc_token personality ptr @__gxx_personality_v0 { +entry: + ; CHECK: invoke ptr @__alloc_token_malloc(i64 16, i64 2) + ; CHECK-NEXT: to label %first_ok unwind label %cleanup1 + %ptr1 = invoke ptr @malloc(i64 16) to label %first_ok unwind label %cleanup1 + +first_ok: + ; CHECK: invoke ptr @__alloc_token_Znwm(i64 32, i64 3) + ; CHECK-NEXT: to label %second_ok unwind label %cleanup2 + %ptr2 = invoke ptr @_Znwm(i64 32) to label %second_ok unwind label %cleanup2 + +second_ok: + ret ptr %ptr1 + +cleanup1: + %lp1 = landingpad { ptr, i32 } cleanup + ret ptr null + +cleanup2: + %lp2 = landingpad { ptr, i32 } cleanup + ret ptr null +} + +; Test mixed call/invoke +; CHECK-LABEL: @test_mixed_call_invoke +define ptr @test_mixed_call_invoke() sanitize_alloc_token personality ptr @__gxx_personality_v0 { +entry: + ; CHECK: call ptr @__alloc_token_malloc(i64 8, i64 4) + %ptr1 = call ptr @malloc(i64 8) + + ; CHECK: invoke ptr @__alloc_token_malloc(i64 16, i64 5) + ; CHECK-NEXT: to label %normal unwind label %cleanup + %ptr2 = invoke ptr @malloc(i64 16) to label %normal unwind label %cleanup + +normal: + ret ptr %ptr1 + +cleanup: + %lp = landingpad { ptr, i32 } cleanup + ret ptr null +} + +declare ptr @malloc(i64) +declare ptr @_Znwm(i64) +declare i32 @__gxx_personality_v0(...) diff --git a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll new file mode 100644 index 0000000000000..be99e5ef14b16 --- /dev/null +++ b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll @@ -0,0 +1,63 @@ +; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -alloc-token-extended -S | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare ptr @malloc(i64) +declare ptr @custom_malloc(i64) +declare ptr @kmalloc(i64, i64) + +; CHECK-LABEL: @test_libcall +define ptr @test_libcall() sanitize_alloc_token { +entry: + ; CHECK: call ptr @__alloc_token_malloc(i64 64, i64 0) + %ptr1 = call ptr @malloc(i64 64) + ret ptr %ptr1 +} + +; CHECK-LABEL: @test_libcall_hint +define ptr @test_libcall_hint() sanitize_alloc_token { +entry: + ; CHECK: call ptr @__alloc_token_malloc(i64 64, i64 1) + %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0 + ret ptr %ptr1 +} + +; CHECK-LABEL: @test_nonlibcall_nohint +define ptr @test_nonlibcall_nohint() sanitize_alloc_token { +entry: + ; CHECK: call ptr @custom_malloc(i64 8) + ; CHECK: call ptr @kmalloc(i64 32, i64 0) + %ptr1 = call ptr @custom_malloc(i64 8) + %ptr2 = call ptr @kmalloc(i64 32, i64 0) + ret ptr %ptr1 +} + +; CHECK-LABEL: @test_nonlibcall_hint +define ptr @test_nonlibcall_hint() sanitize_alloc_token { +entry: + ; CHECK: call ptr @__alloc_token_custom_malloc(i64 8, i64 2) + ; CHECK: call ptr @__alloc_token_kmalloc(i64 32, i64 0, i64 3) + ; CHECK: call ptr @__alloc_token_custom_malloc(i64 64, i64 4) + ; CHECK: call ptr @__alloc_token_kmalloc(i64 128, i64 2, i64 5) + %ptr1 = call ptr @custom_malloc(i64 8), !alloc_token_hint !0 + %ptr2 = call ptr @kmalloc(i64 32, i64 0), !alloc_token_hint !0 + %ptr3 = call ptr @custom_malloc(i64 64), !alloc_token_hint !0 + %ptr4 = call ptr @kmalloc(i64 128, i64 2), !alloc_token_hint !0 + ret ptr %ptr1 +} + +; Functions without sanitize_alloc_token do not get instrumented +; CHECK-LABEL: @without_attribute +define ptr @without_attribute() { +entry: + ; CHECK: call ptr @malloc(i64 64) + ; CHECK: call ptr @custom_malloc(i64 8) + ; CHECK: call ptr @kmalloc(i64 32, i64 0) + %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0 + %ptr2 = call ptr @custom_malloc(i64 8), !alloc_token_hint !0 + %ptr3 = call ptr @kmalloc(i64 32, i64 0), !alloc_token_hint !0 + ret ptr %ptr1 +} + +!0 = !{!"int"} diff --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll new file mode 100644 index 0000000000000..d5ecfc41bace5 --- /dev/null +++ b/llvm/test/Instrumentation/AllocToken/remark.ll @@ -0,0 +1,27 @@ +; RUN: opt < %s -passes=inferattrs,alloc-token -pass-remarks=alloc-token -S 2>&1 | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +declare ptr @malloc(i64) + +; CHECK-NOT: remark: :0:0: Call to 'malloc' in 'test_has_metadata' without source-level type token +; CHECK: remark: :0:0: Call to 'malloc' in 'test_no_metadata' without source-level type token + +; CHECK-LABEL: @test_has_metadata +define ptr @test_has_metadata() sanitize_alloc_token { +entry: + ; CHECK: call ptr @__alloc_token_malloc( + %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0 + ret ptr %ptr1 +} + +; CHECK-LABEL: @test_no_metadata +define ptr @test_no_metadata() sanitize_alloc_token { +entry: + ; CHECK: call ptr @__alloc_token_malloc( + %ptr1 = call ptr @malloc(i64 32) + ret ptr %ptr1 +} + +!0 = !{!"int"} diff --git a/llvm/test/Transforms/Inline/attributes.ll b/llvm/test/Transforms/Inline/attributes.ll index 42b1a3a29aec4..55ab430f201d6 100644 --- a/llvm/test/Transforms/Inline/attributes.ll +++ b/llvm/test/Transforms/Inline/attributes.ll @@ -26,6 +26,10 @@ define i32 @sanitize_memtag_callee(i32 %i) sanitize_memtag { ret i32 %i } +define i32 @sanitize_alloc_token_callee(i32 %i) sanitize_alloc_token { + ret i32 %i +} + define i32 @safestack_callee(i32 %i) safestack { ret i32 %i } @@ -58,6 +62,10 @@ define i32 @alwaysinline_sanitize_memtag_callee(i32 %i) alwaysinline sanitize_me ret i32 %i } +define i32 @alwaysinline_sanitize_alloc_token_callee(i32 %i) alwaysinline sanitize_alloc_token { + ret i32 %i +} + define i32 @alwaysinline_safestack_callee(i32 %i) alwaysinline safestack { ret i32 %i } @@ -184,6 +192,39 @@ define i32 @test_sanitize_memtag(i32 %arg) sanitize_memtag { ; CHECK-NEXT: ret i32 } +; ---------------------------------------------------------------------------- ; + +; Can inline sanitize_alloc_token functions into a noattr function. The +; attribute is *not* viral, otherwise may break code. +define i32 @test_no_sanitize_alloc_token(i32 %arg) { +; CHECK-LABEL: @test_no_sanitize_alloc_token( +; CHECK-SAME: ) { +; CHECK-NOT: call +; CHECK: ret i32 +entry: + %x1 = call i32 @noattr_callee(i32 %arg) + %x2 = call i32 @sanitize_alloc_token_callee(i32 %x1) + %x3 = call i32 @alwaysinline_callee(i32 %x2) + %x4 = call i32 @alwaysinline_sanitize_alloc_token_callee(i32 %x3) + ret i32 %x4 +} + +; Can inline noattr functions into a sanitize_alloc_token function. If +; inlinable noattr functions cannot be instrumented, they should be marked with +; explicit noinline. +define i32 @test_sanitize_alloc_token(i32 %arg) sanitize_alloc_token { +; CHECK-LABEL: @test_sanitize_alloc_token( +; CHECK-SAME: ) [[SANITIZE_ALLOC_TOKEN:.*]] { +; CHECK-NOT: call +; CHECK: ret i32 +entry: + %x1 = call i32 @noattr_callee(i32 %arg) + %x2 = call i32 @sanitize_alloc_token_callee(i32 %x1) + %x3 = call i32 @alwaysinline_callee(i32 %x2) + %x4 = call i32 @alwaysinline_sanitize_alloc_token_callee(i32 %x3) + ret i32 %x4 +} + define i32 @test_safestack(i32 %arg) safestack { %x1 = call i32 @noattr_callee(i32 %arg) %x2 = call i32 @safestack_callee(i32 %x1) @@ -639,6 +680,7 @@ define i32 @loader_replaceable_caller() { ret i32 %1 } +; CHECK: attributes [[SANITIZE_ALLOC_TOKEN]] = { sanitize_alloc_token } ; CHECK: attributes [[SLH]] = { speculative_load_hardening } ; CHECK: attributes [[FPMAD_FALSE]] = { "less-precise-fpmad"="false" } ; CHECK: attributes [[FPMAD_TRUE]] = { "less-precise-fpmad"="true" } diff --git a/llvm/utils/emacs/llvm-mode.el b/llvm/utils/emacs/llvm-mode.el index 660d0718f098c..240c13319f634 100644 --- a/llvm/utils/emacs/llvm-mode.el +++ b/llvm/utils/emacs/llvm-mode.el @@ -34,7 +34,7 @@ "inaccessiblemem_or_argmemonly" "inalloca" "inlinehint" "jumptable" "minsize" "mustprogress" "naked" "nobuiltin" "nonnull" "nocapture" "nocallback" "nocf_check" "noduplicate" "noext" "nofree" "noimplicitfloat" "noinline" "nomerge" "nonlazybind" "noprofile" "noredzone" "noreturn" "norecurse" "nosync" "noundef" "nounwind" "nosanitize_bounds" "nosanitize_coverage" "null_pointer_is_valid" "optdebug" "optforfuzzing" "optnone" "optsize" "preallocated" "readnone" "readonly" "returned" "returns_twice" - "shadowcallstack" "signext" "speculatable" "speculative_load_hardening" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_hwaddress" "sanitize_memtag" + "shadowcallstack" "signext" "speculatable" "speculative_load_hardening" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_alloc_token" "sanitize_hwaddress" "sanitize_memtag" "sanitize_thread" "sanitize_memory" "strictfp" "swifterror" "uwtable" "vscale_range" "willreturn" "writeonly" "zeroext") 'symbols) . font-lock-constant-face) ;; Variables '("%[-a-zA-Z$._][-a-zA-Z$._0-9]*" . font-lock-variable-name-face) diff --git a/llvm/utils/gn/secondary/llvm/lib/Transforms/Instrumentation/BUILD.gn b/llvm/utils/gn/secondary/llvm/lib/Transforms/Instrumentation/BUILD.gn index a8eb834c1da23..2c6204e758559 100644 --- a/llvm/utils/gn/secondary/llvm/lib/Transforms/Instrumentation/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/lib/Transforms/Instrumentation/BUILD.gn @@ -11,6 +11,7 @@ static_library("Instrumentation") { ] sources = [ "AddressSanitizer.cpp", + "AllocToken.cpp", "BlockCoverageInference.cpp", "BoundsChecking.cpp", "CGProfile.cpp", diff --git a/llvm/utils/llvm.grm b/llvm/utils/llvm.grm index 411323178bde1..dddfe3c301b65 100644 --- a/llvm/utils/llvm.grm +++ b/llvm/utils/llvm.grm @@ -173,6 +173,7 @@ FuncAttr ::= noreturn | returns_twice | nonlazybind | sanitize_address + | sanitize_alloc_token | sanitize_thread | sanitize_memory | mustprogress diff --git a/llvm/utils/vim/syntax/llvm.vim b/llvm/utils/vim/syntax/llvm.vim index e3b8ff8629559..e048caa20406a 100644 --- a/llvm/utils/vim/syntax/llvm.vim +++ b/llvm/utils/vim/syntax/llvm.vim @@ -163,6 +163,7 @@ syn keyword llvmKeyword \ returns_twice \ safestack \ sanitize_address + \ sanitize_alloc_token \ sanitize_hwaddress \ sanitize_memory \ sanitize_memtag diff --git a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml index b64482336f404..1faaf6b26f301 100644 --- a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml +++ b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml @@ -258,6 +258,7 @@ patterns: \\breturns_twice\\b|\ \\bsafestack\\b|\ \\bsanitize_address\\b|\ + \\bsanitize_alloc_token\\b|\ \\bsanitize_hwaddress\\b|\ \\bsanitize_memory\\b|\ \\bsanitize_memtag\\b|\ From 68b478377f0e341095b880094d060c2aa2c87106 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Thu, 4 Sep 2025 12:07:33 +0200 Subject: [PATCH 02/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?= =?UTF-8?q?changes=20introduced=20through=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.8-beta.1 [skip ci] --- clang/docs/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/clang/docs/index.rst b/clang/docs/index.rst index be654af57f890..aa2b3a73dc11b 100644 --- a/clang/docs/index.rst +++ b/clang/docs/index.rst @@ -40,6 +40,7 @@ Using Clang as a Compiler SanitizerCoverage SanitizerStats SanitizerSpecialCaseList + AllocToken BoundsSafety BoundsSafetyAdoptionGuide BoundsSafetyImplPlans From 5397b6bfcc2a824ec77b1105a99652c3eb8bd57b Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Fri, 5 Sep 2025 13:19:19 +0200 Subject: [PATCH 03/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?= =?UTF-8?q?changes=20introduced=20through=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.8-beta.1 [skip ci] --- clang/lib/CodeGen/CGExpr.cpp | 2 +- llvm/include/llvm/IR/FixedMetadataKinds.def | 1 + llvm/lib/Transforms/Instrumentation/AllocToken.cpp | 2 +- llvm/lib/Transforms/Utils/Local.cpp | 4 ++++ 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 42dc7e5c91adb..14e4d648428ef 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1285,7 +1285,7 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB, // Format: !{} auto *MDN = llvm::MDNode::get(CGM.getLLVMContext(), {TypeMDS}); - CB->setMetadata("alloc_token_hint", MDN); + CB->setMetadata(llvm::LLVMContext::MD_alloc_token_hint, MDN); } CodeGenFunction::ComplexPairTy CodeGenFunction:: diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def index d09cc15d65ff6..a5a8a5663df06 100644 --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -55,3 +55,4 @@ LLVM_FIXED_MD_KIND(MD_mmra, "mmra", 40) LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41) LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42) LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43) +LLVM_FIXED_MD_KIND(MD_alloc_token_hint, "alloc_token_hint", 44) diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp index f0f7f14448dc8..4ea68470ca684 100644 --- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp +++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp @@ -144,7 +144,7 @@ STATISTIC(NumAllocations, "Allocations found"); /// /// Expected format is: !{} MDNode *getAllocTokenHintMetadata(const CallBase &CB) { - MDNode *Ret = CB.getMetadata("alloc_token_hint"); + MDNode *Ret = CB.getMetadata(LLVMContext::MD_alloc_token_hint); if (!Ret) return nullptr; assert(Ret->getNumOperands() == 1 && "bad !alloc_token_hint"); diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp index b94ed7db91580..108b3fdfa0252 100644 --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -3025,6 +3025,10 @@ static void combineMetadata(Instruction *K, const Instruction *J, // Preserve !nosanitize if both K and J have it. K->setMetadata(Kind, JMD); break; + case LLVMContext::MD_alloc_token_hint: + // Preserve !alloc_token_hint if both K and J have it. + K->setMetadata(Kind, JMD); + break; } } // Set !invariant.group from J if J has it. If both instructions have it From 14c75441e84aa32e4f5876598b9a2c59d4ecbe65 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Mon, 8 Sep 2025 21:32:21 +0200 Subject: [PATCH 04/13] fixup! fix for incomplete types Created using spr 1.3.8-beta.1 --- clang/lib/CodeGen/CGExpr.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 288b41bc42203..455de644daf00 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1289,6 +1289,7 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB, // Check if QualType contains a pointer. Implements a simple DFS to // recursively check if a type contains a pointer type. llvm::SmallPtrSet VisitedRD; + bool IncompleteType = false; auto TypeContainsPtr = [&](auto &&self, QualType T) -> bool { QualType CanonicalType = T.getCanonicalType(); if (CanonicalType->isPointerType()) @@ -1312,6 +1313,10 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB, return self(self, AT->getElementType()); // The type is a struct, class, or union. if (const RecordDecl *RD = CanonicalType->getAsRecordDecl()) { + if (!RD->isCompleteDefinition()) { + IncompleteType = true; + return false; + } if (!VisitedRD.insert(RD).second) return false; // already visited // Check all fields. @@ -1333,6 +1338,8 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB, return false; }; const bool ContainsPtr = TypeContainsPtr(TypeContainsPtr, AllocType); + if (!ContainsPtr && IncompleteType) + return nullptr; auto *ContainsPtrC = Builder.getInt1(ContainsPtr); auto *ContainsPtrMD = MDB.createConstant(ContainsPtrC); From 7f706618ddc40375d4085bc2ebe03f02ec78823a Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Mon, 8 Sep 2025 21:58:01 +0200 Subject: [PATCH 05/13] fixup! Created using spr 1.3.8-beta.1 --- clang/lib/CodeGen/CGExpr.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 455de644daf00..e7a0e7696e204 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1339,7 +1339,7 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB, }; const bool ContainsPtr = TypeContainsPtr(TypeContainsPtr, AllocType); if (!ContainsPtr && IncompleteType) - return nullptr; + return; auto *ContainsPtrC = Builder.getInt1(ContainsPtr); auto *ContainsPtrMD = MDB.createConstant(ContainsPtrC); From 22570af333df492528a5d153adb45ecce96fbc1b Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Thu, 18 Sep 2025 12:11:12 +0200 Subject: [PATCH 06/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?= =?UTF-8?q?changes=20introduced=20through=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.8-beta.1 [skip ci] --- clang/docs/AllocToken.rst | 30 ++--- clang/include/clang/Driver/SanitizerArgs.h | 1 + clang/lib/CodeGen/CGExpr.cpp | 5 +- clang/lib/CodeGen/CGExprCXX.cpp | 4 +- clang/lib/CodeGen/CodeGenFunction.h | 2 +- clang/lib/Frontend/CompilerInvocation.cpp | 5 +- clang/test/CodeGenCXX/alloc-token.cpp | 14 +-- llvm/docs/LangRef.rst | 9 +- llvm/docs/ReleaseNotes.md | 6 +- llvm/include/llvm/IR/FixedMetadataKinds.def | 2 +- .../Transforms/Instrumentation/AllocToken.cpp | 104 ++++++++---------- llvm/lib/Transforms/Utils/Local.cpp | 4 +- llvm/test/Instrumentation/AllocToken/basic.ll | 2 +- .../Instrumentation/AllocToken/basic32.ll | 25 +++++ .../AllocToken/extralibfuncs.ll | 6 +- llvm/test/Instrumentation/AllocToken/fast.ll | 4 +- .../test/Instrumentation/AllocToken/invoke.ll | 2 +- .../Instrumentation/AllocToken/nonlibcalls.ll | 18 +-- .../test/Instrumentation/AllocToken/remark.ll | 2 +- 19 files changed, 134 insertions(+), 111 deletions(-) create mode 100644 llvm/test/Instrumentation/AllocToken/basic32.ll diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst index a7bb8877f371b..f174a450403c4 100644 --- a/clang/docs/AllocToken.rst +++ b/clang/docs/AllocToken.rst @@ -31,18 +31,18 @@ Token Assignment Mode The default mode to calculate tokens is: -* *TypeHash* (mode=2): This mode assigns a token ID based on the hash of - the allocated type's name. +* ``typehash``: This mode assigns a token ID based on the hash of the allocated + type's name. Other token ID assignment modes are supported, but they may be subject to change or removal. These may (experimentally) be selected with ``-mllvm -alloc-token-mode=``: -* *Random* (mode=1): This mode assigns a statically-determined random token ID - to each allocation site. +* ``random``: This mode assigns a statically-determined random token ID to each + allocation site. -* *Increment* (mode=0): This mode assigns a simple, incrementally increasing - token ID to each allocation site. +* ``increment``: This mode assigns a simple, incrementally increasing token ID + to each allocation site. Allocation Token Instrumentation ================================ @@ -69,7 +69,7 @@ In addition, it is typically recommended to configure the following: * ``-falloc-token-max=`` Configures the maximum number of tokens. No max by default (tokens bounded - by ``UINT64_MAX``). + by ``SIZE_MAX``). .. code-block:: console @@ -80,21 +80,21 @@ Runtime Interface A compatible runtime must be provided that implements the token-enabled allocation functions. The instrumentation generates calls to functions that -take a final ``uint64_t token_id`` argument. +take a final ``size_t token_id`` argument. .. code-block:: c // C standard library functions - void *__alloc_token_malloc(size_t size, uint64_t token_id); - void *__alloc_token_calloc(size_t count, size_t size, uint64_t token_id); - void *__alloc_token_realloc(void *ptr, size_t size, uint64_t token_id); + void *__alloc_token_malloc(size_t size, size_t token_id); + void *__alloc_token_calloc(size_t count, size_t size, size_t token_id); + void *__alloc_token_realloc(void *ptr, size_t size, size_t token_id); // ... // C++ operators (mangled names) - // operator new(size_t, uint64_t) - void *__alloc_token_Znwm(size_t size, uint64_t token_id); - // operator new[](size_t, uint64_t) - void *__alloc_token_Znam(size_t size, uint64_t token_id); + // operator new(size_t, size_t) + void *__alloc_token_Znwm(size_t size, size_t token_id); + // operator new[](size_t, size_t) + void *__alloc_token_Znam(size_t size, size_t token_id); // ... other variants like nothrow, etc., are also instrumented. Fast ABI diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index ed51009654d4e..e0328bd6ed8c3 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -74,6 +74,7 @@ class SanitizerArgs { bool HwasanUseAliases = false; llvm::AsanDetectStackUseAfterReturnMode AsanUseAfterReturn = llvm::AsanDetectStackUseAfterReturnMode::Invalid; + std::string MemtagMode; bool AllocTokenFastABI = false; bool AllocTokenExtended = false; diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 14e4d648428ef..4bacb629fec39 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1272,8 +1272,7 @@ void CodeGenFunction::EmitBoundsCheckImpl(const Expr *E, llvm::Value *Bound, EmitCheck(std::make_pair(Check, CheckKind), CheckHandler, StaticData, Index); } -void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB, - QualType AllocType) { +void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) { assert(SanOpts.has(SanitizerKind::AllocToken) && "Only needed with -fsanitize=alloc-token"); @@ -1285,7 +1284,7 @@ void CodeGenFunction::EmitAllocTokenHint(llvm::CallBase *CB, // Format: !{} auto *MDN = llvm::MDNode::get(CGM.getLLVMContext(), {TypeMDS}); - CB->setMetadata(llvm::LLVMContext::MD_alloc_token_hint, MDN); + CB->setMetadata(llvm::LLVMContext::MD_alloc_token, MDN); } CodeGenFunction::ComplexPairTy CodeGenFunction:: diff --git a/clang/lib/CodeGen/CGExprCXX.cpp b/clang/lib/CodeGen/CGExprCXX.cpp index 6bf3332b425fa..9b3fe1612cbcc 100644 --- a/clang/lib/CodeGen/CGExprCXX.cpp +++ b/clang/lib/CodeGen/CGExprCXX.cpp @@ -1713,8 +1713,8 @@ llvm::Value *CodeGenFunction::EmitCXXNewExpr(const CXXNewExpr *E) { CGDI->addHeapAllocSiteMetadata(newCall, allocType, E->getExprLoc()); } if (SanOpts.has(SanitizerKind::AllocToken)) { - // Set !alloc_token_hint metadata. - EmitAllocTokenHint(newCall, allocType); + // Set !alloc_token metadata. + EmitAllocToken(newCall, allocType); } } diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index fd7ec36183c2d..e17e1e616c024 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -3349,7 +3349,7 @@ class CodeGenFunction : public CodeGenTypeCache { SanitizerHandler Handler); /// Emit additional metadata used by the AllocToken instrumentation. - void EmitAllocTokenHint(llvm::CallBase *CB, QualType AllocType); + void EmitAllocToken(llvm::CallBase *CB, QualType AllocType); llvm::Value *GetCountedByFieldExprGEP(const Expr *Base, const FieldDecl *FD, const FieldDecl *CountDecl); diff --git a/clang/lib/Frontend/CompilerInvocation.cpp b/clang/lib/Frontend/CompilerInvocation.cpp index eab2da4ddcc98..06280fd2fc381 100644 --- a/clang/lib/Frontend/CompilerInvocation.cpp +++ b/clang/lib/Frontend/CompilerInvocation.cpp @@ -2374,11 +2374,10 @@ bool CompilerInvocation::ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, if (const auto *Arg = Args.getLastArg(options::OPT_falloc_token_max_EQ)) { StringRef S = Arg->getValue(); uint64_t Value = 0; - if (S.getAsInteger(0, Value)) { + if (S.getAsInteger(0, Value)) Diags.Report(diag::err_drv_invalid_value) << Arg->getAsString(Args) << S; - } else { + else Opts.AllocTokenMax = Value; - } } Opts.EmitVersionIdentMetadata = Args.hasFlag(OPT_Qy, OPT_Qn, true); diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp index 180c771e43ae9..b8f0ed660bc67 100644 --- a/clang/test/CodeGenCXX/alloc-token.cpp +++ b/clang/test/CodeGenCXX/alloc-token.cpp @@ -69,25 +69,25 @@ void test_operator_new_nothrow() { // CHECK-LABEL: @_Z8test_newv( int *test_new() { - // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new int; } // CHECK-LABEL: @_Z14test_new_arrayv( int *test_new_array() { - // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new int[10]; } // CHECK-LABEL: @_Z16test_new_nothrowv( int *test_new_nothrow() { - // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new (std::nothrow) int; } // CHECK-LABEL: @_Z22test_new_array_nothrowv( int *test_new_array_nothrow() { - // CHECK: call {{.*}} ptr @__alloc_token_ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + // CHECK: call {{.*}} ptr @__alloc_token_ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new (std::nothrow) int[10]; } @@ -123,7 +123,7 @@ void may_throw(); TestClass *test_exception_handling_new() { try { // CHECK: invoke {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}) - // CHECK-NEXT: !alloc_token_hint + // CHECK-NEXT: !alloc_token TestClass *obj = new TestClass(); may_throw(); return obj; @@ -134,7 +134,7 @@ TestClass *test_exception_handling_new() { // CHECK-LABEL: @_Z14test_new_classv( TestClass *test_new_class() { - // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token TestClass *obj = new TestClass(); obj->data[0] = 42; return obj; @@ -142,7 +142,7 @@ TestClass *test_new_class() { // CHECK-LABEL: @_Z20test_new_class_arrayv( TestClass *test_new_class_array() { - // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token_hint + // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token TestClass* arr = new TestClass[10]; arr[0].data[0] = 123; return arr; diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 4791527a4b86b..1cf7ccc463b38 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -2428,7 +2428,7 @@ For example: attributed with ``sanitize_realtime``. This attribute is incompatible with the ``sanitize_realtime`` attribute. ``sanitize_alloc_token`` - This attributes indicates that implicit allocation token instrumentation + This attribute indicates that implicit allocation token instrumentation is enabled for this function. ``speculative_load_hardening`` This attribute indicates that @@ -8392,6 +8392,13 @@ Example: The ``nofree`` metadata indicates the memory pointed by the pointer will not be freed after the attached instruction. +'``alloc_token``' Metadata +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The ``alloc_token`` metadata may be attached to calls to memory allocation +functions, and contains richer semantic information about the type of the +allocation. This information is consumed by the ``alloc-token`` pass to +instrument such calls with allocation token IDs. Module Flags Metadata ===================== diff --git a/llvm/docs/ReleaseNotes.md b/llvm/docs/ReleaseNotes.md index eae9a73bedc34..7e0bcbba666d4 100644 --- a/llvm/docs/ReleaseNotes.md +++ b/llvm/docs/ReleaseNotes.md @@ -166,9 +166,9 @@ Changes to Sanitizers Other Changes ------------- -* Introduces the `AllocToken` pass, an instrumentation pass designed to provide - tokens to memory allocators enabling various heap organization strategies, - such as heap partitioning. +* Introduces the `AllocToken` pass, an instrumentation pass providing tokens to + memory allocators enabling various heap organization strategies, such as heap + partitioning. External Open Source Projects Using LLVM {{env.config.release}} =============================================================== diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def index a5a8a5663df06..86a4d79afc2de 100644 --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -55,4 +55,4 @@ LLVM_FIXED_MD_KIND(MD_mmra, "mmra", 40) LLVM_FIXED_MD_KIND(MD_noalias_addrspace, "noalias.addrspace", 41) LLVM_FIXED_MD_KIND(MD_callee_type, "callee_type", 42) LLVM_FIXED_MD_KIND(MD_nofree, "nofree", 43) -LLVM_FIXED_MD_KIND(MD_alloc_token_hint, "alloc_token_hint", 44) +LLVM_FIXED_MD_KIND(MD_alloc_token, "alloc_token", 44) diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp index 4ea68470ca684..bf04d0299437f 100644 --- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp +++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp @@ -69,27 +69,19 @@ enum class TokenMode : unsigned { /// Token ID based on allocated type hash. TypeHash = 2, - - // Mode count - keep last - ModeCount }; //===--- Command-line options ---------------------------------------------===// -struct ModeParser : public cl::parser { - ModeParser(cl::Option &O) : cl::parser(O) {} - bool parse(cl::Option &O, StringRef ArgName, StringRef Arg, unsigned &Value) { - if (cl::parser::parse(O, ArgName, Arg, Value)) - return true; - if (Value >= static_cast(TokenMode::ModeCount)) - return O.error("'" + Arg + "' value invalid"); - return false; - } -}; - -cl::opt - ClMode("alloc-token-mode", cl::desc("Token assignment mode"), cl::Hidden, - cl::init(static_cast(TokenMode::TypeHash))); +cl::opt + ClMode("alloc-token-mode", cl::Hidden, cl::desc("Token assignment mode"), + cl::init(TokenMode::TypeHash), + cl::values(clEnumValN(TokenMode::Increment, "increment", + "Incrementally increasing token ID"), + clEnumValN(TokenMode::Random, "random", + "Statically-assigned random token ID"), + clEnumValN(TokenMode::TypeHash, "typehash", + "Token ID based on allocated type hash"))); cl::opt ClFuncPrefix("alloc-token-prefix", cl::desc("The allocation function prefix"), @@ -106,7 +98,7 @@ cl::opt // Instrument libcalls only by default - compatible allocators only need to take // care of providing standard allocation functions. With extended coverage, also -// instrument non-libcall allocation function calls with !alloc_token_hint +// instrument non-libcall allocation function calls with !alloc_token // metadata. cl::opt ClExtended("alloc-token-extended", @@ -140,14 +132,14 @@ STATISTIC(NumAllocations, "Allocations found"); //===----------------------------------------------------------------------===// -/// Returns the !alloc_token_hint metadata if available. +/// Returns the !alloc_token metadata if available. /// /// Expected format is: !{} -MDNode *getAllocTokenHintMetadata(const CallBase &CB) { - MDNode *Ret = CB.getMetadata(LLVMContext::MD_alloc_token_hint); +MDNode *getAllocTokenMetadata(const CallBase &CB) { + MDNode *Ret = CB.getMetadata(LLVMContext::MD_alloc_token); if (!Ret) return nullptr; - assert(Ret->getNumOperands() == 1 && "bad !alloc_token_hint"); + assert(Ret->getNumOperands() == 1 && "bad !alloc_token"); assert(isa(Ret->getOperand(0))); return Ret; } @@ -198,7 +190,7 @@ class TypeHashMode : public ModeBase { using ModeBase::ModeBase; uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &ORE) { - if (MDNode *N = getAllocTokenHintMetadata(CB)) { + if (MDNode *N = getAllocTokenMetadata(CB)) { MDString *S = cast(N->getOperand(0)); return boundedToken(xxHash64(S->getString())); } @@ -212,7 +204,7 @@ class TypeHashMode : public ModeBase { ore::NV FuncNV("Function", CB.getParent()->getParent()); const Function *Callee = CB.getCalledFunction(); ore::NV CalleeNV("Callee", Callee ? Callee->getName() : ""); - return OptimizationRemark(DEBUG_TYPE, "NoAllocTokenHint", &CB) + return OptimizationRemark(DEBUG_TYPE, "NoAllocToken", &CB) << "Call to '" << CalleeNV << "' in '" << FuncNV << "' without source-level type token"; }); @@ -235,7 +227,7 @@ class AllocToken { : Options(transformOptionsFromCl(std::move(Opts))), Mod(M), FAM(MAM.getResult(M).getManager()), Mode(IncrementMode(*Options.MaxTokens)) { - switch (static_cast(ClMode.getValue())) { + switch (ClMode.getValue()) { case TokenMode::Increment: break; case TokenMode::Random: @@ -244,9 +236,6 @@ class AllocToken { case TokenMode::TypeHash: Mode.emplace(*Options.MaxTokens); break; - case TokenMode::ModeCount: - llvm_unreachable(""); - break; } } @@ -255,10 +244,11 @@ class AllocToken { private: /// Returns true for !isAllocationFn() functions that are also eligible for /// instrumentation. - bool isInstrumentableLibFunc(LibFunc Func) const; + static bool isInstrumentableLibFunc(LibFunc Func, const Value *V, + const TargetLibraryInfo *TLI); /// Returns true for isAllocationFn() functions that we should ignore. - bool ignoreInstrumentableLibFunc(LibFunc Func) const; + static bool ignoreInstrumentableLibFunc(LibFunc Func); /// Replace a call/invoke with a call/invoke to the allocation function /// with token ID. @@ -277,6 +267,7 @@ class AllocToken { const AllocTokenOptions Options; Module &Mod; + IntegerType *IntPtrTy = Mod.getDataLayout().getIntPtrType(Mod.getContext()); FunctionAnalysisManager &FAM; // Cache for replacement functions. DenseMap, FunctionCallee> TokenAllocFunctions; @@ -316,28 +307,29 @@ bool AllocToken::instrumentFunction(Function &F) { if (TLI.getLibFunc(*Callee, Func)) { if (ignoreInstrumentableLibFunc(Func)) continue; - if (isInstrumentableLibFunc(Func) || isAllocationFn(CB, &TLI)) + if (isInstrumentableLibFunc(Func, CB, &TLI)) AllocCalls.emplace_back(CB, Func); - } else if (Options.Extended && getAllocTokenHintMetadata(*CB)) { + } else if (Options.Extended && getAllocTokenMetadata(*CB)) { AllocCalls.emplace_back(CB, NotLibFunc); } } - bool Modified = false; + if (AllocCalls.empty()) + return false; - if (!AllocCalls.empty()) { - for (auto &[CB, Func] : AllocCalls) { - replaceAllocationCall(CB, Func, ORE, TLI); - } - NumAllocations += AllocCalls.size(); - NumFunctionsInstrumented++; - Modified = true; - } + for (auto &[CB, Func] : AllocCalls) + replaceAllocationCall(CB, Func, ORE, TLI); + NumAllocations += AllocCalls.size(); + NumFunctionsInstrumented++; - return Modified; + return true; } -bool AllocToken::isInstrumentableLibFunc(LibFunc Func) const { +bool AllocToken::isInstrumentableLibFunc(LibFunc Func, const Value *V, + const TargetLibraryInfo *TLI) { + if (isAllocationFn(V, TLI)) + return true; + switch (Func) { case LibFunc_posix_memalign: case LibFunc_size_returning_new: @@ -375,7 +367,7 @@ bool AllocToken::isInstrumentableLibFunc(LibFunc Func) const { } } -bool AllocToken::ignoreInstrumentableLibFunc(LibFunc Func) const { +bool AllocToken::ignoreInstrumentableLibFunc(LibFunc Func) { switch (Func) { case LibFunc_strdup: case LibFunc_dunder_strdup: @@ -395,16 +387,17 @@ void AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func, FunctionCallee TokenAlloc = getTokenAllocFunction(*CB, TokenID, Func); if (!TokenAlloc) return; + if (Options.FastABI) { + assert(TokenAlloc.getFunctionType()->getNumParams() == CB->arg_size()); + CB->setCalledFunction(TokenAlloc); + return; + } IRBuilder<> IRB(CB); - // Original args. SmallVector NewArgs{CB->args()}; - if (!Options.FastABI) { - // Add token ID. - NewArgs.push_back( - ConstantInt::get(Type::getInt64Ty(Mod.getContext()), TokenID)); - } + // Add token ID. + NewArgs.push_back(ConstantInt::get(IntPtrTy, TokenID)); assert(TokenAlloc.getFunctionType()->getNumParams() == NewArgs.size()); // Preserve invoke vs call semantics for exception handling. @@ -443,15 +436,13 @@ FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB, if (OldFTy->isVarArg()) return FunctionCallee(); // Copy params, and append token ID type. - LLVMContext &C = Mod.getContext(); Type *RetTy = OldFTy->getReturnType(); SmallVector NewParams{OldFTy->params()}; std::string TokenAllocName = ClFuncPrefix; - if (Options.FastABI) { + if (Options.FastABI) TokenAllocName += utostr(TokenID) + "_"; - } else { - NewParams.push_back(Type::getInt64Ty(C)); // token ID - } + else + NewParams.push_back(IntPtrTy); // token ID FunctionType *NewFTy = FunctionType::get(RetTy, NewParams, false); // Remove leading '_' - we add our own. StringRef No_ = Callee->getName().drop_while([](char C) { return C == '_'; }); @@ -480,5 +471,6 @@ PreservedAnalyses AllocTokenPass::run(Module &M, ModuleAnalysisManager &MAM) { Modified |= Pass.instrumentFunction(F); } - return Modified ? PreservedAnalyses::none() : PreservedAnalyses::all(); + return Modified ? PreservedAnalyses::none().preserveSet() + : PreservedAnalyses::all(); } diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp index 108b3fdfa0252..0649e70f4f5c8 100644 --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -3025,8 +3025,8 @@ static void combineMetadata(Instruction *K, const Instruction *J, // Preserve !nosanitize if both K and J have it. K->setMetadata(Kind, JMD); break; - case LLVMContext::MD_alloc_token_hint: - // Preserve !alloc_token_hint if both K and J have it. + case LLVMContext::MD_alloc_token: + // Preserve !alloc_token if both K and J have it. K->setMetadata(Kind, JMD); break; } diff --git a/llvm/test/Instrumentation/AllocToken/basic.ll b/llvm/test/Instrumentation/AllocToken/basic.ll index 94f5ef7ac5511..0fdd2ced7b130 100644 --- a/llvm/test/Instrumentation/AllocToken/basic.ll +++ b/llvm/test/Instrumentation/AllocToken/basic.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -S | FileCheck %s +; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/Instrumentation/AllocToken/basic32.ll b/llvm/test/Instrumentation/AllocToken/basic32.ll new file mode 100644 index 0000000000000..a28a11d1b9367 --- /dev/null +++ b/llvm/test/Instrumentation/AllocToken/basic32.ll @@ -0,0 +1,25 @@ +; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s + +target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" +target triple = "i386-pc-linux-gnu" + +declare ptr @malloc(i32) +declare ptr @_Znwm(i32) + +; CHECK-LABEL: @test_basic_rewriting +define ptr @test_basic_rewriting() sanitize_alloc_token { +entry: + ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_malloc(i32 64, i32 0) + ; CHECK-NOT: call ptr @malloc( + %ptr1 = call ptr @malloc(i32 64) + ret ptr %ptr1 +} + +; CHECK-LABEL: @test_cpp_operators +define ptr @test_cpp_operators() sanitize_alloc_token { +entry: + ; CHECK: call ptr @__alloc_token_Znwm(i32 32, i32 1) + ; CHECK-NOT: call ptr @_Znwm( + %ptr1 = call ptr @_Znwm(i32 32) + ret ptr %ptr1 +} diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll index 37c6d6463ffd2..3e0ffa9d7fba9 100644 --- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll +++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll @@ -11,7 +11,7 @@ declare {ptr, i64} @__size_returning_new(i64) define ptr @test_extra_libfuncs() sanitize_alloc_token { entry: ; CHECK: call {{.*}} @__alloc_token_size_returning_new( - %srn = call {ptr, i64} @__size_returning_new(i64 10), !alloc_token_hint !0 + %srn = call {ptr, i64} @__size_returning_new(i64 10), !alloc_token !0 %ptr1 = extractvalue {ptr, i64} %srn, 0 ret ptr %ptr1 } @@ -23,9 +23,9 @@ declare ptr @_Znam(i64) nobuiltin allocsize(0) define ptr @test_replaceable_new() sanitize_alloc_token { entry: ; CHECK: call ptr @__alloc_token_Znwm( - %ptr1 = call ptr @_Znwm(i64 32), !alloc_token_hint !0 + %ptr1 = call ptr @_Znwm(i64 32), !alloc_token !0 ; CHECK: call ptr @__alloc_token_Znam( - %ptr2 = call ptr @_Znam(i64 64), !alloc_token_hint !0 + %ptr2 = call ptr @_Znam(i64 64), !alloc_token !0 ret ptr %ptr1 } diff --git a/llvm/test/Instrumentation/AllocToken/fast.ll b/llvm/test/Instrumentation/AllocToken/fast.ll index c691cdcdc37c6..140035d93a851 100644 --- a/llvm/test/Instrumentation/AllocToken/fast.ll +++ b/llvm/test/Instrumentation/AllocToken/fast.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -alloc-token-fast-abi -alloc-token-max=3 -S | FileCheck %s +; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-fast-abi -alloc-token-max=3 -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" @@ -13,7 +13,7 @@ declare ptr @_Znam(i64) ; CHECK-LABEL: @test_basic_rewriting define ptr @test_basic_rewriting() sanitize_alloc_token { entry: - ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_0_malloc(i64 64) + ; CHECK: [[PTR1:%ptr[0-9]]] = call ptr @__alloc_token_0_malloc(i64 64) ; CHECK: call ptr @__alloc_token_1_calloc(i64 8, i64 8) ; CHECK: call ptr @__alloc_token_2_realloc(ptr [[PTR1]], i64 128) ; CHECK-NOT: call ptr @malloc( diff --git a/llvm/test/Instrumentation/AllocToken/invoke.ll b/llvm/test/Instrumentation/AllocToken/invoke.ll index 243462a54968f..b1564e99adfde 100644 --- a/llvm/test/Instrumentation/AllocToken/invoke.ll +++ b/llvm/test/Instrumentation/AllocToken/invoke.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -S | FileCheck %s +; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" diff --git a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll index be99e5ef14b16..c7b3e65058246 100644 --- a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll +++ b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll @@ -1,4 +1,4 @@ -; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=0 -alloc-token-extended -S | FileCheck %s +; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-extended -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" @@ -19,7 +19,7 @@ entry: define ptr @test_libcall_hint() sanitize_alloc_token { entry: ; CHECK: call ptr @__alloc_token_malloc(i64 64, i64 1) - %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0 + %ptr1 = call ptr @malloc(i64 64), !alloc_token !0 ret ptr %ptr1 } @@ -40,10 +40,10 @@ entry: ; CHECK: call ptr @__alloc_token_kmalloc(i64 32, i64 0, i64 3) ; CHECK: call ptr @__alloc_token_custom_malloc(i64 64, i64 4) ; CHECK: call ptr @__alloc_token_kmalloc(i64 128, i64 2, i64 5) - %ptr1 = call ptr @custom_malloc(i64 8), !alloc_token_hint !0 - %ptr2 = call ptr @kmalloc(i64 32, i64 0), !alloc_token_hint !0 - %ptr3 = call ptr @custom_malloc(i64 64), !alloc_token_hint !0 - %ptr4 = call ptr @kmalloc(i64 128, i64 2), !alloc_token_hint !0 + %ptr1 = call ptr @custom_malloc(i64 8), !alloc_token !0 + %ptr2 = call ptr @kmalloc(i64 32, i64 0), !alloc_token !0 + %ptr3 = call ptr @custom_malloc(i64 64), !alloc_token !0 + %ptr4 = call ptr @kmalloc(i64 128, i64 2), !alloc_token !0 ret ptr %ptr1 } @@ -54,9 +54,9 @@ entry: ; CHECK: call ptr @malloc(i64 64) ; CHECK: call ptr @custom_malloc(i64 8) ; CHECK: call ptr @kmalloc(i64 32, i64 0) - %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0 - %ptr2 = call ptr @custom_malloc(i64 8), !alloc_token_hint !0 - %ptr3 = call ptr @kmalloc(i64 32, i64 0), !alloc_token_hint !0 + %ptr1 = call ptr @malloc(i64 64), !alloc_token !0 + %ptr2 = call ptr @custom_malloc(i64 8), !alloc_token !0 + %ptr3 = call ptr @kmalloc(i64 32, i64 0), !alloc_token !0 ret ptr %ptr1 } diff --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll index d5ecfc41bace5..a664cfb37e4d0 100644 --- a/llvm/test/Instrumentation/AllocToken/remark.ll +++ b/llvm/test/Instrumentation/AllocToken/remark.ll @@ -12,7 +12,7 @@ declare ptr @malloc(i64) define ptr @test_has_metadata() sanitize_alloc_token { entry: ; CHECK: call ptr @__alloc_token_malloc( - %ptr1 = call ptr @malloc(i64 64), !alloc_token_hint !0 + %ptr1 = call ptr @malloc(i64 64), !alloc_token !0 ret ptr %ptr1 } From 01f8d55d792daf9dbb914b6a7e663c0c868851d9 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Fri, 19 Sep 2025 14:31:21 +0200 Subject: [PATCH 07/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?= =?UTF-8?q?changes=20introduced=20through=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.8-beta.1 [skip ci] --- clang/docs/AllocToken.rst | 3 +- .../Transforms/Instrumentation/AllocToken.cpp | 43 ++++++++----------- .../test/Instrumentation/AllocToken/ignore.ll | 15 +++---- 3 files changed, 26 insertions(+), 35 deletions(-) diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst index f174a450403c4..30bd6b4d90ff9 100644 --- a/clang/docs/AllocToken.rst +++ b/clang/docs/AllocToken.rst @@ -63,7 +63,7 @@ example: ptr = malloc(size); // Instrumented: - ptr = __alloc_token_malloc(size, token_id); + ptr = __alloc_token_malloc(size, ); In addition, it is typically recommended to configure the following: @@ -148,6 +148,7 @@ files or functions from instrumentation. .. code-block:: bash + [alloc-token] # Exclude specific source files src:third_party/allocator.c # Exclude function name patterns diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp index bf04d0299437f..8a59adf03b32f 100644 --- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp +++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp @@ -113,13 +113,6 @@ cl::opt ClCoverReplaceableNew("alloc-token-cover-replaceable-new", cl::desc("Cover replaceable operator new"), cl::Hidden, cl::init(true)); -// strdup-family functions only operate on strings, covering them does not make -// sense in most cases. -cl::opt - ClCoverStrdup("alloc-token-cover-strdup", - cl::desc("Cover strdup-family allocation functions"), - cl::Hidden, cl::init(false)); - cl::opt ClFallbackToken( "alloc-token-fallback", cl::desc("The default fallback token where none could be determined"), @@ -128,7 +121,7 @@ cl::opt ClFallbackToken( //===--- Statistics -------------------------------------------------------===// STATISTIC(NumFunctionsInstrumented, "Functions instrumented"); -STATISTIC(NumAllocations, "Allocations found"); +STATISTIC(NumAllocationsInstrumented, "Allocations instrumented"); //===----------------------------------------------------------------------===// @@ -194,12 +187,13 @@ class TypeHashMode : public ModeBase { MDString *S = cast(N->getOperand(0)); return boundedToken(xxHash64(S->getString())); } - remarkNoHint(CB, ORE); + remarkNoMetadata(CB, ORE); return ClFallbackToken; } /// Remark that there was no precise type information. - void remarkNoHint(const CallBase &CB, OptimizationRemarkEmitter &ORE) { + static void remarkNoMetadata(const CallBase &CB, + OptimizationRemarkEmitter &ORE) { ORE.emit([&] { ore::NV FuncNV("Function", CB.getParent()->getParent()); const Function *Callee = CB.getCalledFunction(); @@ -212,12 +206,12 @@ class TypeHashMode : public ModeBase { }; // Apply opt overrides. -AllocTokenOptions &&transformOptionsFromCl(AllocTokenOptions &&Opts) { +AllocTokenOptions transformOptionsFromCl(AllocTokenOptions Opts) { if (!Opts.MaxTokens.has_value()) Opts.MaxTokens = ClMaxTokens; Opts.FastABI |= ClFastABI; Opts.Extended |= ClExtended; - return std::move(Opts); + return Opts; } class AllocToken { @@ -252,7 +246,7 @@ class AllocToken { /// Replace a call/invoke with a call/invoke to the allocation function /// with token ID. - void replaceAllocationCall(CallBase *CB, LibFunc Func, + bool replaceAllocationCall(CallBase *CB, LibFunc Func, OptimizationRemarkEmitter &ORE, const TargetLibraryInfo &TLI); @@ -314,15 +308,13 @@ bool AllocToken::instrumentFunction(Function &F) { } } - if (AllocCalls.empty()) - return false; - + bool Modified = false; for (auto &[CB, Func] : AllocCalls) - replaceAllocationCall(CB, Func, ORE, TLI); - NumAllocations += AllocCalls.size(); - NumFunctionsInstrumented++; + Modified |= replaceAllocationCall(CB, Func, ORE, TLI); - return true; + if (Modified) + NumFunctionsInstrumented++; + return Modified; } bool AllocToken::isInstrumentableLibFunc(LibFunc Func, const Value *V, @@ -373,24 +365,26 @@ bool AllocToken::ignoreInstrumentableLibFunc(LibFunc Func) { case LibFunc_dunder_strdup: case LibFunc_strndup: case LibFunc_dunder_strndup: - return !ClCoverStrdup; + return true; default: return false; } } -void AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func, +bool AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func, OptimizationRemarkEmitter &ORE, const TargetLibraryInfo &TLI) { uint64_t TokenID = getToken(*CB, ORE); FunctionCallee TokenAlloc = getTokenAllocFunction(*CB, TokenID, Func); if (!TokenAlloc) - return; + return false; + NumAllocationsInstrumented++; + if (Options.FastABI) { assert(TokenAlloc.getFunctionType()->getNumParams() == CB->arg_size()); CB->setCalledFunction(TokenAlloc); - return; + return true; } IRBuilder<> IRB(CB); @@ -416,6 +410,7 @@ void AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func, // Replace all uses and delete the old call. CB->replaceAllUsesWith(NewCall); CB->eraseFromParent(); + return true; } FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB, diff --git a/llvm/test/Instrumentation/AllocToken/ignore.ll b/llvm/test/Instrumentation/AllocToken/ignore.ll index 65921685d70a0..cc8ec4934e222 100644 --- a/llvm/test/Instrumentation/AllocToken/ignore.ll +++ b/llvm/test/Instrumentation/AllocToken/ignore.ll @@ -1,7 +1,6 @@ ; Test for all allocation functions that should be ignored by default. ; -; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s --check-prefixes=CHECK,DEFAULT -; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-cover-strdup -S | FileCheck %s --check-prefixes=CHECK,COVER +; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" @@ -14,17 +13,13 @@ declare ptr @__strndup(ptr, i64) ; CHECK-LABEL: @test_ignorable_allocation_functions define ptr @test_ignorable_allocation_functions(ptr %ptr) sanitize_alloc_token { entry: - ; COVER: call ptr @__alloc_token_strdup( - ; DEFAULT: call ptr @strdup( + ; CHECK: call ptr @strdup( %ptr1 = call ptr @strdup(ptr %ptr) - ; COVER: call ptr @__alloc_token_strdup( - ; DEFAULT: call ptr @__strdup( + ; CHECK: call ptr @__strdup( %ptr2 = call ptr @__strdup(ptr %ptr) - ; COVER: call ptr @__alloc_token_strndup( - ; DEFAULT: call ptr @strndup( + ; CHECK: call ptr @strndup( %ptr3 = call ptr @strndup(ptr %ptr, i64 42) - ; COVER: call ptr @__alloc_token_strndup( - ; DEFAULT: call ptr @__strndup( + ; CHECK: call ptr @__strndup( %ptr4 = call ptr @__strndup(ptr %ptr, i64 42) ret ptr %ptr1 } From 69aad6dd2f1035098d985f2d979c61279220785b Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Mon, 22 Sep 2025 16:51:44 +0200 Subject: [PATCH 08/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?= =?UTF-8?q?changes=20introduced=20through=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.8-beta.1 [skip ci] --- llvm/test/Instrumentation/AllocToken/basic.ll | 65 ++++++++++------ .../Instrumentation/AllocToken/basic32.ll | 19 +++-- .../AllocToken/extralibfuncs.ll | 23 ++++-- llvm/test/Instrumentation/AllocToken/fast.ll | 28 ++++--- .../test/Instrumentation/AllocToken/ignore.ll | 15 ++-- .../test/Instrumentation/AllocToken/invoke.ll | 76 ++++++++++++++----- .../Instrumentation/AllocToken/nonlibcalls.ll | 55 ++++++++++---- .../test/Instrumentation/AllocToken/remark.ll | 20 ++++- 8 files changed, 209 insertions(+), 92 deletions(-) diff --git a/llvm/test/Instrumentation/AllocToken/basic.ll b/llvm/test/Instrumentation/AllocToken/basic.ll index 0fdd2ced7b130..fe673ca9fd524 100644 --- a/llvm/test/Instrumentation/AllocToken/basic.ll +++ b/llvm/test/Instrumentation/AllocToken/basic.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" @@ -13,15 +14,16 @@ declare void @_ZdlPv(ptr) declare i32 @foobar(i64) ; Test basic allocation call rewriting -; CHECK-LABEL: @test_basic_rewriting define ptr @test_basic_rewriting() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_basic_rewriting( +; CHECK-SAME: ) #[[ATTR5:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 0) +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token_calloc(i64 8, i64 8, i64 1) +; CHECK-NEXT: [[TMP2:%.*]] = call ptr @__alloc_token_realloc(ptr [[TMP0]], i64 128, i64 2) +; CHECK-NEXT: ret ptr [[TMP2]] +; entry: - ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_malloc(i64 64, i64 0) - ; CHECK: call ptr @__alloc_token_calloc(i64 8, i64 8, i64 1) - ; CHECK: call ptr @__alloc_token_realloc(ptr [[PTR1]], i64 128, i64 2) - ; CHECK-NOT: call ptr @malloc( - ; CHECK-NOT: call ptr @calloc( - ; CHECK-NOT: call ptr @realloc( %ptr1 = call ptr @malloc(i64 64) %ptr2 = call ptr @calloc(i64 8, i64 8) %ptr3 = call ptr @realloc(ptr %ptr1, i64 128) @@ -29,56 +31,69 @@ entry: } ; Test C++ operator rewriting -; CHECK-LABEL: @test_cpp_operators define ptr @test_cpp_operators() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_cpp_operators( +; CHECK-SAME: ) #[[ATTR5]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i64 32, i64 3) +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token_Znam(i64 64, i64 4) +; CHECK-NEXT: ret ptr [[TMP0]] +; entry: - ; CHECK: call ptr @__alloc_token_Znwm(i64 32, i64 3) - ; CHECK: call ptr @__alloc_token_Znam(i64 64, i64 4) - ; CHECK-NOT: call ptr @_Znwm( - ; CHECK-NOT: call ptr @_Znam( %ptr1 = call ptr @_Znwm(i64 32) %ptr2 = call ptr @_Znam(i64 64) ret ptr %ptr1 } ; Functions without sanitize_alloc_token do not get instrumented -; CHECK-LABEL: @without_attribute define ptr @without_attribute() { +; CHECK-LABEL: define ptr @without_attribute() { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[PTR:%.*]] = call ptr @malloc(i64 16) +; CHECK-NEXT: ret ptr [[PTR]] +; entry: - ; CHECK: call ptr @malloc(i64 16) - ; CHECK-NOT: call ptr @__alloc_token_malloc %ptr = call ptr @malloc(i64 16) ret ptr %ptr } ; Test that free/delete are untouched -; CHECK-LABEL: @test_free_untouched define void @test_free_untouched(ptr %ptr) sanitize_alloc_token { +; CHECK-LABEL: define void @test_free_untouched( +; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR5]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: call void @free(ptr [[PTR]]) +; CHECK-NEXT: call void @_ZdlPv(ptr [[PTR]]) +; CHECK-NEXT: ret void +; entry: - ; CHECK: call void @free(ptr %ptr) - ; CHECK: call void @_ZdlPv(ptr %ptr) - ; CHECK-NOT: call ptr @__alloc_token_ call void @free(ptr %ptr) call void @_ZdlPv(ptr %ptr) ret void } ; Non-allocation functions are untouched -; CHECK-LABEL: @no_allocations define i32 @no_allocations(i32 %x) sanitize_alloc_token { +; CHECK-LABEL: define i32 @no_allocations( +; CHECK-SAME: i32 [[X:%.*]]) #[[ATTR5]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[RESULT:%.*]] = call i32 @foobar(i64 42) +; CHECK-NEXT: ret i32 [[RESULT]] +; entry: - ; CHECK: call i32 @foobar - ; CHECK-NOT: call i32 @__alloc_token_ %result = call i32 @foobar(i64 42) ret i32 %result } ; Test that tail calls are preserved -; CHECK-LABEL: @test_tail_call_preserved define ptr @test_tail_call_preserved() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_tail_call_preserved( +; CHECK-SAME: ) #[[ATTR5]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = tail call ptr @__alloc_token_malloc(i64 42, i64 5) +; CHECK-NEXT: ret ptr [[TMP0]] +; entry: - ; CHECK: tail call ptr @__alloc_token_malloc(i64 42, i64 5) - ; CHECK-NOT: tail call ptr @malloc( %result = tail call ptr @malloc(i64 42) ret ptr %result } diff --git a/llvm/test/Instrumentation/AllocToken/basic32.ll b/llvm/test/Instrumentation/AllocToken/basic32.ll index a28a11d1b9367..ed09fdbaf3d56 100644 --- a/llvm/test/Instrumentation/AllocToken/basic32.ll +++ b/llvm/test/Instrumentation/AllocToken/basic32.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" @@ -6,20 +7,26 @@ target triple = "i386-pc-linux-gnu" declare ptr @malloc(i32) declare ptr @_Znwm(i32) -; CHECK-LABEL: @test_basic_rewriting define ptr @test_basic_rewriting() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_basic_rewriting( +; CHECK-SAME: ) #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i32 64, i32 0) +; CHECK-NEXT: ret ptr [[TMP0]] +; entry: - ; CHECK: [[PTR1:%[0-9]]] = call ptr @__alloc_token_malloc(i32 64, i32 0) - ; CHECK-NOT: call ptr @malloc( %ptr1 = call ptr @malloc(i32 64) ret ptr %ptr1 } -; CHECK-LABEL: @test_cpp_operators define ptr @test_cpp_operators() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_cpp_operators( +; CHECK-SAME: ) #[[ATTR2]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i32 32, i32 1) +; CHECK-NEXT: ret ptr [[TMP0]] +; entry: - ; CHECK: call ptr @__alloc_token_Znwm(i32 32, i32 1) - ; CHECK-NOT: call ptr @_Znwm( %ptr1 = call ptr @_Znwm(i32 32) ret ptr %ptr1 } diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll index 3e0ffa9d7fba9..de968aad9ca3a 100644 --- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll +++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; Test for special libfuncs not automatically considered allocation functions. ; ; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s @@ -7,10 +8,15 @@ target triple = "x86_64-unknown-linux-gnu" declare {ptr, i64} @__size_returning_new(i64) -; CHECK-LABEL: @test_extra_libfuncs define ptr @test_extra_libfuncs() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_extra_libfuncs( +; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call { ptr, i64 } @__alloc_token_size_returning_new(i64 10, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]] +; CHECK-NEXT: [[PTR1:%.*]] = extractvalue { ptr, i64 } [[TMP0]], 0 +; CHECK-NEXT: ret ptr [[PTR1]] +; entry: - ; CHECK: call {{.*}} @__alloc_token_size_returning_new( %srn = call {ptr, i64} @__size_returning_new(i64 10), !alloc_token !0 %ptr1 = extractvalue {ptr, i64} %srn, 0 ret ptr %ptr1 @@ -19,14 +25,21 @@ entry: declare ptr @_Znwm(i64) nobuiltin allocsize(0) declare ptr @_Znam(i64) nobuiltin allocsize(0) -; CHECK-LABEL: @test_replaceable_new define ptr @test_replaceable_new() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_replaceable_new( +; CHECK-SAME: ) #[[ATTR1]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i64 32, i64 6985720287680550851), !alloc_token [[META0]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token_Znam(i64 64, i64 6985720287680550851), !alloc_token [[META0]] +; CHECK-NEXT: ret ptr [[TMP0]] +; entry: - ; CHECK: call ptr @__alloc_token_Znwm( %ptr1 = call ptr @_Znwm(i64 32), !alloc_token !0 - ; CHECK: call ptr @__alloc_token_Znam( %ptr2 = call ptr @_Znam(i64 64), !alloc_token !0 ret ptr %ptr1 } !0 = !{!"int"} +;. +; CHECK: [[META0]] = !{!"int"} +;. diff --git a/llvm/test/Instrumentation/AllocToken/fast.ll b/llvm/test/Instrumentation/AllocToken/fast.ll index 140035d93a851..60169f308244f 100644 --- a/llvm/test/Instrumentation/AllocToken/fast.ll +++ b/llvm/test/Instrumentation/AllocToken/fast.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-fast-abi -alloc-token-max=3 -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" @@ -10,15 +11,16 @@ declare ptr @_Znwm(i64) declare ptr @_Znam(i64) ; Test basic allocation call rewriting -; CHECK-LABEL: @test_basic_rewriting define ptr @test_basic_rewriting() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_basic_rewriting( +; CHECK-SAME: ) #[[ATTR4:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[PTR1:%.*]] = call ptr @__alloc_token_0_malloc(i64 64) +; CHECK-NEXT: [[PTR2:%.*]] = call ptr @__alloc_token_1_calloc(i64 8, i64 8) +; CHECK-NEXT: [[PTR3:%.*]] = call ptr @__alloc_token_2_realloc(ptr [[PTR1]], i64 128) +; CHECK-NEXT: ret ptr [[PTR3]] +; entry: - ; CHECK: [[PTR1:%ptr[0-9]]] = call ptr @__alloc_token_0_malloc(i64 64) - ; CHECK: call ptr @__alloc_token_1_calloc(i64 8, i64 8) - ; CHECK: call ptr @__alloc_token_2_realloc(ptr [[PTR1]], i64 128) - ; CHECK-NOT: call ptr @malloc( - ; CHECK-NOT: call ptr @calloc( - ; CHECK-NOT: call ptr @realloc( %ptr1 = call ptr @malloc(i64 64) %ptr2 = call ptr @calloc(i64 8, i64 8) %ptr3 = call ptr @realloc(ptr %ptr1, i64 128) @@ -26,13 +28,15 @@ entry: } ; Test C++ operator rewriting -; CHECK-LABEL: @test_cpp_operators define ptr @test_cpp_operators() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_cpp_operators( +; CHECK-SAME: ) #[[ATTR4]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[PTR1:%.*]] = call ptr @__alloc_token_0_Znwm(i64 32) +; CHECK-NEXT: [[PTR2:%.*]] = call ptr @__alloc_token_1_Znam(i64 64) +; CHECK-NEXT: ret ptr [[PTR1]] +; entry: - ; CHECK: call ptr @__alloc_token_0_Znwm(i64 32) - ; CHECK: call ptr @__alloc_token_1_Znam(i64 64) - ; CHECK-NOT: call ptr @_Znwm( - ; CHECK-NOT: call ptr @_Znam( %ptr1 = call ptr @_Znwm(i64 32) %ptr2 = call ptr @_Znam(i64 64) ret ptr %ptr1 diff --git a/llvm/test/Instrumentation/AllocToken/ignore.ll b/llvm/test/Instrumentation/AllocToken/ignore.ll index cc8ec4934e222..ff8e85f87f64e 100644 --- a/llvm/test/Instrumentation/AllocToken/ignore.ll +++ b/llvm/test/Instrumentation/AllocToken/ignore.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; Test for all allocation functions that should be ignored by default. ; ; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s @@ -10,16 +11,20 @@ declare ptr @__strdup(ptr) declare ptr @strndup(ptr, i64) declare ptr @__strndup(ptr, i64) -; CHECK-LABEL: @test_ignorable_allocation_functions define ptr @test_ignorable_allocation_functions(ptr %ptr) sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_ignorable_allocation_functions( +; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR2:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[PTR1:%.*]] = call ptr @strdup(ptr [[PTR]]) +; CHECK-NEXT: [[PTR2:%.*]] = call ptr @__strdup(ptr [[PTR]]) +; CHECK-NEXT: [[PTR3:%.*]] = call ptr @strndup(ptr [[PTR]], i64 42) +; CHECK-NEXT: [[PTR4:%.*]] = call ptr @__strndup(ptr [[PTR]], i64 42) +; CHECK-NEXT: ret ptr [[PTR1]] +; entry: - ; CHECK: call ptr @strdup( %ptr1 = call ptr @strdup(ptr %ptr) - ; CHECK: call ptr @__strdup( %ptr2 = call ptr @__strdup(ptr %ptr) - ; CHECK: call ptr @strndup( %ptr3 = call ptr @strndup(ptr %ptr, i64 42) - ; CHECK: call ptr @__strndup( %ptr4 = call ptr @__strndup(ptr %ptr, i64 42) ret ptr %ptr1 } diff --git a/llvm/test/Instrumentation/AllocToken/invoke.ll b/llvm/test/Instrumentation/AllocToken/invoke.ll index b1564e99adfde..8977ed67563c8 100644 --- a/llvm/test/Instrumentation/AllocToken/invoke.ll +++ b/llvm/test/Instrumentation/AllocToken/invoke.ll @@ -1,15 +1,23 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu" -; CHECK-LABEL: @test_invoke_malloc define ptr @test_invoke_malloc() sanitize_alloc_token personality ptr @__gxx_personality_v0 { +; CHECK-LABEL: define ptr @test_invoke_malloc( +; CHECK-SAME: ) #[[ATTR0:[0-9]+]] personality ptr @__gxx_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = invoke ptr @__alloc_token_malloc(i64 64, i64 0) +; CHECK-NEXT: to label %[[NORMAL:.*]] unwind label %[[CLEANUP:.*]] +; CHECK: [[NORMAL]]: +; CHECK-NEXT: ret ptr [[TMP0]] +; CHECK: [[CLEANUP]]: +; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: ret ptr null +; entry: - ; CHECK: invoke ptr @__alloc_token_malloc(i64 64, i64 0) - ; CHECK-NEXT: to label %normal unwind label %cleanup - ; CHECK-NOT: call ptr @__alloc_token_malloc - ; CHECK-NOT: call ptr @malloc %ptr = invoke ptr @malloc(i64 64) to label %normal unwind label %cleanup normal: @@ -20,13 +28,20 @@ cleanup: ret ptr null } -; CHECK-LABEL: @test_invoke_operator_new define ptr @test_invoke_operator_new() sanitize_alloc_token personality ptr @__gxx_personality_v0 { +; CHECK-LABEL: define ptr @test_invoke_operator_new( +; CHECK-SAME: ) #[[ATTR0]] personality ptr @__gxx_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = invoke ptr @__alloc_token_Znwm(i64 32, i64 1) +; CHECK-NEXT: to label %[[NORMAL:.*]] unwind label %[[CLEANUP:.*]] +; CHECK: [[NORMAL]]: +; CHECK-NEXT: ret ptr [[TMP0]] +; CHECK: [[CLEANUP]]: +; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: ret ptr null +; entry: - ; CHECK: invoke ptr @__alloc_token_Znwm(i64 32, i64 1) - ; CHECK-NEXT: to label %normal unwind label %cleanup - ; CHECK-NOT: call ptr @__alloc_token_Znwm - ; CHECK-NOT: call ptr @_Znwm %ptr = invoke ptr @_Znwm(i64 32) to label %normal unwind label %cleanup normal: @@ -38,16 +53,30 @@ cleanup: } ; Test complex exception flow with multiple invoke allocations -; CHECK-LABEL: @test_complex_invoke_flow define ptr @test_complex_invoke_flow() sanitize_alloc_token personality ptr @__gxx_personality_v0 { +; CHECK-LABEL: define ptr @test_complex_invoke_flow( +; CHECK-SAME: ) #[[ATTR0]] personality ptr @__gxx_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = invoke ptr @__alloc_token_malloc(i64 16, i64 2) +; CHECK-NEXT: to label %[[FIRST_OK:.*]] unwind label %[[CLEANUP1:.*]] +; CHECK: [[FIRST_OK]]: +; CHECK-NEXT: [[TMP1:%.*]] = invoke ptr @__alloc_token_Znwm(i64 32, i64 3) +; CHECK-NEXT: to label %[[SECOND_OK:.*]] unwind label %[[CLEANUP2:.*]] +; CHECK: [[SECOND_OK]]: +; CHECK-NEXT: ret ptr [[TMP0]] +; CHECK: [[CLEANUP1]]: +; CHECK-NEXT: [[LP1:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: ret ptr null +; CHECK: [[CLEANUP2]]: +; CHECK-NEXT: [[LP2:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: ret ptr null +; entry: - ; CHECK: invoke ptr @__alloc_token_malloc(i64 16, i64 2) - ; CHECK-NEXT: to label %first_ok unwind label %cleanup1 %ptr1 = invoke ptr @malloc(i64 16) to label %first_ok unwind label %cleanup1 first_ok: - ; CHECK: invoke ptr @__alloc_token_Znwm(i64 32, i64 3) - ; CHECK-NEXT: to label %second_ok unwind label %cleanup2 %ptr2 = invoke ptr @_Znwm(i64 32) to label %second_ok unwind label %cleanup2 second_ok: @@ -63,14 +92,23 @@ cleanup2: } ; Test mixed call/invoke -; CHECK-LABEL: @test_mixed_call_invoke define ptr @test_mixed_call_invoke() sanitize_alloc_token personality ptr @__gxx_personality_v0 { +; CHECK-LABEL: define ptr @test_mixed_call_invoke( +; CHECK-SAME: ) #[[ATTR0]] personality ptr @__gxx_personality_v0 { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 8, i64 4) +; CHECK-NEXT: [[TMP1:%.*]] = invoke ptr @__alloc_token_malloc(i64 16, i64 5) +; CHECK-NEXT: to label %[[NORMAL:.*]] unwind label %[[CLEANUP:.*]] +; CHECK: [[NORMAL]]: +; CHECK-NEXT: ret ptr [[TMP0]] +; CHECK: [[CLEANUP]]: +; CHECK-NEXT: [[LP:%.*]] = landingpad { ptr, i32 } +; CHECK-NEXT: cleanup +; CHECK-NEXT: ret ptr null +; entry: - ; CHECK: call ptr @__alloc_token_malloc(i64 8, i64 4) %ptr1 = call ptr @malloc(i64 8) - ; CHECK: invoke ptr @__alloc_token_malloc(i64 16, i64 5) - ; CHECK-NEXT: to label %normal unwind label %cleanup %ptr2 = invoke ptr @malloc(i64 16) to label %normal unwind label %cleanup normal: diff --git a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll index c7b3e65058246..3ba5285e0f1ae 100644 --- a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll +++ b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-extended -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" @@ -7,39 +8,55 @@ declare ptr @malloc(i64) declare ptr @custom_malloc(i64) declare ptr @kmalloc(i64, i64) -; CHECK-LABEL: @test_libcall define ptr @test_libcall() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_libcall( +; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 0) +; CHECK-NEXT: ret ptr [[TMP0]] +; entry: - ; CHECK: call ptr @__alloc_token_malloc(i64 64, i64 0) %ptr1 = call ptr @malloc(i64 64) ret ptr %ptr1 } -; CHECK-LABEL: @test_libcall_hint define ptr @test_libcall_hint() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_libcall_hint( +; CHECK-SAME: ) #[[ATTR1]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 1), !alloc_token [[META0:![0-9]+]] +; CHECK-NEXT: ret ptr [[TMP0]] +; entry: - ; CHECK: call ptr @__alloc_token_malloc(i64 64, i64 1) %ptr1 = call ptr @malloc(i64 64), !alloc_token !0 ret ptr %ptr1 } -; CHECK-LABEL: @test_nonlibcall_nohint define ptr @test_nonlibcall_nohint() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_nonlibcall_nohint( +; CHECK-SAME: ) #[[ATTR1]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[PTR1:%.*]] = call ptr @custom_malloc(i64 8) +; CHECK-NEXT: [[PTR2:%.*]] = call ptr @kmalloc(i64 32, i64 0) +; CHECK-NEXT: ret ptr [[PTR1]] +; entry: - ; CHECK: call ptr @custom_malloc(i64 8) - ; CHECK: call ptr @kmalloc(i64 32, i64 0) %ptr1 = call ptr @custom_malloc(i64 8) %ptr2 = call ptr @kmalloc(i64 32, i64 0) ret ptr %ptr1 } -; CHECK-LABEL: @test_nonlibcall_hint define ptr @test_nonlibcall_hint() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_nonlibcall_hint( +; CHECK-SAME: ) #[[ATTR1]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_custom_malloc(i64 8, i64 2), !alloc_token [[META0]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token_kmalloc(i64 32, i64 0, i64 3), !alloc_token [[META0]] +; CHECK-NEXT: [[TMP2:%.*]] = call ptr @__alloc_token_custom_malloc(i64 64, i64 4), !alloc_token [[META0]] +; CHECK-NEXT: [[TMP3:%.*]] = call ptr @__alloc_token_kmalloc(i64 128, i64 2, i64 5), !alloc_token [[META0]] +; CHECK-NEXT: ret ptr [[TMP0]] +; entry: - ; CHECK: call ptr @__alloc_token_custom_malloc(i64 8, i64 2) - ; CHECK: call ptr @__alloc_token_kmalloc(i64 32, i64 0, i64 3) - ; CHECK: call ptr @__alloc_token_custom_malloc(i64 64, i64 4) - ; CHECK: call ptr @__alloc_token_kmalloc(i64 128, i64 2, i64 5) %ptr1 = call ptr @custom_malloc(i64 8), !alloc_token !0 %ptr2 = call ptr @kmalloc(i64 32, i64 0), !alloc_token !0 %ptr3 = call ptr @custom_malloc(i64 64), !alloc_token !0 @@ -48,12 +65,15 @@ entry: } ; Functions without sanitize_alloc_token do not get instrumented -; CHECK-LABEL: @without_attribute define ptr @without_attribute() { +; CHECK-LABEL: define ptr @without_attribute() { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[PTR1:%.*]] = call ptr @malloc(i64 64), !alloc_token [[META0]] +; CHECK-NEXT: [[PTR2:%.*]] = call ptr @custom_malloc(i64 8), !alloc_token [[META0]] +; CHECK-NEXT: [[PTR3:%.*]] = call ptr @kmalloc(i64 32, i64 0), !alloc_token [[META0]] +; CHECK-NEXT: ret ptr [[PTR1]] +; entry: - ; CHECK: call ptr @malloc(i64 64) - ; CHECK: call ptr @custom_malloc(i64 8) - ; CHECK: call ptr @kmalloc(i64 32, i64 0) %ptr1 = call ptr @malloc(i64 64), !alloc_token !0 %ptr2 = call ptr @custom_malloc(i64 8), !alloc_token !0 %ptr3 = call ptr @kmalloc(i64 32, i64 0), !alloc_token !0 @@ -61,3 +81,6 @@ entry: } !0 = !{!"int"} +;. +; CHECK: [[META0]] = !{!"int"} +;. diff --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll index a664cfb37e4d0..429d806b4a2e7 100644 --- a/llvm/test/Instrumentation/AllocToken/remark.ll +++ b/llvm/test/Instrumentation/AllocToken/remark.ll @@ -1,3 +1,4 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 ; RUN: opt < %s -passes=inferattrs,alloc-token -pass-remarks=alloc-token -S 2>&1 | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" @@ -8,20 +9,31 @@ declare ptr @malloc(i64) ; CHECK-NOT: remark: :0:0: Call to 'malloc' in 'test_has_metadata' without source-level type token ; CHECK: remark: :0:0: Call to 'malloc' in 'test_no_metadata' without source-level type token -; CHECK-LABEL: @test_has_metadata define ptr @test_has_metadata() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_has_metadata( +; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]] +; CHECK-NEXT: ret ptr [[TMP0]] +; entry: - ; CHECK: call ptr @__alloc_token_malloc( %ptr1 = call ptr @malloc(i64 64), !alloc_token !0 ret ptr %ptr1 } -; CHECK-LABEL: @test_no_metadata define ptr @test_no_metadata() sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_no_metadata( +; CHECK-SAME: ) #[[ATTR1]] { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 32, i64 0) +; CHECK-NEXT: ret ptr [[TMP0]] +; entry: - ; CHECK: call ptr @__alloc_token_malloc( %ptr1 = call ptr @malloc(i64 32) ret ptr %ptr1 } !0 = !{!"int"} +;. +; CHECK: [[META0]] = !{!"int"} +;. From d5a42a151df365cada407f1eb85b7221999d6072 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Tue, 23 Sep 2025 16:43:37 +0200 Subject: [PATCH 09/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?= =?UTF-8?q?changes=20introduced=20through=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.8-beta.1 [skip ci] --- clang/docs/AllocToken.rst | 4 +-- clang/test/CodeGenCXX/alloc-token.cpp | 30 +++++++++---------- .../Transforms/Instrumentation/AllocToken.cpp | 4 +-- llvm/test/Instrumentation/AllocToken/basic.ll | 4 +-- .../Instrumentation/AllocToken/basic32.ll | 2 +- .../AllocToken/extralibfuncs.ll | 6 ++-- llvm/test/Instrumentation/AllocToken/fast.ll | 4 +-- .../test/Instrumentation/AllocToken/invoke.ll | 4 +-- 8 files changed, 28 insertions(+), 30 deletions(-) diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst index 30bd6b4d90ff9..6dd321a5b2efc 100644 --- a/clang/docs/AllocToken.rst +++ b/clang/docs/AllocToken.rst @@ -92,9 +92,9 @@ take a final ``size_t token_id`` argument. // C++ operators (mangled names) // operator new(size_t, size_t) - void *__alloc_token_Znwm(size_t size, size_t token_id); + void *__alloc_token__Znwm(size_t size, size_t token_id); // operator new[](size_t, size_t) - void *__alloc_token_Znam(size_t size, size_t token_id); + void *__alloc_token__Znam(size_t size, size_t token_id); // ... other variants like nothrow, etc., are also instrumented. Fast ABI diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp index b8f0ed660bc67..d115720e6b209 100644 --- a/clang/test/CodeGenCXX/alloc-token.cpp +++ b/clang/test/CodeGenCXX/alloc-token.cpp @@ -52,42 +52,42 @@ void test_malloc_like() { // CHECK-LABEL: @_Z17test_operator_newv( void test_operator_new() { // FIXME: This should not be token ID 0! - // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0) + // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 0) sink = __builtin_operator_new(sizeof(int)); - // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 0) + // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 0) sink = ::operator new(sizeof(int)); } // CHECK-LABEL: @_Z25test_operator_new_nothrowv( void test_operator_new_nothrow() { // FIXME: This should not be token ID 0! - // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0) + // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0) sink = __builtin_operator_new(sizeof(int), std::nothrow); - // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0) + // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0) sink = ::operator new(sizeof(int), std::nothrow); } // CHECK-LABEL: @_Z8test_newv( int *test_new() { - // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token + // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new int; } // CHECK-LABEL: @_Z14test_new_arrayv( int *test_new_array() { - // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token + // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new int[10]; } // CHECK-LABEL: @_Z16test_new_nothrowv( int *test_new_nothrow() { - // CHECK: call {{.*}} ptr @__alloc_token_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token + // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new (std::nothrow) int; } // CHECK-LABEL: @_Z22test_new_array_nothrowv( int *test_new_array_nothrow() { - // CHECK: call {{.*}} ptr @__alloc_token_ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token + // CHECK: call {{.*}} ptr @__alloc_token__ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new (std::nothrow) int[10]; } @@ -100,10 +100,10 @@ __attribute__((no_sanitize("alloc-token"))) int *no_sanitize_new() { // CHECK-LABEL: @_Z23test_size_returning_newv( void test_size_returning_new() { // FIXME: This should not be token ID 0! - // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new(i64 noundef 8, i64 0) - // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1, i64 0) - // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_aligned(i64 noundef 8, i64 noundef 32, i64 0) - // CHECK: call { ptr, i64 } @__alloc_token_size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1, i64 0) + // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new(i64 noundef 8, i64 0) + // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1, i64 0) + // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_aligned(i64 noundef 8, i64 noundef 32, i64 0) + // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1, i64 0) 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; @@ -122,7 +122,7 @@ void may_throw(); // CHECK-LABEL: @_Z27test_exception_handling_newv( TestClass *test_exception_handling_new() { try { - // CHECK: invoke {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}) + // CHECK: invoke {{.*}} ptr @__alloc_token__Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}) // CHECK-NEXT: !alloc_token TestClass *obj = new TestClass(); may_throw(); @@ -134,7 +134,7 @@ TestClass *test_exception_handling_new() { // CHECK-LABEL: @_Z14test_new_classv( TestClass *test_new_class() { - // CHECK: call {{.*}} ptr @__alloc_token_Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token + // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token TestClass *obj = new TestClass(); obj->data[0] = 42; return obj; @@ -142,7 +142,7 @@ TestClass *test_new_class() { // CHECK-LABEL: @_Z20test_new_class_arrayv( TestClass *test_new_class_array() { - // CHECK: call {{.*}} ptr @__alloc_token_Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token + // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token TestClass* arr = new TestClass[10]; arr[0].data[0] = 123; return arr; diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp index 8a59adf03b32f..ac52d5969642f 100644 --- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp +++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp @@ -438,10 +438,8 @@ FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB, TokenAllocName += utostr(TokenID) + "_"; else NewParams.push_back(IntPtrTy); // token ID + TokenAllocName += Callee->getName(); FunctionType *NewFTy = FunctionType::get(RetTy, NewParams, false); - // Remove leading '_' - we add our own. - StringRef No_ = Callee->getName().drop_while([](char C) { return C == '_'; }); - TokenAllocName += No_; FunctionCallee TokenAlloc = Mod.getOrInsertFunction(TokenAllocName, NewFTy); if (Function *F = dyn_cast(TokenAlloc.getCallee())) F->copyAttributesFrom(Callee); // preserve attrs diff --git a/llvm/test/Instrumentation/AllocToken/basic.ll b/llvm/test/Instrumentation/AllocToken/basic.ll index fe673ca9fd524..add258ba4476b 100644 --- a/llvm/test/Instrumentation/AllocToken/basic.ll +++ b/llvm/test/Instrumentation/AllocToken/basic.ll @@ -35,8 +35,8 @@ define ptr @test_cpp_operators() sanitize_alloc_token { ; CHECK-LABEL: define ptr @test_cpp_operators( ; CHECK-SAME: ) #[[ATTR5]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i64 32, i64 3) -; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token_Znam(i64 64, i64 4) +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token__Znwm(i64 32, i64 3) +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token__Znam(i64 64, i64 4) ; CHECK-NEXT: ret ptr [[TMP0]] ; entry: diff --git a/llvm/test/Instrumentation/AllocToken/basic32.ll b/llvm/test/Instrumentation/AllocToken/basic32.ll index ed09fdbaf3d56..5f68898983cc1 100644 --- a/llvm/test/Instrumentation/AllocToken/basic32.ll +++ b/llvm/test/Instrumentation/AllocToken/basic32.ll @@ -23,7 +23,7 @@ define ptr @test_cpp_operators() sanitize_alloc_token { ; CHECK-LABEL: define ptr @test_cpp_operators( ; CHECK-SAME: ) #[[ATTR2]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i32 32, i32 1) +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token__Znwm(i32 32, i32 1) ; CHECK-NEXT: ret ptr [[TMP0]] ; entry: diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll index de968aad9ca3a..bc331331f6061 100644 --- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll +++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll @@ -12,7 +12,7 @@ define ptr @test_extra_libfuncs() sanitize_alloc_token { ; CHECK-LABEL: define ptr @test_extra_libfuncs( ; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[TMP0:%.*]] = call { ptr, i64 } @__alloc_token_size_returning_new(i64 10, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]] +; CHECK-NEXT: [[TMP0:%.*]] = call { ptr, i64 } @__alloc_token___size_returning_new(i64 10, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]] ; CHECK-NEXT: [[PTR1:%.*]] = extractvalue { ptr, i64 } [[TMP0]], 0 ; CHECK-NEXT: ret ptr [[PTR1]] ; @@ -29,8 +29,8 @@ define ptr @test_replaceable_new() sanitize_alloc_token { ; CHECK-LABEL: define ptr @test_replaceable_new( ; CHECK-SAME: ) #[[ATTR1]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_Znwm(i64 32, i64 6985720287680550851), !alloc_token [[META0]] -; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token_Znam(i64 64, i64 6985720287680550851), !alloc_token [[META0]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token__Znwm(i64 32, i64 6985720287680550851), !alloc_token [[META0]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token__Znam(i64 64, i64 6985720287680550851), !alloc_token [[META0]] ; CHECK-NEXT: ret ptr [[TMP0]] ; entry: diff --git a/llvm/test/Instrumentation/AllocToken/fast.ll b/llvm/test/Instrumentation/AllocToken/fast.ll index 60169f308244f..9c79e94453466 100644 --- a/llvm/test/Instrumentation/AllocToken/fast.ll +++ b/llvm/test/Instrumentation/AllocToken/fast.ll @@ -32,8 +32,8 @@ define ptr @test_cpp_operators() sanitize_alloc_token { ; CHECK-LABEL: define ptr @test_cpp_operators( ; CHECK-SAME: ) #[[ATTR4]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[PTR1:%.*]] = call ptr @__alloc_token_0_Znwm(i64 32) -; CHECK-NEXT: [[PTR2:%.*]] = call ptr @__alloc_token_1_Znam(i64 64) +; CHECK-NEXT: [[PTR1:%.*]] = call ptr @__alloc_token_0__Znwm(i64 32) +; CHECK-NEXT: [[PTR2:%.*]] = call ptr @__alloc_token_1__Znam(i64 64) ; CHECK-NEXT: ret ptr [[PTR1]] ; entry: diff --git a/llvm/test/Instrumentation/AllocToken/invoke.ll b/llvm/test/Instrumentation/AllocToken/invoke.ll index 8977ed67563c8..68cca2a6a1a33 100644 --- a/llvm/test/Instrumentation/AllocToken/invoke.ll +++ b/llvm/test/Instrumentation/AllocToken/invoke.ll @@ -32,7 +32,7 @@ define ptr @test_invoke_operator_new() sanitize_alloc_token personality ptr @__g ; CHECK-LABEL: define ptr @test_invoke_operator_new( ; CHECK-SAME: ) #[[ATTR0]] personality ptr @__gxx_personality_v0 { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[TMP0:%.*]] = invoke ptr @__alloc_token_Znwm(i64 32, i64 1) +; CHECK-NEXT: [[TMP0:%.*]] = invoke ptr @__alloc_token__Znwm(i64 32, i64 1) ; CHECK-NEXT: to label %[[NORMAL:.*]] unwind label %[[CLEANUP:.*]] ; CHECK: [[NORMAL]]: ; CHECK-NEXT: ret ptr [[TMP0]] @@ -60,7 +60,7 @@ define ptr @test_complex_invoke_flow() sanitize_alloc_token personality ptr @__g ; CHECK-NEXT: [[TMP0:%.*]] = invoke ptr @__alloc_token_malloc(i64 16, i64 2) ; CHECK-NEXT: to label %[[FIRST_OK:.*]] unwind label %[[CLEANUP1:.*]] ; CHECK: [[FIRST_OK]]: -; CHECK-NEXT: [[TMP1:%.*]] = invoke ptr @__alloc_token_Znwm(i64 32, i64 3) +; CHECK-NEXT: [[TMP1:%.*]] = invoke ptr @__alloc_token__Znwm(i64 32, i64 3) ; CHECK-NEXT: to label %[[SECOND_OK:.*]] unwind label %[[CLEANUP2:.*]] ; CHECK: [[SECOND_OK]]: ; CHECK-NEXT: ret ptr [[TMP0]] From 7ba5526680a9f65c8b7512a9c08a3b3e563151f6 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Fri, 26 Sep 2025 20:54:01 +0200 Subject: [PATCH 10/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?= =?UTF-8?q?changes=20introduced=20through=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.8-beta.1 [skip ci] --- clang/docs/AllocToken.rst | 2 +- clang/docs/ReleaseNotes.rst | 7 +- clang/docs/UsersManual.rst | 20 +- clang/include/clang/Basic/CodeGenOptions.h | 3 +- clang/include/clang/Driver/Options.td | 2 +- clang/include/clang/Driver/SanitizerArgs.h | 1 - clang/lib/CodeGen/CGExpr.cpp | 6 +- clang/lib/Driver/ToolChain.cpp | 3 +- clang/lib/Driver/ToolChains/BareMetal.cpp | 1 - clang/lib/Driver/ToolChains/Linux.cpp | 1 - clang/test/CodeGen/alloc-token-ignorelist.c | 6 +- clang/test/CodeGen/alloc-token.c | 119 +++++++- clang/test/CodeGenCXX/alloc-token.cpp | 259 ++++++++++++++---- llvm/docs/LangRef.rst | 8 + llvm/lib/IR/Verifier.cpp | 10 + .../Transforms/Instrumentation/AllocToken.cpp | 69 +++-- llvm/lib/Transforms/Utils/Local.cpp | 7 +- llvm/test/Instrumentation/AllocToken/basic.ll | 1 - .../Instrumentation/AllocToken/basic32.ll | 1 - .../AllocToken/extralibfuncs.ll | 1 - llvm/test/Instrumentation/AllocToken/fast.ll | 1 - .../test/Instrumentation/AllocToken/ignore.ll | 5 +- .../test/Instrumentation/AllocToken/invoke.ll | 1 - .../Instrumentation/AllocToken/nonlibcalls.ll | 1 - .../test/Instrumentation/AllocToken/remark.ll | 1 - .../SimplifyCFG/merge-calls-alloc-token.ll | 104 +++++++ 26 files changed, 509 insertions(+), 131 deletions(-) create mode 100644 llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll diff --git a/clang/docs/AllocToken.rst b/clang/docs/AllocToken.rst index 6dd321a5b2efc..fb5c060bed939 100644 --- a/clang/docs/AllocToken.rst +++ b/clang/docs/AllocToken.rst @@ -65,7 +65,7 @@ example: // Instrumented: ptr = __alloc_token_malloc(size, ); -In addition, it is typically recommended to configure the following: +The following command-line options affect generated token IDs: * ``-falloc-token-max=`` Configures the maximum number of tokens. No max by default (tokens bounded diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 193b356631995..7e56f4225d984 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -203,9 +203,10 @@ Non-comprehensive list of changes in this release Currently, the use of ``__builtin_dedup_pack`` is limited to template arguments and base specifiers, it also must be used within a template context. -- Introduce support for allocation tokens to enable allocator-level heap - organization strategies. A feature to instrument all allocation functions - with a token ID can be enabled via the ``-fsanitize=alloc-token`` flag. +- Introduce support for :doc:`allocation tokens ` to enable + allocator-level heap organization strategies. A feature to instrument all + allocation functions with a token ID can be enabled via the + ``-fsanitize=alloc-token`` flag. New Compiler Flags ------------------ diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index f1bd348e4e22d..67e356a9ad685 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -2155,13 +2155,11 @@ are listed below. .. option:: -f[no-]sanitize=check1,check2,... - Turn on runtime checks for various forms of undefined or suspicious - behavior. + Turn on runtime checks or mitigations for various forms of undefined or + suspicious behavior. These are disabled by default. - This option controls whether Clang adds runtime checks for various - forms of undefined or suspicious behavior, and is disabled by - default. If a check fails, a diagnostic message is produced at - runtime explaining the problem. The main checks are: + The following options enable runtime checks for various forms of undefined + or suspicious behavior: - .. _opt_fsanitize_address: @@ -2194,8 +2192,14 @@ are listed below. protection against stack-based memory corruption errors. - ``-fsanitize=realtime``: :doc:`RealtimeSanitizer`, a real-time safety checker. - - ``-fsanitize=alloc-token``: :doc:`AllocToken`, - allocation token instrumentation (requires compatible allocator). + + The following options enable runtime mitigations for various forms of + undefined or suspicious behavior: + + - ``-fsanitize=alloc-token``: Enables :doc:`allocation tokens ` + for allocator-level heap organization strategies, such as for security + hardening. It passes type-derived token IDs to a compatible memory + allocator. Requires linking against a token-aware allocator. There are more fine-grained checks available: see the :ref:`list ` of specific kinds of diff --git a/clang/include/clang/Basic/CodeGenOptions.h b/clang/include/clang/Basic/CodeGenOptions.h index eb283a27ed95a..cae06c3c9495a 100644 --- a/clang/include/clang/Basic/CodeGenOptions.h +++ b/clang/include/clang/Basic/CodeGenOptions.h @@ -447,7 +447,8 @@ class CodeGenOptions : public CodeGenOptionsBase { std::optional AllowRuntimeCheckSkipHotCutoff; - /// Maximum number of allocation tokens (0 = no max). + /// Maximum number of allocation tokens (0 = no max), nullopt if none set (use + /// pass default). std::optional AllocTokenMax; /// List of backend command-line options for -fembed-bitcode. diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 064d55d14dcc0..6de5e84fd1168 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -2745,7 +2745,7 @@ defm sanitize_alloc_token_extended : BoolOption<"f", "sanitize-alloc-token-exten } // end -f[no-]sanitize* flags def falloc_token_max_EQ : Joined<["-"], "falloc-token-max=">, - Group, Visibility<[ClangOption, CC1Option, CLOption]>, + Group, Visibility<[ClangOption, CC1Option]>, MetaVarName<"">, HelpText<"Limit to maximum N allocation tokens (0 = no max)">; diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index e0328bd6ed8c3..eea7897e96afd 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -13,7 +13,6 @@ #include "llvm/Option/Arg.h" #include "llvm/Option/ArgList.h" #include "llvm/Transforms/Instrumentation/AddressSanitizerOptions.h" -#include #include #include diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 4bacb629fec39..435fb4ccf4fc4 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -1279,8 +1279,10 @@ void CodeGenFunction::EmitAllocToken(llvm::CallBase *CB, QualType AllocType) { PrintingPolicy Policy(CGM.getContext().getLangOpts()); Policy.SuppressTagKeyword = true; Policy.FullyQualifiedName = true; - std::string TypeName = AllocType.getCanonicalType().getAsString(Policy); - auto *TypeMDS = llvm::MDString::get(CGM.getLLVMContext(), TypeName); + SmallString<64> TypeName; + llvm::raw_svector_ostream TypeNameOS(TypeName); + AllocType.getCanonicalType().print(TypeNameOS, Policy); + auto *TypeMDS = llvm::MDString::get(CGM.getLLVMContext(), TypeNameOS.str()); // Format: !{} auto *MDN = llvm::MDNode::get(CGM.getLLVMContext(), {TypeMDS}); diff --git a/clang/lib/Driver/ToolChain.cpp b/clang/lib/Driver/ToolChain.cpp index 49c89ab0c037f..cce00f13ba6c8 100644 --- a/clang/lib/Driver/ToolChain.cpp +++ b/clang/lib/Driver/ToolChain.cpp @@ -1621,7 +1621,8 @@ SanitizerMask ToolChain::getSupportedSanitizers() const { SanitizerKind::CFICastStrict | SanitizerKind::FloatDivideByZero | SanitizerKind::KCFI | SanitizerKind::UnsignedIntegerOverflow | SanitizerKind::UnsignedShiftBase | SanitizerKind::ImplicitConversion | - SanitizerKind::Nullability | SanitizerKind::LocalBounds; + SanitizerKind::Nullability | SanitizerKind::LocalBounds | + SanitizerKind::AllocToken; if (getTriple().getArch() == llvm::Triple::x86 || getTriple().getArch() == llvm::Triple::x86_64 || getTriple().getArch() == llvm::Triple::arm || diff --git a/clang/lib/Driver/ToolChains/BareMetal.cpp b/clang/lib/Driver/ToolChains/BareMetal.cpp index 3319cdd93b5e2..9b7f58c392885 100644 --- a/clang/lib/Driver/ToolChains/BareMetal.cpp +++ b/clang/lib/Driver/ToolChains/BareMetal.cpp @@ -726,7 +726,6 @@ SanitizerMask BareMetal::getSupportedSanitizers() const { Res |= SanitizerKind::SafeStack; Res |= SanitizerKind::Thread; Res |= SanitizerKind::Scudo; - Res |= SanitizerKind::AllocToken; if (IsX86_64 || IsAArch64 || IsRISCV64) { Res |= SanitizerKind::HWAddress; Res |= SanitizerKind::KernelHWAddress; diff --git a/clang/lib/Driver/ToolChains/Linux.cpp b/clang/lib/Driver/ToolChains/Linux.cpp index 76070b063678f..16e35b08cfbd6 100644 --- a/clang/lib/Driver/ToolChains/Linux.cpp +++ b/clang/lib/Driver/ToolChains/Linux.cpp @@ -819,7 +819,6 @@ SanitizerMask Linux::getSupportedSanitizers() const { Res |= SanitizerKind::KernelAddress; Res |= SanitizerKind::Vptr; Res |= SanitizerKind::SafeStack; - Res |= SanitizerKind::AllocToken; if (IsX86_64 || IsMIPS64 || IsAArch64 || IsLoongArch64) Res |= SanitizerKind::DataFlow; if (IsX86_64 || IsMIPS64 || IsAArch64 || IsX86 || IsArmArch || IsPowerPC64 || diff --git a/clang/test/CodeGen/alloc-token-ignorelist.c b/clang/test/CodeGen/alloc-token-ignorelist.c index f6f82fe3b83e6..954e6e5964773 100644 --- a/clang/test/CodeGen/alloc-token-ignorelist.c +++ b/clang/test/CodeGen/alloc-token-ignorelist.c @@ -1,12 +1,12 @@ // Test AllocToken respects ignorelist for functions and files. // -// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ALLOW +// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-ALLOW // // RUN: echo "fun:excluded_by_all" > %t.func.ignorelist -// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.func.ignorelist -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-FUN +// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.func.ignorelist -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-FUN // // RUN: echo "src:%s" | sed -e 's/\\/\\\\/g' > %t.file.ignorelist -// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.file.ignorelist -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-SRC +// RUN: %clang_cc1 -fsanitize=alloc-token -fsanitize-ignorelist=%t.file.ignorelist -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefixes=CHECK,CHECK-SRC extern void* malloc(unsigned long size); diff --git a/clang/test/CodeGen/alloc-token.c b/clang/test/CodeGen/alloc-token.c index de9b3f48c995f..58fdad55f8d25 100644 --- a/clang/test/CodeGen/alloc-token.c +++ b/clang/test/CodeGen/alloc-token.c @@ -1,5 +1,7 @@ -// RUN: %clang_cc1 -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -O -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -x c -emit-llvm %s -o - | FileCheck %s +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm -disable-llvm-passes %s -o - | FileCheck --check-prefix=CHECK-CODEGEN %s +// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s +// RUN: %clang_cc1 -O -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O1 %s typedef __typeof(sizeof(int)) size_t; @@ -16,30 +18,119 @@ int posix_memalign(void **memptr, size_t alignment, size_t size); void *sink; // CHECK-LABEL: @test_malloc_like( +// CHECK-CODEGEN-LABEL: define dso_local void @test_malloc_like( +// CHECK-CODEGEN-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-CODEGEN-NEXT: [[ENTRY:.*:]] +// CHECK-CODEGEN-NEXT: [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR6:[0-9]+]] +// CHECK-CODEGEN-NEXT: store ptr [[CALL]], ptr @sink, align 8 +// CHECK-CODEGEN-NEXT: [[CALL1:%.*]] = call ptr @calloc(i64 noundef 3, i64 noundef 4) #[[ATTR7:[0-9]+]] +// CHECK-CODEGEN-NEXT: store ptr [[CALL1]], ptr @sink, align 8 +// CHECK-CODEGEN-NEXT: [[TMP0:%.*]] = load ptr, ptr @sink, align 8 +// CHECK-CODEGEN-NEXT: [[CALL2:%.*]] = call ptr @realloc(ptr noundef [[TMP0]], i64 noundef 8) #[[ATTR8:[0-9]+]] +// CHECK-CODEGEN-NEXT: store ptr [[CALL2]], ptr @sink, align 8 +// CHECK-CODEGEN-NEXT: [[TMP1:%.*]] = load ptr, ptr @sink, align 8 +// CHECK-CODEGEN-NEXT: [[CALL3:%.*]] = call ptr @reallocarray(ptr noundef [[TMP1]], i64 noundef 5, i64 noundef 8) +// CHECK-CODEGEN-NEXT: store ptr [[CALL3]], ptr @sink, align 8 +// CHECK-CODEGEN-NEXT: [[CALL4:%.*]] = call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024) #[[ATTR8]] +// CHECK-CODEGEN-NEXT: store ptr [[CALL4]], ptr @sink, align 8 +// CHECK-CODEGEN-NEXT: [[CALL5:%.*]] = call align 16 ptr @memalign(i64 noundef 16, i64 noundef 256) #[[ATTR8]] +// CHECK-CODEGEN-NEXT: store ptr [[CALL5]], ptr @sink, align 8 +// CHECK-CODEGEN-NEXT: [[CALL6:%.*]] = call ptr @valloc(i64 noundef 4096) +// CHECK-CODEGEN-NEXT: store ptr [[CALL6]], ptr @sink, align 8 +// CHECK-CODEGEN-NEXT: [[CALL7:%.*]] = call ptr @pvalloc(i64 noundef 8192) +// CHECK-CODEGEN-NEXT: store ptr [[CALL7]], ptr @sink, align 8 +// CHECK-CODEGEN-NEXT: [[CALL8:%.*]] = call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4) +// CHECK-CODEGEN-NEXT: ret void +// +// CHECK-O0-LABEL: define dso_local void @test_malloc_like( +// CHECK-O0-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-O0-NEXT: [[ENTRY:.*:]] +// CHECK-O0-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 noundef 4, i64 0) #[[ATTR9:[0-9]+]] +// CHECK-O0-NEXT: store ptr [[TMP0]], ptr @sink, align 8 +// CHECK-O0-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0) #[[ATTR10:[0-9]+]] +// CHECK-O0-NEXT: store ptr [[TMP1]], ptr @sink, align 8 +// CHECK-O0-NEXT: [[TMP2:%.*]] = load ptr, ptr @sink, align 8 +// CHECK-O0-NEXT: [[TMP3:%.*]] = call ptr @__alloc_token_realloc(ptr noundef [[TMP2]], i64 noundef 8, i64 0) #[[ATTR11:[0-9]+]] +// CHECK-O0-NEXT: store ptr [[TMP3]], ptr @sink, align 8 +// CHECK-O0-NEXT: [[TMP4:%.*]] = load ptr, ptr @sink, align 8 +// CHECK-O0-NEXT: [[TMP5:%.*]] = call ptr @__alloc_token_reallocarray(ptr noundef [[TMP4]], i64 noundef 5, i64 noundef 8, i64 0) +// CHECK-O0-NEXT: store ptr [[TMP5]], ptr @sink, align 8 +// CHECK-O0-NEXT: [[TMP6:%.*]] = call align 128 ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0) #[[ATTR11]] +// CHECK-O0-NEXT: store ptr [[TMP6]], ptr @sink, align 8 +// CHECK-O0-NEXT: [[TMP7:%.*]] = call align 16 ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0) #[[ATTR11]] +// CHECK-O0-NEXT: store ptr [[TMP7]], ptr @sink, align 8 +// CHECK-O0-NEXT: [[TMP8:%.*]] = call ptr @__alloc_token_valloc(i64 noundef 4096, i64 0) +// CHECK-O0-NEXT: store ptr [[TMP8]], ptr @sink, align 8 +// CHECK-O0-NEXT: [[TMP9:%.*]] = call ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0) +// CHECK-O0-NEXT: store ptr [[TMP9]], ptr @sink, align 8 +// CHECK-O0-NEXT: [[TMP10:%.*]] = call i32 @__alloc_token_posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4, i64 0) +// CHECK-O0-NEXT: ret void +// +// CHECK-O1-LABEL: define dso_local void @test_malloc_like( +// CHECK-O1-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { +// CHECK-O1-NEXT: [[ENTRY:.*:]] +// CHECK-O1-NEXT: [[TMP0:%.*]] = tail call dereferenceable_or_null(4) ptr @__alloc_token_malloc(i64 noundef 4, i64 0) #[[ATTR9:[0-9]+]] +// CHECK-O1-NEXT: store ptr [[TMP0]], ptr @sink, align 8, !tbaa [[TBAA2:![0-9]+]] +// CHECK-O1-NEXT: [[TMP1:%.*]] = tail call dereferenceable_or_null(12) ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0) #[[ATTR10:[0-9]+]] +// CHECK-O1-NEXT: store ptr [[TMP1]], ptr @sink, align 8, !tbaa [[TBAA2]] +// CHECK-O1-NEXT: [[TMP2:%.*]] = tail call dereferenceable_or_null(8) ptr @__alloc_token_realloc(ptr noundef [[TMP1]], i64 noundef 8, i64 0) #[[ATTR11:[0-9]+]] +// CHECK-O1-NEXT: store ptr [[TMP2]], ptr @sink, align 8, !tbaa [[TBAA2]] +// CHECK-O1-NEXT: [[TMP3:%.*]] = tail call dereferenceable_or_null(40) ptr @__alloc_token_reallocarray(ptr noundef [[TMP2]], i64 noundef 5, i64 noundef 8, i64 0) +// CHECK-O1-NEXT: store ptr [[TMP3]], ptr @sink, align 8, !tbaa [[TBAA2]] +// CHECK-O1-NEXT: [[TMP4:%.*]] = tail call align 128 dereferenceable_or_null(1024) ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0) #[[ATTR11]] +// CHECK-O1-NEXT: store ptr [[TMP4]], ptr @sink, align 8, !tbaa [[TBAA2]] +// CHECK-O1-NEXT: [[TMP5:%.*]] = tail call align 16 dereferenceable_or_null(256) ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0) #[[ATTR11]] +// CHECK-O1-NEXT: store ptr [[TMP5]], ptr @sink, align 8, !tbaa [[TBAA2]] +// CHECK-O1-NEXT: [[TMP6:%.*]] = tail call dereferenceable_or_null(4096) ptr @__alloc_token_valloc(i64 noundef 4096, i64 0) +// CHECK-O1-NEXT: store ptr [[TMP6]], ptr @sink, align 8, !tbaa [[TBAA2]] +// CHECK-O1-NEXT: [[TMP7:%.*]] = tail call ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0) +// CHECK-O1-NEXT: store ptr [[TMP7]], ptr @sink, align 8, !tbaa [[TBAA2]] +// CHECK-O1-NEXT: [[TMP8:%.*]] = tail call i32 @__alloc_token_posix_memalign(ptr noundef nonnull @sink, i64 noundef 64, i64 noundef 4, i64 0) #[[ATTR12:[0-9]+]] +// CHECK-O1-NEXT: ret void +// void test_malloc_like() { - // FIXME: Should not be token ID 0! Currently fail to infer the type. - // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0) sink = malloc(sizeof(int)); - // CHECK: call{{.*}} ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0) sink = calloc(3, sizeof(int)); - // CHECK: call{{.*}} ptr @__alloc_token_realloc(ptr noundef {{[^,]*}}, i64 noundef 8, i64 0) sink = realloc(sink, sizeof(long)); - // CHECK: call{{.*}} ptr @__alloc_token_reallocarray(ptr noundef {{[^,]*}}, i64 noundef 5, i64 noundef 8, i64 0) sink = reallocarray(sink, 5, sizeof(long)); - // CHECK: call{{.*}} i32 @__alloc_token_posix_memalign(ptr noundef {{[^,]*}}, i64 noundef 64, i64 noundef 4, i64 0) - posix_memalign(&sink, 64, sizeof(int)); - // CHECK: call align 128{{.*}} ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0) sink = aligned_alloc(128, 1024); - // CHECK: call align 16{{.*}} ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0) sink = memalign(16, 256); - // CHECK: call{{.*}} ptr @__alloc_token_valloc(i64 noundef 4096, i64 0) sink = valloc(4096); - // CHECK: call{{.*}} ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0) sink = pvalloc(8192); + posix_memalign(&sink, 64, sizeof(int)); } // CHECK-LABEL: @no_sanitize_malloc( +// CHECK-CODEGEN-LABEL: define dso_local ptr @no_sanitize_malloc( +// CHECK-CODEGEN-SAME: i64 noundef [[SIZE:%.*]]) #[[ATTR5:[0-9]+]] { +// CHECK-CODEGEN-NEXT: [[ENTRY:.*:]] +// CHECK-CODEGEN-NEXT: [[SIZE_ADDR:%.*]] = alloca i64, align 8 +// CHECK-CODEGEN-NEXT: store i64 [[SIZE]], ptr [[SIZE_ADDR]], align 8 +// CHECK-CODEGEN-NEXT: [[TMP0:%.*]] = load i64, ptr [[SIZE_ADDR]], align 8 +// CHECK-CODEGEN-NEXT: [[CALL:%.*]] = call ptr @malloc(i64 noundef [[TMP0]]) #[[ATTR6]] +// CHECK-CODEGEN-NEXT: ret ptr [[CALL]] +// +// CHECK-O0-LABEL: define dso_local ptr @no_sanitize_malloc( +// CHECK-O0-SAME: i64 noundef [[SIZE:%.*]]) #[[ATTR8:[0-9]+]] { +// CHECK-O0-NEXT: [[ENTRY:.*:]] +// CHECK-O0-NEXT: [[SIZE_ADDR:%.*]] = alloca i64, align 8 +// CHECK-O0-NEXT: store i64 [[SIZE]], ptr [[SIZE_ADDR]], align 8 +// CHECK-O0-NEXT: [[TMP0:%.*]] = load i64, ptr [[SIZE_ADDR]], align 8 +// CHECK-O0-NEXT: [[CALL:%.*]] = call ptr @malloc(i64 noundef [[TMP0]]) #[[ATTR9]] +// CHECK-O0-NEXT: ret ptr [[CALL]] +// +// CHECK-O1-LABEL: define dso_local noalias noundef ptr @no_sanitize_malloc( +// CHECK-O1-SAME: i64 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { +// CHECK-O1-NEXT: [[ENTRY:.*:]] +// CHECK-O1-NEXT: [[CALL:%.*]] = tail call ptr @malloc(i64 noundef [[SIZE]]) #[[ATTR9]] +// CHECK-O1-NEXT: ret ptr [[CALL]] +// void *no_sanitize_malloc(size_t size) __attribute__((no_sanitize("alloc-token"))) { - // CHECK: call ptr @malloc( return malloc(size); } +//. +// CHECK-O1: [[TBAA2]] = !{[[META3:![0-9]+]], [[META3]], i64 0} +// CHECK-O1: [[META3]] = !{!"any pointer", [[META4:![0-9]+]], i64 0} +// CHECK-O1: [[META4]] = !{!"omnipotent char", [[META5:![0-9]+]], i64 0} +// CHECK-O1: [[META5]] = !{!"Simple C/C++ TBAA"} +//. diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp index d115720e6b209..77efc6cf85ae1 100644 --- a/clang/test/CodeGenCXX/alloc-token.cpp +++ b/clang/test/CodeGenCXX/alloc-token.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_cc1 -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s -// RUN: %clang_cc1 -O -fsanitize=alloc-token -falloc-token-max=2147483647 -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 +// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s #include "../Analysis/Inputs/system-header-simulator-cxx.h" extern "C" { @@ -26,84 +26,160 @@ __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: @_Z16test_malloc_likev( +// CHECK-LABEL: define dso_local void @_Z16test_malloc_likev( +// CHECK-SAME: ) #[[ATTR1:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR10:[0-9]+]] +// CHECK-NEXT: store ptr [[CALL]], ptr @sink, align 8 +// CHECK-NEXT: [[CALL1:%.*]] = call ptr @calloc(i64 noundef 3, i64 noundef 4) #[[ATTR11:[0-9]+]] +// CHECK-NEXT: store ptr [[CALL1]], ptr @sink, align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @sink, align 8 +// CHECK-NEXT: [[CALL2:%.*]] = call ptr @realloc(ptr noundef [[TMP0]], i64 noundef 8) #[[ATTR12:[0-9]+]] +// CHECK-NEXT: store ptr [[CALL2]], ptr @sink, align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr @sink, align 8 +// CHECK-NEXT: [[CALL3:%.*]] = call ptr @reallocarray(ptr noundef [[TMP1]], i64 noundef 5, i64 noundef 8) +// CHECK-NEXT: store ptr [[CALL3]], ptr @sink, align 8 +// CHECK-NEXT: [[CALL4:%.*]] = call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024) #[[ATTR12]] +// CHECK-NEXT: store ptr [[CALL4]], ptr @sink, align 8 +// CHECK-NEXT: [[CALL5:%.*]] = call ptr @memalign(i64 noundef 16, i64 noundef 256) +// CHECK-NEXT: store ptr [[CALL5]], ptr @sink, align 8 +// CHECK-NEXT: [[CALL6:%.*]] = call ptr @valloc(i64 noundef 4096) +// CHECK-NEXT: store ptr [[CALL6]], ptr @sink, align 8 +// CHECK-NEXT: [[CALL7:%.*]] = call ptr @pvalloc(i64 noundef 8192) +// CHECK-NEXT: store ptr [[CALL7]], ptr @sink, align 8 +// CHECK-NEXT: [[CALL8:%.*]] = call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4) +// CHECK-NEXT: ret void +// void test_malloc_like() { - // FIXME: Should not be token ID 0! Currently fail to infer the type. - // CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0) sink = malloc(sizeof(int)); - // CHECK: call{{.*}} ptr @__alloc_token_calloc(i64 noundef 3, i64 noundef 4, i64 0) sink = calloc(3, sizeof(int)); - // CHECK: call{{.*}} ptr @__alloc_token_realloc(ptr noundef {{[^,]*}}, i64 noundef 8, i64 0) sink = realloc(sink, sizeof(long)); - // CHECK: call{{.*}} ptr @__alloc_token_reallocarray(ptr noundef {{[^,]*}}, i64 noundef 5, i64 noundef 8, i64 0) sink = reallocarray(sink, 5, sizeof(long)); - // CHECK: call{{.*}} i32 @__alloc_token_posix_memalign(ptr noundef {{[^,]*}}, i64 noundef 64, i64 noundef 4, i64 0) - posix_memalign(&sink, 64, sizeof(int)); - // CHECK: call align 128{{.*}} ptr @__alloc_token_aligned_alloc(i64 noundef 128, i64 noundef 1024, i64 0) sink = aligned_alloc(128, 1024); - // CHECK: call{{.*}} ptr @__alloc_token_memalign(i64 noundef 16, i64 noundef 256, i64 0) sink = memalign(16, 256); - // CHECK: call{{.*}} ptr @__alloc_token_valloc(i64 noundef 4096, i64 0) sink = valloc(4096); - // CHECK: call{{.*}} ptr @__alloc_token_pvalloc(i64 noundef 8192, i64 0) sink = pvalloc(8192); + posix_memalign(&sink, 64, sizeof(int)); } -// CHECK-LABEL: @_Z17test_operator_newv( +// CHECK-LABEL: define dso_local void @_Z17test_operator_newv( +// CHECK-SAME: ) #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR13:[0-9]+]] +// CHECK-NEXT: store ptr [[CALL]], ptr @sink, align 8 +// CHECK-NEXT: [[CALL1:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR10]] +// CHECK-NEXT: store ptr [[CALL1]], ptr @sink, align 8 +// CHECK-NEXT: ret void +// void test_operator_new() { - // FIXME: This should not be token ID 0! - // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 0) sink = __builtin_operator_new(sizeof(int)); - // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 0) sink = ::operator new(sizeof(int)); } -// CHECK-LABEL: @_Z25test_operator_new_nothrowv( +// CHECK-LABEL: define dso_local void @_Z25test_operator_new_nothrowv( +// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14:[0-9]+]] +// CHECK-NEXT: store ptr [[CALL]], ptr @sink, align 8 +// CHECK-NEXT: [[CALL1:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR15:[0-9]+]] +// CHECK-NEXT: store ptr [[CALL1]], ptr @sink, align 8 +// CHECK-NEXT: ret void +// void test_operator_new_nothrow() { - // FIXME: This should not be token ID 0! - // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0) sink = __builtin_operator_new(sizeof(int), std::nothrow); - // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 0) sink = ::operator new(sizeof(int), std::nothrow); } -// CHECK-LABEL: @_Z8test_newv( +// CHECK-LABEL: define dso_local noundef ptr @_Z8test_newv( +// CHECK-SAME: ) #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR13]], !alloc_token [[META2:![0-9]+]] +// CHECK-NEXT: ret ptr [[CALL]] +// int *test_new() { - // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 4, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new int; } -// CHECK-LABEL: @_Z14test_new_arrayv( +// CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_arrayv( +// CHECK-SAME: ) #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 40) #[[ATTR13]], !alloc_token [[META2]] +// CHECK-NEXT: ret ptr [[CALL]] +// int *test_new_array() { - // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 40, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new int[10]; } -// CHECK-LABEL: @_Z16test_new_nothrowv( +// CHECK-LABEL: define dso_local noundef ptr @_Z16test_new_nothrowv( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14]], !alloc_token [[META2]] +// CHECK-NEXT: ret ptr [[CALL]] +// int *test_new_nothrow() { - // CHECK: call {{.*}} ptr @__alloc_token__ZnwmRKSt9nothrow_t(i64 noundef 4, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new (std::nothrow) int; } -// CHECK-LABEL: @_Z22test_new_array_nothrowv( +// CHECK-LABEL: define dso_local noundef ptr @_Z22test_new_array_nothrowv( +// CHECK-SAME: ) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef ptr @_ZnamRKSt9nothrow_t(i64 noundef 40, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14]], !alloc_token [[META2]] +// CHECK-NEXT: ret ptr [[CALL]] +// int *test_new_array_nothrow() { - // CHECK: call {{.*}} ptr @__alloc_token__ZnamRKSt9nothrow_t(i64 noundef 40, ptr {{.*}} @_ZSt7nothrow, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token return new (std::nothrow) int[10]; } -// CHECK-LABEL: @_Z15no_sanitize_newv( -__attribute__((no_sanitize("alloc-token"))) int *no_sanitize_new() { - // CHECK: call {{.*}} ptr @_Znwm(i64 noundef 4) - return new int; -} - -// CHECK-LABEL: @_Z23test_size_returning_newv( +// CHECK-LABEL: define dso_local void @_Z23test_size_returning_newv( +// CHECK-SAME: ) #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [[STRUCT___SIZED_PTR_T:%.*]], align 8 +// CHECK-NEXT: [[REF_TMP1:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8 +// CHECK-NEXT: [[REF_TMP4:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8 +// CHECK-NEXT: [[REF_TMP7:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8 +// CHECK-NEXT: [[CALL:%.*]] = call { ptr, i64 } @__size_returning_new(i64 noundef 8) +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = extractvalue { ptr, i64 } [[CALL]], 0 +// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP0]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP]], i32 0, i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = extractvalue { ptr, i64 } [[CALL]], 1 +// CHECK-NEXT: store i64 [[TMP3]], ptr [[TMP2]], align 8 +// CHECK-NEXT: [[P:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP]], i32 0, i32 0 +// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[P]], align 8 +// CHECK-NEXT: store ptr [[TMP4]], ptr @sink, align 8 +// CHECK-NEXT: [[CALL2:%.*]] = call { ptr, i64 } @__size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1) +// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP6:%.*]] = extractvalue { ptr, i64 } [[CALL2]], 0 +// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP5]], align 8 +// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP1]], i32 0, i32 1 +// CHECK-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i64 } [[CALL2]], 1 +// CHECK-NEXT: store i64 [[TMP8]], ptr [[TMP7]], align 8 +// CHECK-NEXT: [[P3:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP1]], i32 0, i32 0 +// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[P3]], align 8 +// CHECK-NEXT: store ptr [[TMP9]], ptr @sink, align 8 +// CHECK-NEXT: [[CALL5:%.*]] = call { ptr, i64 } @__size_returning_new_aligned(i64 noundef 8, i64 noundef 32) +// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP4]], i32 0, i32 0 +// CHECK-NEXT: [[TMP11:%.*]] = extractvalue { ptr, i64 } [[CALL5]], 0 +// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP10]], align 8 +// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP4]], i32 0, i32 1 +// CHECK-NEXT: [[TMP13:%.*]] = extractvalue { ptr, i64 } [[CALL5]], 1 +// CHECK-NEXT: store i64 [[TMP13]], ptr [[TMP12]], align 8 +// CHECK-NEXT: [[P6:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP4]], i32 0, i32 0 +// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[P6]], align 8 +// CHECK-NEXT: store ptr [[TMP14]], ptr @sink, align 8 +// CHECK-NEXT: [[CALL8:%.*]] = call { ptr, i64 } @__size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1) +// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP7]], i32 0, i32 0 +// CHECK-NEXT: [[TMP16:%.*]] = extractvalue { ptr, i64 } [[CALL8]], 0 +// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP15]], align 8 +// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP7]], i32 0, i32 1 +// CHECK-NEXT: [[TMP18:%.*]] = extractvalue { ptr, i64 } [[CALL8]], 1 +// CHECK-NEXT: store i64 [[TMP18]], ptr [[TMP17]], align 8 +// CHECK-NEXT: [[P9:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP7]], i32 0, i32 0 +// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[P9]], align 8 +// CHECK-NEXT: store ptr [[TMP19]], ptr @sink, align 8 +// CHECK-NEXT: ret void +// void test_size_returning_new() { - // FIXME: This should not be token ID 0! - // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new(i64 noundef 8, i64 0) - // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1, i64 0) - // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_aligned(i64 noundef 8, i64 noundef 32, i64 0) - // CHECK: call { ptr, i64 } @__alloc_token___size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1, i64 0) 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; @@ -119,11 +195,48 @@ class TestClass { void may_throw(); -// CHECK-LABEL: @_Z27test_exception_handling_newv( +// CHECK-LABEL: define dso_local noundef ptr @_Z27test_exception_handling_newv( +// CHECK-SAME: ) #[[ATTR1]] personality ptr @__gxx_personality_v0 { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[RETVAL:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[OBJ:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 +// CHECK-NEXT: [[CALL:%.*]] = invoke noalias noundef nonnull ptr @_Znwm(i64 noundef 72) #[[ATTR13]] +// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]], !alloc_token [[META3:![0-9]+]] +// CHECK: [[INVOKE_CONT]]: +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 72, i1 false) +// CHECK-NEXT: call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[CALL]]) #[[ATTR16:[0-9]+]] +// CHECK-NEXT: store ptr [[CALL]], ptr [[OBJ]], align 8 +// CHECK-NEXT: invoke void @_Z9may_throwv() +// CHECK-NEXT: to label %[[INVOKE_CONT1:.*]] unwind label %[[LPAD]] +// CHECK: [[INVOKE_CONT1]]: +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OBJ]], align 8 +// CHECK-NEXT: store ptr [[TMP0]], ptr [[RETVAL]], align 8 +// CHECK-NEXT: br label %[[RETURN:.*]] +// CHECK: [[LPAD]]: +// CHECK-NEXT: [[TMP1:%.*]] = landingpad { ptr, i32 } +// CHECK-NEXT: catch ptr null +// CHECK-NEXT: [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 0 +// CHECK-NEXT: store ptr [[TMP2]], ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP3:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 1 +// CHECK-NEXT: store i32 [[TMP3]], ptr [[EHSELECTOR_SLOT]], align 4 +// CHECK-NEXT: br label %[[CATCH:.*]] +// CHECK: [[CATCH]]: +// CHECK-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 +// CHECK-NEXT: [[TMP4:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR16]] +// CHECK-NEXT: store ptr null, ptr [[RETVAL]], align 8 +// CHECK-NEXT: call void @__cxa_end_catch() +// CHECK-NEXT: br label %[[RETURN]] +// CHECK: [[TRY_CONT:.*:]] +// CHECK-NEXT: call void @llvm.trap() +// CHECK-NEXT: unreachable +// CHECK: [[RETURN]]: +// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[RETVAL]], align 8 +// CHECK-NEXT: ret ptr [[TMP5]] +// TestClass *test_exception_handling_new() { try { - // CHECK: invoke {{.*}} ptr @__alloc_token__Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}) - // CHECK-NEXT: !alloc_token TestClass *obj = new TestClass(); may_throw(); return obj; @@ -132,26 +245,58 @@ TestClass *test_exception_handling_new() { } } -// CHECK-LABEL: @_Z14test_new_classv( +// CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_classv( +// CHECK-SAME: ) #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[OBJ:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 72) #[[ATTR13]], !alloc_token [[META3]] +// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 72, i1 false) +// CHECK-NEXT: call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[CALL]]) #[[ATTR16]] +// CHECK-NEXT: store ptr [[CALL]], ptr [[OBJ]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OBJ]], align 8 +// CHECK-NEXT: [[DATA:%.*]] = getelementptr inbounds nuw [[CLASS_TESTCLASS:%.*]], ptr [[TMP0]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [16 x i32], ptr [[DATA]], i64 0, i64 0 +// CHECK-NEXT: store i32 42, ptr [[ARRAYIDX]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[OBJ]], align 8 +// CHECK-NEXT: ret ptr [[TMP1]] +// TestClass *test_new_class() { - // CHECK: call {{.*}} ptr @__alloc_token__Znwm(i64 noundef 72, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token TestClass *obj = new TestClass(); obj->data[0] = 42; return obj; } -// CHECK-LABEL: @_Z20test_new_class_arrayv( +// CHECK-LABEL: define dso_local noundef ptr @_Z20test_new_class_arrayv( +// CHECK-SAME: ) #[[ATTR1]] { +// CHECK-NEXT: [[ENTRY:.*]]: +// CHECK-NEXT: [[ARR:%.*]] = alloca ptr, align 8 +// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 728) #[[ATTR13]], !alloc_token [[META3]] +// CHECK-NEXT: store i64 10, ptr [[CALL]], align 16 +// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8 +// CHECK-NEXT: [[ARRAYCTOR_END:%.*]] = getelementptr inbounds [[CLASS_TESTCLASS:%.*]], ptr [[TMP0]], i64 10 +// CHECK-NEXT: br label %[[ARRAYCTOR_LOOP:.*]] +// CHECK: [[ARRAYCTOR_LOOP]]: +// CHECK-NEXT: [[ARRAYCTOR_CUR:%.*]] = phi ptr [ [[TMP0]], %[[ENTRY]] ], [ [[ARRAYCTOR_NEXT:%.*]], %[[ARRAYCTOR_LOOP]] ] +// CHECK-NEXT: call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[ARRAYCTOR_CUR]]) #[[ATTR16]] +// CHECK-NEXT: [[ARRAYCTOR_NEXT]] = getelementptr inbounds [[CLASS_TESTCLASS]], ptr [[ARRAYCTOR_CUR]], i64 1 +// CHECK-NEXT: [[ARRAYCTOR_DONE:%.*]] = icmp eq ptr [[ARRAYCTOR_NEXT]], [[ARRAYCTOR_END]] +// CHECK-NEXT: br i1 [[ARRAYCTOR_DONE]], label %[[ARRAYCTOR_CONT:.*]], label %[[ARRAYCTOR_LOOP]] +// CHECK: [[ARRAYCTOR_CONT]]: +// CHECK-NEXT: store ptr [[TMP0]], ptr [[ARR]], align 8 +// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARR]], align 8 +// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[CLASS_TESTCLASS]], ptr [[TMP1]], i64 0 +// CHECK-NEXT: [[DATA:%.*]] = getelementptr inbounds nuw [[CLASS_TESTCLASS]], ptr [[ARRAYIDX]], i32 0, i32 1 +// CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds [16 x i32], ptr [[DATA]], i64 0, i64 0 +// CHECK-NEXT: store i32 123, ptr [[ARRAYIDX1]], align 8 +// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[ARR]], align 8 +// CHECK-NEXT: ret ptr [[TMP2]] +// TestClass *test_new_class_array() { - // CHECK: call {{.*}} ptr @__alloc_token__Znam(i64 noundef 728, i64 {{[1-9][0-9]*}}){{.*}} !alloc_token TestClass* arr = new TestClass[10]; arr[0].data[0] = 123; return arr; } - -// CHECK-LABEL: @_Z21test_delete_unchangedPiS_( -void test_delete_unchanged(int *x, int *y) { - // CHECK: call void @_ZdlPvm - // CHECK: call void @_ZdaPv - delete x; - delete [] y; -} +//. +// CHECK: [[META2]] = !{!"int"} +// CHECK: [[META3]] = !{!"TestClass"} +//. diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index 1cf7ccc463b38..47ea81a7f861d 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -8400,6 +8400,14 @@ functions, and contains richer semantic information about the type of the allocation. This information is consumed by the ``alloc-token`` pass to instrument such calls with allocation token IDs. +The metadata contains a string with the type of an allocation. + +.. code-block:: none + + call ptr @malloc(i64 64), !alloc_token !0 + + !0 = !{!""} + Module Flags Metadata ===================== diff --git a/llvm/lib/IR/Verifier.cpp b/llvm/lib/IR/Verifier.cpp index 48007be924bda..acd571d9db62a 100644 --- a/llvm/lib/IR/Verifier.cpp +++ b/llvm/lib/IR/Verifier.cpp @@ -535,6 +535,7 @@ class Verifier : public InstVisitor, VerifierSupport { void visitDIAssignIDMetadata(Instruction &I, MDNode *MD); void visitMMRAMetadata(Instruction &I, MDNode *MD); void visitAnnotationMetadata(MDNode *Annotation); + void visitAllocTokenMetadata(Instruction &I, MDNode *MD); void visitAliasScopeMetadata(const MDNode *MD); void visitAliasScopeListMetadata(const MDNode *MD); void visitAccessGroupMetadata(const MDNode *MD); @@ -5332,6 +5333,12 @@ void Verifier::visitAccessGroupMetadata(const MDNode *MD) { } } +void Verifier::visitAllocTokenMetadata(Instruction &I, MDNode *MD) { + Check(isa(I), "!alloc_token should only exist on calls", &I); + Check(MD->getNumOperands() == 1, "!alloc_token must have 1 operand", MD); + Check(isa(MD->getOperand(0)), "expected string", MD); +} + /// verifyInstruction - Verify that an instruction is well formed. /// void Verifier::visitInstruction(Instruction &I) { @@ -5559,6 +5566,9 @@ void Verifier::visitInstruction(Instruction &I) { if (MDNode *Annotation = I.getMetadata(LLVMContext::MD_annotation)) visitAnnotationMetadata(Annotation); + if (MDNode *MD = I.getMetadata(LLVMContext::MD_alloc_token)) + visitAllocTokenMetadata(I, MD); + if (MDNode *N = I.getDebugLoc().getAsMDNode()) { CheckDI(isa(N), "invalid !dbg metadata attachment", &I, N); visitMDNode(*N, AreDebugLocsAllowed::Yes); diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp index ac52d5969642f..330408077c79b 100644 --- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp +++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp @@ -236,10 +236,13 @@ class AllocToken { bool instrumentFunction(Function &F); private: - /// Returns true for !isAllocationFn() functions that are also eligible for - /// instrumentation. - static bool isInstrumentableLibFunc(LibFunc Func, const Value *V, - const TargetLibraryInfo *TLI); + /// Returns the LibFunc (or NotLibFunc) if this call should be instrumented. + std::optional + shouldInstrumentCall(const CallBase &CB, const TargetLibraryInfo &TLI) const; + + /// Returns true for functions that are eligible for instrumentation. + static bool isInstrumentableLibFunc(LibFunc Func, const CallBase &CB, + const TargetLibraryInfo &TLI); /// Returns true for isAllocationFn() functions that we should ignore. static bool ignoreInstrumentableLibFunc(LibFunc Func); @@ -291,21 +294,8 @@ bool AllocToken::instrumentFunction(Function &F) { auto *CB = dyn_cast(&I); if (!CB) continue; - const Function *Callee = CB->getCalledFunction(); - if (!Callee) - continue; - // Ignore nobuiltin of the CallBase, so that we can cover nobuiltin libcalls - // if requested via isInstrumentableLibFunc(). Note that isAllocationFn() is - // returning false for nobuiltin calls. - LibFunc Func; - if (TLI.getLibFunc(*Callee, Func)) { - if (ignoreInstrumentableLibFunc(Func)) - continue; - if (isInstrumentableLibFunc(Func, CB, &TLI)) - AllocCalls.emplace_back(CB, Func); - } else if (Options.Extended && getAllocTokenMetadata(*CB)) { - AllocCalls.emplace_back(CB, NotLibFunc); - } + if (std::optional Func = shouldInstrumentCall(*CB, TLI)) + AllocCalls.emplace_back(CB, Func.value()); } bool Modified = false; @@ -317,18 +307,46 @@ bool AllocToken::instrumentFunction(Function &F) { return Modified; } -bool AllocToken::isInstrumentableLibFunc(LibFunc Func, const Value *V, - const TargetLibraryInfo *TLI) { - if (isAllocationFn(V, TLI)) +std::optional +AllocToken::shouldInstrumentCall(const CallBase &CB, + const TargetLibraryInfo &TLI) const { + const Function *Callee = CB.getCalledFunction(); + if (!Callee) + return std::nullopt; + + // Ignore nobuiltin of the CallBase, so that we can cover nobuiltin libcalls + // if requested via isInstrumentableLibFunc(). Note that isAllocationFn() is + // returning false for nobuiltin calls. + LibFunc Func; + if (TLI.getLibFunc(*Callee, Func)) { + if (isInstrumentableLibFunc(Func, CB, TLI)) + return Func; + } else if (Options.Extended && getAllocTokenMetadata(CB)) { + return NotLibFunc; + } + + return std::nullopt; +} + +bool AllocToken::isInstrumentableLibFunc(LibFunc Func, const CallBase &CB, + const TargetLibraryInfo &TLI) { + if (ignoreInstrumentableLibFunc(Func)) + return false; + + if (isAllocationFn(&CB, &TLI)) return true; switch (Func) { + // These libfuncs don't return normal pointers, and are therefore not handled + // by isAllocationFn(). case LibFunc_posix_memalign: case LibFunc_size_returning_new: case LibFunc_size_returning_new_hot_cold: case LibFunc_size_returning_new_aligned: case LibFunc_size_returning_new_aligned_hot_cold: return true; + + // See comment above ClCoverReplaceableNew. case LibFunc_Znwj: case LibFunc_ZnwjRKSt9nothrow_t: case LibFunc_ZnwjSt11align_val_t: @@ -354,6 +372,7 @@ bool AllocToken::isInstrumentableLibFunc(LibFunc Func, const Value *V, case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t: case LibFunc_ZnamSt11align_val_tRKSt9nothrow_t12__hot_cold_t: return ClCoverReplaceableNew; + default: return false; } @@ -390,7 +409,7 @@ bool AllocToken::replaceAllocationCall(CallBase *CB, LibFunc Func, IRBuilder<> IRB(CB); // Original args. SmallVector NewArgs{CB->args()}; - // Add token ID. + // Add token ID, truncated to IntPtrTy width. NewArgs.push_back(ConstantInt::get(IntPtrTy, TokenID)); assert(TokenAlloc.getFunctionType()->getNumParams() == NewArgs.size()); @@ -420,7 +439,7 @@ FunctionCallee AllocToken::getTokenAllocFunction(const CallBase &CB, if (OriginalFunc != NotLibFunc) { Key = std::make_pair(OriginalFunc, Options.FastABI ? TokenID : 0); auto It = TokenAllocFunctions.find(*Key); - if (LLVM_LIKELY(It != TokenAllocFunctions.end())) + if (It != TokenAllocFunctions.end()) return It->second; } @@ -459,7 +478,7 @@ PreservedAnalyses AllocTokenPass::run(Module &M, ModuleAnalysisManager &MAM) { bool Modified = false; for (Function &F : M) { - if (LLVM_LIKELY(F.empty())) + if (F.empty()) continue; // declaration Modified |= Pass.instrumentFunction(F); } diff --git a/llvm/lib/Transforms/Utils/Local.cpp b/llvm/lib/Transforms/Utils/Local.cpp index 0649e70f4f5c8..1460c7e08fe0a 100644 --- a/llvm/lib/Transforms/Utils/Local.cpp +++ b/llvm/lib/Transforms/Utils/Local.cpp @@ -3026,8 +3026,11 @@ static void combineMetadata(Instruction *K, const Instruction *J, K->setMetadata(Kind, JMD); break; case LLVMContext::MD_alloc_token: - // Preserve !alloc_token if both K and J have it. - K->setMetadata(Kind, JMD); + // Preserve !alloc_token if both K and J have it, and they are equal. + if (KMD == JMD) + K->setMetadata(Kind, JMD); + else + K->setMetadata(Kind, nullptr); break; } } diff --git a/llvm/test/Instrumentation/AllocToken/basic.ll b/llvm/test/Instrumentation/AllocToken/basic.ll index add258ba4476b..099d37df264d6 100644 --- a/llvm/test/Instrumentation/AllocToken/basic.ll +++ b/llvm/test/Instrumentation/AllocToken/basic.ll @@ -2,7 +2,6 @@ ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-unknown-linux-gnu" declare ptr @malloc(i64) declare ptr @calloc(i64, i64) diff --git a/llvm/test/Instrumentation/AllocToken/basic32.ll b/llvm/test/Instrumentation/AllocToken/basic32.ll index 5f68898983cc1..944a452f4b4d7 100644 --- a/llvm/test/Instrumentation/AllocToken/basic32.ll +++ b/llvm/test/Instrumentation/AllocToken/basic32.ll @@ -2,7 +2,6 @@ ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s target datalayout = "e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32-S128" -target triple = "i386-pc-linux-gnu" declare ptr @malloc(i32) declare ptr @_Znwm(i32) diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll index bc331331f6061..3c331fd12a5e0 100644 --- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll +++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll @@ -4,7 +4,6 @@ ; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-unknown-linux-gnu" declare {ptr, i64} @__size_returning_new(i64) diff --git a/llvm/test/Instrumentation/AllocToken/fast.ll b/llvm/test/Instrumentation/AllocToken/fast.ll index 9c79e94453466..19a3ef6bb9ede 100644 --- a/llvm/test/Instrumentation/AllocToken/fast.ll +++ b/llvm/test/Instrumentation/AllocToken/fast.ll @@ -2,7 +2,6 @@ ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-fast-abi -alloc-token-max=3 -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-unknown-linux-gnu" declare ptr @malloc(i64) declare ptr @calloc(i64, i64) diff --git a/llvm/test/Instrumentation/AllocToken/ignore.ll b/llvm/test/Instrumentation/AllocToken/ignore.ll index ff8e85f87f64e..b92a920ed11ee 100644 --- a/llvm/test/Instrumentation/AllocToken/ignore.ll +++ b/llvm/test/Instrumentation/AllocToken/ignore.ll @@ -4,15 +4,14 @@ ; RUN: opt < %s -passes=inferattrs,alloc-token -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-unknown-linux-gnu" declare ptr @strdup(ptr) declare ptr @__strdup(ptr) declare ptr @strndup(ptr, i64) declare ptr @__strndup(ptr, i64) -define ptr @test_ignorable_allocation_functions(ptr %ptr) sanitize_alloc_token { -; CHECK-LABEL: define ptr @test_ignorable_allocation_functions( +define ptr @test_ignored_allocation_functions(ptr %ptr) sanitize_alloc_token { +; CHECK-LABEL: define ptr @test_ignored_allocation_functions( ; CHECK-SAME: ptr [[PTR:%.*]]) #[[ATTR2:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] ; CHECK-NEXT: [[PTR1:%.*]] = call ptr @strdup(ptr [[PTR]]) diff --git a/llvm/test/Instrumentation/AllocToken/invoke.ll b/llvm/test/Instrumentation/AllocToken/invoke.ll index 68cca2a6a1a33..347c99a2e8f8d 100644 --- a/llvm/test/Instrumentation/AllocToken/invoke.ll +++ b/llvm/test/Instrumentation/AllocToken/invoke.ll @@ -2,7 +2,6 @@ ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-unknown-linux-gnu" define ptr @test_invoke_malloc() sanitize_alloc_token personality ptr @__gxx_personality_v0 { ; CHECK-LABEL: define ptr @test_invoke_malloc( diff --git a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll index 3ba5285e0f1ae..e023ab6b11b1e 100644 --- a/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll +++ b/llvm/test/Instrumentation/AllocToken/nonlibcalls.ll @@ -2,7 +2,6 @@ ; RUN: opt < %s -passes=inferattrs,alloc-token -alloc-token-mode=increment -alloc-token-extended -S | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-unknown-linux-gnu" declare ptr @malloc(i64) declare ptr @custom_malloc(i64) diff --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll index 429d806b4a2e7..d58ceb0ef1a1a 100644 --- a/llvm/test/Instrumentation/AllocToken/remark.ll +++ b/llvm/test/Instrumentation/AllocToken/remark.ll @@ -2,7 +2,6 @@ ; RUN: opt < %s -passes=inferattrs,alloc-token -pass-remarks=alloc-token -S 2>&1 | FileCheck %s target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" -target triple = "x86_64-unknown-linux-gnu" declare ptr @malloc(i64) diff --git a/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll b/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll new file mode 100644 index 0000000000000..9bbe3eb371673 --- /dev/null +++ b/llvm/test/Transforms/SimplifyCFG/merge-calls-alloc-token.ll @@ -0,0 +1,104 @@ +; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5 +; RUN: opt < %s -passes=simplifycfg -S | FileCheck %s + +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128" + +declare ptr @_Znwm(i64) + +define ptr @test_merge_alloc_token_same(i1 %b) { +; CHECK-LABEL: define ptr @test_merge_alloc_token_same( +; CHECK-SAME: i1 [[B:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CALL:%.*]] = call ptr @_Znwm(i64 4), !alloc_token [[META0:![0-9]+]] +; CHECK-NEXT: ret ptr [[CALL]] +; +entry: + br i1 %b, label %if.then, label %if.else + +if.then: + %call = call ptr @_Znwm(i64 4), !alloc_token !0 + br label %if.end + +if.else: + %call1 = call ptr @_Znwm(i64 4), !alloc_token !0 + br label %if.end + +if.end: + %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ] + ret ptr %x.0 +} + +define ptr @test_merge_alloc_token_different(i1 %b) { +; CHECK-LABEL: define ptr @test_merge_alloc_token_different( +; CHECK-SAME: i1 [[B:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CALL:%.*]] = call ptr @_Znwm(i64 4) +; CHECK-NEXT: ret ptr [[CALL]] +; +entry: + br i1 %b, label %if.then, label %if.else + +if.then: + %call = call ptr @_Znwm(i64 4), !alloc_token !0 + br label %if.end + +if.else: + %call1 = call ptr @_Znwm(i64 4), !alloc_token !1 + br label %if.end + +if.end: + %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ] + ret ptr %x.0 +} + +define ptr @test_merge_alloc_token_some1(i1 %b) { +; CHECK-LABEL: define ptr @test_merge_alloc_token_some1( +; CHECK-SAME: i1 [[B:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CALL:%.*]] = call ptr @_Znwm(i64 4) +; CHECK-NEXT: ret ptr [[CALL]] +; +entry: + br i1 %b, label %if.then, label %if.else + +if.then: + %call = call ptr @_Znwm(i64 4), !alloc_token !0 + br label %if.end + +if.else: + %call1 = call ptr @_Znwm(i64 4) + br label %if.end + +if.end: + %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ] + ret ptr %x.0 +} + +define ptr @test_merge_alloc_token_some2(i1 %b) { +; CHECK-LABEL: define ptr @test_merge_alloc_token_some2( +; CHECK-SAME: i1 [[B:%.*]]) { +; CHECK-NEXT: [[ENTRY:.*:]] +; CHECK-NEXT: [[CALL:%.*]] = call ptr @_Znwm(i64 4) +; CHECK-NEXT: ret ptr [[CALL]] +; +entry: + br i1 %b, label %if.then, label %if.else + +if.then: + %call = call ptr @_Znwm(i64 4) + br label %if.end + +if.else: + %call1 = call ptr @_Znwm(i64 4), !alloc_token !0 + br label %if.end + +if.end: + %x.0 = phi ptr [ %call, %if.then ], [ %call1, %if.else ] + ret ptr %x.0 +} + +!0 = !{!"int"} +!1 = !{!"char[4]"} +;. +; CHECK: [[META0]] = !{!"int"} +;. From 25ac80237eeb7dd8ae019c86f9863f8b10c46bbf Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Fri, 26 Sep 2025 22:37:49 +0200 Subject: [PATCH 11/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?= =?UTF-8?q?changes=20introduced=20through=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.8-beta.1 [skip ci] --- llvm/include/llvm/Support/SipHash.h | 7 +++++++ llvm/lib/Support/SipHash.cpp | 11 ++++++++--- llvm/lib/Transforms/Instrumentation/AllocToken.cpp | 6 +++--- llvm/test/Instrumentation/AllocToken/extralibfuncs.ll | 6 +++--- llvm/test/Instrumentation/AllocToken/remark.ll | 2 +- llvm/unittests/Support/SipHashTest.cpp | 7 +++++++ 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/llvm/include/llvm/Support/SipHash.h b/llvm/include/llvm/Support/SipHash.h index 910cf59432c69..b090565641526 100644 --- a/llvm/include/llvm/Support/SipHash.h +++ b/llvm/include/llvm/Support/SipHash.h @@ -33,6 +33,13 @@ LLVM_ABI void getSipHash_2_4_64(ArrayRef In, const uint8_t (&K)[16], LLVM_ABI void getSipHash_2_4_128(ArrayRef In, const uint8_t (&K)[16], uint8_t (&Out)[16]); +/// Compute a stable 64-bit hash of the given string. +/// +/// The exact algorithm is the little-endian interpretation of the +/// non-doubled (i.e. 64-bit) result of applying a SipHash-2-4 using +/// a specific seed value which can be found in the source. +LLVM_ABI uint64_t getStableSipHash(StringRef Str); + /// Compute a stable non-zero 16-bit hash of the given string. /// /// The exact algorithm is the little-endian interpretation of the diff --git a/llvm/lib/Support/SipHash.cpp b/llvm/lib/Support/SipHash.cpp index 86dad66420435..382d36f0a8da5 100644 --- a/llvm/lib/Support/SipHash.cpp +++ b/llvm/lib/Support/SipHash.cpp @@ -35,14 +35,19 @@ void llvm::getSipHash_2_4_128(ArrayRef In, const uint8_t (&K)[16], siphash<2, 4>(In.data(), In.size(), K, Out); } -/// Compute an ABI-stable 16-bit hash of the given string. -uint16_t llvm::getPointerAuthStableSipHash(StringRef Str) { +/// Compute an ABI-stable 64-bit hash of the given string. +uint64_t llvm::getStableSipHash(StringRef Str) { static const uint8_t K[16] = {0xb5, 0xd4, 0xc9, 0xeb, 0x79, 0x10, 0x4a, 0x79, 0x6f, 0xec, 0x8b, 0x1b, 0x42, 0x87, 0x81, 0xd4}; uint8_t RawHashBytes[8]; getSipHash_2_4_64(arrayRefFromStringRef(Str), K, RawHashBytes); - uint64_t RawHash = endian::read64le(RawHashBytes); + return endian::read64le(RawHashBytes); +} + +/// Compute an ABI-stable 16-bit hash of the given string. +uint16_t llvm::getPointerAuthStableSipHash(StringRef Str) { + uint64_t RawHash = getStableSipHash(Str); // Produce a non-zero 16-bit discriminator. uint16_t Discriminator = (RawHash % 0xFFFF) + 1; diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp index 330408077c79b..2bc91252e5b4b 100644 --- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp +++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp @@ -40,8 +40,8 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/RandomNumberGenerator.h" +#include "llvm/Support/SipHash.h" #include "llvm/Support/raw_ostream.h" -#include "llvm/Support/xxhash.h" #include #include #include @@ -176,7 +176,7 @@ class RandomMode : public ModeBase { }; /// Implementation for TokenMode::TypeHash. The implementation ensures -/// hashes are stable across different compiler invocations. Uses xxHash as the +/// hashes are stable across different compiler invocations. Uses SipHash as the /// hash function. class TypeHashMode : public ModeBase { public: @@ -185,7 +185,7 @@ class TypeHashMode : public ModeBase { uint64_t operator()(const CallBase &CB, OptimizationRemarkEmitter &ORE) { if (MDNode *N = getAllocTokenMetadata(CB)) { MDString *S = cast(N->getOperand(0)); - return boundedToken(xxHash64(S->getString())); + return boundedToken(getStableSipHash(S->getString())); } remarkNoMetadata(CB, ORE); return ClFallbackToken; diff --git a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll index 3c331fd12a5e0..5f08552c789f3 100644 --- a/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll +++ b/llvm/test/Instrumentation/AllocToken/extralibfuncs.ll @@ -11,7 +11,7 @@ define ptr @test_extra_libfuncs() sanitize_alloc_token { ; CHECK-LABEL: define ptr @test_extra_libfuncs( ; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[TMP0:%.*]] = call { ptr, i64 } @__alloc_token___size_returning_new(i64 10, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]] +; CHECK-NEXT: [[TMP0:%.*]] = call { ptr, i64 } @__alloc_token___size_returning_new(i64 10, i64 2689373973731826898), !alloc_token [[META0:![0-9]+]] ; CHECK-NEXT: [[PTR1:%.*]] = extractvalue { ptr, i64 } [[TMP0]], 0 ; CHECK-NEXT: ret ptr [[PTR1]] ; @@ -28,8 +28,8 @@ define ptr @test_replaceable_new() sanitize_alloc_token { ; CHECK-LABEL: define ptr @test_replaceable_new( ; CHECK-SAME: ) #[[ATTR1]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token__Znwm(i64 32, i64 6985720287680550851), !alloc_token [[META0]] -; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token__Znam(i64 64, i64 6985720287680550851), !alloc_token [[META0]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token__Znwm(i64 32, i64 2689373973731826898), !alloc_token [[META0]] +; CHECK-NEXT: [[TMP1:%.*]] = call ptr @__alloc_token__Znam(i64 64, i64 2689373973731826898), !alloc_token [[META0]] ; CHECK-NEXT: ret ptr [[TMP0]] ; entry: diff --git a/llvm/test/Instrumentation/AllocToken/remark.ll b/llvm/test/Instrumentation/AllocToken/remark.ll index d58ceb0ef1a1a..a2404526ea53f 100644 --- a/llvm/test/Instrumentation/AllocToken/remark.ll +++ b/llvm/test/Instrumentation/AllocToken/remark.ll @@ -12,7 +12,7 @@ define ptr @test_has_metadata() sanitize_alloc_token { ; CHECK-LABEL: define ptr @test_has_metadata( ; CHECK-SAME: ) #[[ATTR1:[0-9]+]] { ; CHECK-NEXT: [[ENTRY:.*:]] -; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 6985720287680550851), !alloc_token [[META0:![0-9]+]] +; CHECK-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 64, i64 2689373973731826898), !alloc_token [[META0:![0-9]+]] ; CHECK-NEXT: ret ptr [[TMP0]] ; entry: diff --git a/llvm/unittests/Support/SipHashTest.cpp b/llvm/unittests/Support/SipHashTest.cpp index 7c557eb488acc..3037e6436e18d 100644 --- a/llvm/unittests/Support/SipHashTest.cpp +++ b/llvm/unittests/Support/SipHashTest.cpp @@ -50,6 +50,13 @@ TEST(SipHashTest, SipHash_2_4_128) { } } +// Tests for the 64-bit stable SipHash wrapper. +TEST(SipHashTest, StableSipHash) { + EXPECT_EQ(0xB2BB69BB0A2AC0F1UL, getStableSipHash("")); + EXPECT_EQ(0x9304ABFF427B72E8UL, getStableSipHash("strlen")); + EXPECT_EQ(0x55F45179A08AE51BUL, getStableSipHash("_ZN1 ind; f")); +} + // Tests for the ptrauth-specific SipHash wrapper. TEST(SipHashTest, PointerAuthSipHash) { // Test some basic cases. From 37031e14604f3e6bdd072ab0d4ef38f903535b16 Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Thu, 2 Oct 2025 16:45:11 +0200 Subject: [PATCH 12/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?= =?UTF-8?q?changes=20introduced=20through=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.8-beta.1 [skip ci] --- llvm/lib/Transforms/Instrumentation/AllocToken.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp index 04861007d1164..782d5a162a314 100644 --- a/llvm/lib/Transforms/Instrumentation/AllocToken.cpp +++ b/llvm/lib/Transforms/Instrumentation/AllocToken.cpp @@ -140,7 +140,9 @@ MDNode *getAllocTokenMetadata(const CallBase &CB) { class ModeBase { public: explicit ModeBase(const IntegerType &TokenTy, uint64_t MaxTokens) - : MaxTokens(MaxTokens ? MaxTokens : TokenTy.getBitMask()) {} + : MaxTokens(MaxTokens ? MaxTokens : TokenTy.getBitMask()) { + assert(MaxTokens <= TokenTy.getBitMask()); + } protected: uint64_t boundedToken(uint64_t Val) const { From 946afaa4fdb9ae4791c3f80f11522c6094e4424f Mon Sep 17 00:00:00 2001 From: Marco Elver Date: Thu, 2 Oct 2025 19:05:02 +0200 Subject: [PATCH 13/13] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20?= =?UTF-8?q?changes=20introduced=20through=20rebase?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Created using spr 1.3.8-beta.1 [skip ci] --- clang/test/CodeGen/alloc-token-lower.c | 50 +----- clang/test/CodeGen/alloc-token.c | 33 +--- clang/test/CodeGenCXX/alloc-token.cpp | 217 ++++--------------------- 3 files changed, 44 insertions(+), 256 deletions(-) diff --git a/clang/test/CodeGen/alloc-token-lower.c b/clang/test/CodeGen/alloc-token-lower.c index 722fd121440e4..75197bb3dbd44 100644 --- a/clang/test/CodeGen/alloc-token-lower.c +++ b/clang/test/CodeGen/alloc-token-lower.c @@ -1,58 +1,22 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6 -// // Test optimization pipelines do not interfere with AllocToken lowering, and we // pass on function attributes correctly. // -// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O0 %s -// RUN: %clang_cc1 -O1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O1 %s -// RUN: %clang_cc1 -O2 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck --check-prefix=CHECK-O2 %s +// RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s +// RUN: %clang_cc1 -O2 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s typedef __typeof(sizeof(int)) size_t; void *malloc(size_t size); -// CHECK-O0-LABEL: define dso_local ptr @test_malloc( -// CHECK-O0-SAME: ) #[[ATTR0:[0-9]+]] { -// CHECK-O0-NEXT: [[ENTRY:.*:]] -// CHECK-O0-NEXT: [[TMP0:%.*]] = call ptr @__alloc_token_malloc(i64 noundef 4, i64 0) #[[ATTR3:[0-9]+]] -// CHECK-O0-NEXT: ret ptr [[TMP0]] -// -// CHECK-O1-LABEL: define dso_local noalias noundef ptr @test_malloc( -// CHECK-O1-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { -// CHECK-O1-NEXT: [[ENTRY:.*:]] -// CHECK-O1-NEXT: [[TMP0:%.*]] = tail call dereferenceable_or_null(4) ptr @__alloc_token_malloc(i64 noundef 4, i64 0) #[[ATTR3:[0-9]+]] -// CHECK-O1-NEXT: ret ptr [[TMP0]] -// -// CHECK-O2-LABEL: define dso_local noalias noundef ptr @test_malloc( -// CHECK-O2-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] { -// CHECK-O2-NEXT: [[ENTRY:.*:]] -// CHECK-O2-NEXT: [[TMP0:%.*]] = tail call dereferenceable_or_null(4) ptr @__alloc_token_malloc(i64 noundef 4, i64 0) #[[ATTR3:[0-9]+]] -// CHECK-O2-NEXT: ret ptr [[TMP0]] -// +// CHECK-LABEL: @test_malloc( +// CHECK: call{{.*}} ptr @__alloc_token_malloc(i64 noundef 4, i64 0) void *test_malloc() { return malloc(sizeof(int)); } -// CHECK-O0-LABEL: define dso_local ptr @no_sanitize_malloc( -// CHECK-O0-SAME: i64 noundef [[SIZE:%.*]]) #[[ATTR2:[0-9]+]] { -// CHECK-O0-NEXT: [[ENTRY:.*:]] -// CHECK-O0-NEXT: [[SIZE_ADDR:%.*]] = alloca i64, align 8 -// CHECK-O0-NEXT: store i64 [[SIZE]], ptr [[SIZE_ADDR]], align 8 -// CHECK-O0-NEXT: [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR3]] -// CHECK-O0-NEXT: ret ptr [[CALL]] -// -// CHECK-O1-LABEL: define dso_local noalias noundef ptr @no_sanitize_malloc( -// CHECK-O1-SAME: i64 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { -// CHECK-O1-NEXT: [[ENTRY:.*:]] -// CHECK-O1-NEXT: [[CALL:%.*]] = tail call dereferenceable_or_null(4) ptr @malloc(i64 noundef 4) #[[ATTR3]] -// CHECK-O1-NEXT: ret ptr [[CALL]] -// -// CHECK-O2-LABEL: define dso_local noalias noundef ptr @no_sanitize_malloc( -// CHECK-O2-SAME: i64 noundef [[SIZE:%.*]]) local_unnamed_addr #[[ATTR2:[0-9]+]] { -// CHECK-O2-NEXT: [[ENTRY:.*:]] -// CHECK-O2-NEXT: [[CALL:%.*]] = tail call dereferenceable_or_null(4) ptr @malloc(i64 noundef 4) #[[ATTR3]] -// CHECK-O2-NEXT: ret ptr [[CALL]] -// +// CHECK-LABEL: @no_sanitize_malloc( +// CHECK: call{{.*}} ptr @malloc(i64 noundef 4) void *no_sanitize_malloc(size_t size) __attribute__((no_sanitize("alloc-token"))) { return malloc(sizeof(int)); } diff --git a/clang/test/CodeGen/alloc-token.c b/clang/test/CodeGen/alloc-token.c index ec789c069f985..d1160adc060ba 100644 --- a/clang/test/CodeGen/alloc-token.c +++ b/clang/test/CodeGen/alloc-token.c @@ -1,4 +1,3 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 6 // RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s typedef __typeof(sizeof(int)) size_t; @@ -16,29 +15,15 @@ int posix_memalign(void **memptr, size_t alignment, size_t size); void *sink; // CHECK-LABEL: define dso_local void @test_malloc_like( -// CHECK-SAME: ) #[[ATTR0:[0-9]+]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR5:[0-9]+]] -// CHECK-NEXT: store ptr [[CALL]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL1:%.*]] = call ptr @calloc(i64 noundef 3, i64 noundef 4) #[[ATTR6:[0-9]+]] -// CHECK-NEXT: store ptr [[CALL1]], ptr @sink, align 8 -// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @sink, align 8 -// CHECK-NEXT: [[CALL2:%.*]] = call ptr @realloc(ptr noundef [[TMP0]], i64 noundef 8) #[[ATTR7:[0-9]+]] -// CHECK-NEXT: store ptr [[CALL2]], ptr @sink, align 8 -// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr @sink, align 8 -// CHECK-NEXT: [[CALL3:%.*]] = call ptr @reallocarray(ptr noundef [[TMP1]], i64 noundef 5, i64 noundef 8) -// CHECK-NEXT: store ptr [[CALL3]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL4:%.*]] = call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024) #[[ATTR7]] -// CHECK-NEXT: store ptr [[CALL4]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL5:%.*]] = call align 16 ptr @memalign(i64 noundef 16, i64 noundef 256) #[[ATTR7]] -// CHECK-NEXT: store ptr [[CALL5]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL6:%.*]] = call ptr @valloc(i64 noundef 4096) -// CHECK-NEXT: store ptr [[CALL6]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL7:%.*]] = call ptr @pvalloc(i64 noundef 8192) -// CHECK-NEXT: store ptr [[CALL7]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL8:%.*]] = call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4) -// CHECK-NEXT: ret void -// +// 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 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)); diff --git a/clang/test/CodeGenCXX/alloc-token.cpp b/clang/test/CodeGenCXX/alloc-token.cpp index 77efc6cf85ae1..52bad9c54fb3b 100644 --- a/clang/test/CodeGenCXX/alloc-token.cpp +++ b/clang/test/CodeGenCXX/alloc-token.cpp @@ -1,4 +1,3 @@ -// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --version 5 // RUN: %clang_cc1 -fsanitize=alloc-token -triple x86_64-linux-gnu -std=c++20 -fexceptions -fcxx-exceptions -emit-llvm -disable-llvm-passes %s -o - | FileCheck %s #include "../Analysis/Inputs/system-header-simulator-cxx.h" @@ -27,29 +26,15 @@ __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-SAME: ) #[[ATTR1:[0-9]+]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[CALL:%.*]] = call ptr @malloc(i64 noundef 4) #[[ATTR10:[0-9]+]] -// CHECK-NEXT: store ptr [[CALL]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL1:%.*]] = call ptr @calloc(i64 noundef 3, i64 noundef 4) #[[ATTR11:[0-9]+]] -// CHECK-NEXT: store ptr [[CALL1]], ptr @sink, align 8 -// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr @sink, align 8 -// CHECK-NEXT: [[CALL2:%.*]] = call ptr @realloc(ptr noundef [[TMP0]], i64 noundef 8) #[[ATTR12:[0-9]+]] -// CHECK-NEXT: store ptr [[CALL2]], ptr @sink, align 8 -// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr @sink, align 8 -// CHECK-NEXT: [[CALL3:%.*]] = call ptr @reallocarray(ptr noundef [[TMP1]], i64 noundef 5, i64 noundef 8) -// CHECK-NEXT: store ptr [[CALL3]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL4:%.*]] = call align 128 ptr @aligned_alloc(i64 noundef 128, i64 noundef 1024) #[[ATTR12]] -// CHECK-NEXT: store ptr [[CALL4]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL5:%.*]] = call ptr @memalign(i64 noundef 16, i64 noundef 256) -// CHECK-NEXT: store ptr [[CALL5]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL6:%.*]] = call ptr @valloc(i64 noundef 4096) -// CHECK-NEXT: store ptr [[CALL6]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL7:%.*]] = call ptr @pvalloc(i64 noundef 8192) -// CHECK-NEXT: store ptr [[CALL7]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL8:%.*]] = call i32 @posix_memalign(ptr noundef @sink, i64 noundef 64, i64 noundef 4) -// CHECK-NEXT: ret void -// +// 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 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)); @@ -63,122 +48,50 @@ void test_malloc_like() { } // CHECK-LABEL: define dso_local void @_Z17test_operator_newv( -// CHECK-SAME: ) #[[ATTR1]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR13:[0-9]+]] -// CHECK-NEXT: store ptr [[CALL]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL1:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR10]] -// CHECK-NEXT: store ptr [[CALL1]], ptr @sink, align 8 -// CHECK-NEXT: ret void -// +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) 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-SAME: ) #[[ATTR0:[0-9]+]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14:[0-9]+]] -// CHECK-NEXT: store ptr [[CALL]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL1:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR15:[0-9]+]] -// CHECK-NEXT: store ptr [[CALL1]], ptr @sink, align 8 -// CHECK-NEXT: ret void -// +// 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) 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-SAME: ) #[[ATTR1]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 4) #[[ATTR13]], !alloc_token [[META2:![0-9]+]] -// CHECK-NEXT: ret ptr [[CALL]] -// +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 4){{.*}} !alloc_token [[META_INT:![0-9]+]] int *test_new() { return new int; } // CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_arrayv( -// CHECK-SAME: ) #[[ATTR1]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 40) #[[ATTR13]], !alloc_token [[META2]] -// CHECK-NEXT: ret ptr [[CALL]] -// +// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 40){{.*}} !alloc_token [[META_INT]] int *test_new_array() { return new int[10]; } // CHECK-LABEL: define dso_local noundef ptr @_Z16test_new_nothrowv( -// CHECK-SAME: ) #[[ATTR0]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14]], !alloc_token [[META2]] -// CHECK-NEXT: ret ptr [[CALL]] -// +// CHECK: call noalias noundef ptr @_ZnwmRKSt9nothrow_t(i64 noundef 4, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow){{.*}} !alloc_token [[META_INT]] int *test_new_nothrow() { return new (std::nothrow) int; } // CHECK-LABEL: define dso_local noundef ptr @_Z22test_new_array_nothrowv( -// CHECK-SAME: ) #[[ATTR0]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef ptr @_ZnamRKSt9nothrow_t(i64 noundef 40, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow) #[[ATTR14]], !alloc_token [[META2]] -// CHECK-NEXT: ret ptr [[CALL]] -// +// CHECK: call noalias noundef ptr @_ZnamRKSt9nothrow_t(i64 noundef 40, ptr noundef nonnull align 1 dereferenceable(1) @_ZSt7nothrow){{.*}} !alloc_token [[META_INT]] int *test_new_array_nothrow() { return new (std::nothrow) int[10]; } // CHECK-LABEL: define dso_local void @_Z23test_size_returning_newv( -// CHECK-SAME: ) #[[ATTR1]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[REF_TMP:%.*]] = alloca [[STRUCT___SIZED_PTR_T:%.*]], align 8 -// CHECK-NEXT: [[REF_TMP1:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8 -// CHECK-NEXT: [[REF_TMP4:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8 -// CHECK-NEXT: [[REF_TMP7:%.*]] = alloca [[STRUCT___SIZED_PTR_T]], align 8 -// CHECK-NEXT: [[CALL:%.*]] = call { ptr, i64 } @__size_returning_new(i64 noundef 8) -// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP]], i32 0, i32 0 -// CHECK-NEXT: [[TMP1:%.*]] = extractvalue { ptr, i64 } [[CALL]], 0 -// CHECK-NEXT: store ptr [[TMP1]], ptr [[TMP0]], align 8 -// CHECK-NEXT: [[TMP2:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP]], i32 0, i32 1 -// CHECK-NEXT: [[TMP3:%.*]] = extractvalue { ptr, i64 } [[CALL]], 1 -// CHECK-NEXT: store i64 [[TMP3]], ptr [[TMP2]], align 8 -// CHECK-NEXT: [[P:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP]], i32 0, i32 0 -// CHECK-NEXT: [[TMP4:%.*]] = load ptr, ptr [[P]], align 8 -// CHECK-NEXT: store ptr [[TMP4]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL2:%.*]] = call { ptr, i64 } @__size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1) -// CHECK-NEXT: [[TMP5:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP1]], i32 0, i32 0 -// CHECK-NEXT: [[TMP6:%.*]] = extractvalue { ptr, i64 } [[CALL2]], 0 -// CHECK-NEXT: store ptr [[TMP6]], ptr [[TMP5]], align 8 -// CHECK-NEXT: [[TMP7:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP1]], i32 0, i32 1 -// CHECK-NEXT: [[TMP8:%.*]] = extractvalue { ptr, i64 } [[CALL2]], 1 -// CHECK-NEXT: store i64 [[TMP8]], ptr [[TMP7]], align 8 -// CHECK-NEXT: [[P3:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP1]], i32 0, i32 0 -// CHECK-NEXT: [[TMP9:%.*]] = load ptr, ptr [[P3]], align 8 -// CHECK-NEXT: store ptr [[TMP9]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL5:%.*]] = call { ptr, i64 } @__size_returning_new_aligned(i64 noundef 8, i64 noundef 32) -// CHECK-NEXT: [[TMP10:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP4]], i32 0, i32 0 -// CHECK-NEXT: [[TMP11:%.*]] = extractvalue { ptr, i64 } [[CALL5]], 0 -// CHECK-NEXT: store ptr [[TMP11]], ptr [[TMP10]], align 8 -// CHECK-NEXT: [[TMP12:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP4]], i32 0, i32 1 -// CHECK-NEXT: [[TMP13:%.*]] = extractvalue { ptr, i64 } [[CALL5]], 1 -// CHECK-NEXT: store i64 [[TMP13]], ptr [[TMP12]], align 8 -// CHECK-NEXT: [[P6:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP4]], i32 0, i32 0 -// CHECK-NEXT: [[TMP14:%.*]] = load ptr, ptr [[P6]], align 8 -// CHECK-NEXT: store ptr [[TMP14]], ptr @sink, align 8 -// CHECK-NEXT: [[CALL8:%.*]] = call { ptr, i64 } @__size_returning_new_aligned_hot_cold(i64 noundef 8, i64 noundef 32, i8 noundef zeroext 1) -// CHECK-NEXT: [[TMP15:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP7]], i32 0, i32 0 -// CHECK-NEXT: [[TMP16:%.*]] = extractvalue { ptr, i64 } [[CALL8]], 0 -// CHECK-NEXT: store ptr [[TMP16]], ptr [[TMP15]], align 8 -// CHECK-NEXT: [[TMP17:%.*]] = getelementptr inbounds nuw { ptr, i64 }, ptr [[REF_TMP7]], i32 0, i32 1 -// CHECK-NEXT: [[TMP18:%.*]] = extractvalue { ptr, i64 } [[CALL8]], 1 -// CHECK-NEXT: store i64 [[TMP18]], ptr [[TMP17]], align 8 -// CHECK-NEXT: [[P9:%.*]] = getelementptr inbounds nuw [[STRUCT___SIZED_PTR_T]], ptr [[REF_TMP7]], i32 0, i32 0 -// CHECK-NEXT: [[TMP19:%.*]] = load ptr, ptr [[P9]], align 8 -// CHECK-NEXT: store ptr [[TMP19]], ptr @sink, align 8 -// CHECK-NEXT: ret void -// +// CHECK: call { ptr, i64 } @__size_returning_new(i64 noundef 8) +// CHECK: call { ptr, i64 } @__size_returning_new_hot_cold(i64 noundef 8, i8 noundef zeroext 1) +// 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() { sink = __size_returning_new(sizeof(long)).p; sink = __size_returning_new_hot_cold(sizeof(long), __hot_cold_t{1}).p; @@ -196,45 +109,8 @@ class TestClass { void may_throw(); // CHECK-LABEL: define dso_local noundef ptr @_Z27test_exception_handling_newv( -// CHECK-SAME: ) #[[ATTR1]] personality ptr @__gxx_personality_v0 { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[RETVAL:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: [[OBJ:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: [[EXN_SLOT:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: [[EHSELECTOR_SLOT:%.*]] = alloca i32, align 4 -// CHECK-NEXT: [[CALL:%.*]] = invoke noalias noundef nonnull ptr @_Znwm(i64 noundef 72) #[[ATTR13]] -// CHECK-NEXT: to label %[[INVOKE_CONT:.*]] unwind label %[[LPAD:.*]], !alloc_token [[META3:![0-9]+]] -// CHECK: [[INVOKE_CONT]]: -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 72, i1 false) -// CHECK-NEXT: call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[CALL]]) #[[ATTR16:[0-9]+]] -// CHECK-NEXT: store ptr [[CALL]], ptr [[OBJ]], align 8 -// CHECK-NEXT: invoke void @_Z9may_throwv() -// CHECK-NEXT: to label %[[INVOKE_CONT1:.*]] unwind label %[[LPAD]] -// CHECK: [[INVOKE_CONT1]]: -// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OBJ]], align 8 -// CHECK-NEXT: store ptr [[TMP0]], ptr [[RETVAL]], align 8 -// CHECK-NEXT: br label %[[RETURN:.*]] -// CHECK: [[LPAD]]: -// CHECK-NEXT: [[TMP1:%.*]] = landingpad { ptr, i32 } -// CHECK-NEXT: catch ptr null -// CHECK-NEXT: [[TMP2:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 0 -// CHECK-NEXT: store ptr [[TMP2]], ptr [[EXN_SLOT]], align 8 -// CHECK-NEXT: [[TMP3:%.*]] = extractvalue { ptr, i32 } [[TMP1]], 1 -// CHECK-NEXT: store i32 [[TMP3]], ptr [[EHSELECTOR_SLOT]], align 4 -// CHECK-NEXT: br label %[[CATCH:.*]] -// CHECK: [[CATCH]]: -// CHECK-NEXT: [[EXN:%.*]] = load ptr, ptr [[EXN_SLOT]], align 8 -// CHECK-NEXT: [[TMP4:%.*]] = call ptr @__cxa_begin_catch(ptr [[EXN]]) #[[ATTR16]] -// CHECK-NEXT: store ptr null, ptr [[RETVAL]], align 8 -// CHECK-NEXT: call void @__cxa_end_catch() -// CHECK-NEXT: br label %[[RETURN]] -// CHECK: [[TRY_CONT:.*:]] -// CHECK-NEXT: call void @llvm.trap() -// CHECK-NEXT: unreachable -// CHECK: [[RETURN]]: -// CHECK-NEXT: [[TMP5:%.*]] = load ptr, ptr [[RETVAL]], align 8 -// CHECK-NEXT: ret ptr [[TMP5]] -// +// CHECK: invoke noalias noundef nonnull ptr @_Znwm(i64 noundef 72) +// CHECK-NEXT: !alloc_token [[META_TESTCLASS:![0-9]+]] TestClass *test_exception_handling_new() { try { TestClass *obj = new TestClass(); @@ -246,20 +122,7 @@ TestClass *test_exception_handling_new() { } // CHECK-LABEL: define dso_local noundef ptr @_Z14test_new_classv( -// CHECK-SAME: ) #[[ATTR1]] { -// CHECK-NEXT: [[ENTRY:.*:]] -// CHECK-NEXT: [[OBJ:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znwm(i64 noundef 72) #[[ATTR13]], !alloc_token [[META3]] -// CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 [[CALL]], i8 0, i64 72, i1 false) -// CHECK-NEXT: call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[CALL]]) #[[ATTR16]] -// CHECK-NEXT: store ptr [[CALL]], ptr [[OBJ]], align 8 -// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[OBJ]], align 8 -// CHECK-NEXT: [[DATA:%.*]] = getelementptr inbounds nuw [[CLASS_TESTCLASS:%.*]], ptr [[TMP0]], i32 0, i32 1 -// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [16 x i32], ptr [[DATA]], i64 0, i64 0 -// CHECK-NEXT: store i32 42, ptr [[ARRAYIDX]], align 8 -// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[OBJ]], align 8 -// CHECK-NEXT: ret ptr [[TMP1]] -// +// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef 72){{.*}} !alloc_token [[META_TESTCLASS]] TestClass *test_new_class() { TestClass *obj = new TestClass(); obj->data[0] = 42; @@ -267,36 +130,12 @@ TestClass *test_new_class() { } // CHECK-LABEL: define dso_local noundef ptr @_Z20test_new_class_arrayv( -// CHECK-SAME: ) #[[ATTR1]] { -// CHECK-NEXT: [[ENTRY:.*]]: -// CHECK-NEXT: [[ARR:%.*]] = alloca ptr, align 8 -// CHECK-NEXT: [[CALL:%.*]] = call noalias noundef nonnull ptr @_Znam(i64 noundef 728) #[[ATTR13]], !alloc_token [[META3]] -// CHECK-NEXT: store i64 10, ptr [[CALL]], align 16 -// CHECK-NEXT: [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[CALL]], i64 8 -// CHECK-NEXT: [[ARRAYCTOR_END:%.*]] = getelementptr inbounds [[CLASS_TESTCLASS:%.*]], ptr [[TMP0]], i64 10 -// CHECK-NEXT: br label %[[ARRAYCTOR_LOOP:.*]] -// CHECK: [[ARRAYCTOR_LOOP]]: -// CHECK-NEXT: [[ARRAYCTOR_CUR:%.*]] = phi ptr [ [[TMP0]], %[[ENTRY]] ], [ [[ARRAYCTOR_NEXT:%.*]], %[[ARRAYCTOR_LOOP]] ] -// CHECK-NEXT: call void @_ZN9TestClassC1Ev(ptr noundef nonnull align 8 dereferenceable(72) [[ARRAYCTOR_CUR]]) #[[ATTR16]] -// CHECK-NEXT: [[ARRAYCTOR_NEXT]] = getelementptr inbounds [[CLASS_TESTCLASS]], ptr [[ARRAYCTOR_CUR]], i64 1 -// CHECK-NEXT: [[ARRAYCTOR_DONE:%.*]] = icmp eq ptr [[ARRAYCTOR_NEXT]], [[ARRAYCTOR_END]] -// CHECK-NEXT: br i1 [[ARRAYCTOR_DONE]], label %[[ARRAYCTOR_CONT:.*]], label %[[ARRAYCTOR_LOOP]] -// CHECK: [[ARRAYCTOR_CONT]]: -// CHECK-NEXT: store ptr [[TMP0]], ptr [[ARR]], align 8 -// CHECK-NEXT: [[TMP1:%.*]] = load ptr, ptr [[ARR]], align 8 -// CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [[CLASS_TESTCLASS]], ptr [[TMP1]], i64 0 -// CHECK-NEXT: [[DATA:%.*]] = getelementptr inbounds nuw [[CLASS_TESTCLASS]], ptr [[ARRAYIDX]], i32 0, i32 1 -// CHECK-NEXT: [[ARRAYIDX1:%.*]] = getelementptr inbounds [16 x i32], ptr [[DATA]], i64 0, i64 0 -// CHECK-NEXT: store i32 123, ptr [[ARRAYIDX1]], align 8 -// CHECK-NEXT: [[TMP2:%.*]] = load ptr, ptr [[ARR]], align 8 -// CHECK-NEXT: ret ptr [[TMP2]] -// +// CHECK: call noalias noundef nonnull ptr @_Znam(i64 noundef 728){{.*}} !alloc_token [[META_TESTCLASS]] TestClass *test_new_class_array() { TestClass* arr = new TestClass[10]; arr[0].data[0] = 123; return arr; } -//. -// CHECK: [[META2]] = !{!"int"} -// CHECK: [[META3]] = !{!"TestClass"} -//. + +// CHECK: [[META_INT]] = !{!"int"} +// CHECK: [[META_TESTCLASS]] = !{!"TestClass"}