From f548a8c3e9f3249e76ea86c61a06f7ce95532d54 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 11 Aug 2026 16:13:04 +0000 Subject: [PATCH] [IR] Add !sensitive metadata for sensitive stack objects Register a fixed metadata kind marking an alloca whose contents should not be left readable in the stack frame once the function is done with them. The metadata is only used as a flag: its presence on the alloca is the entire signal and the node must be empty, with the contents of the node reserved for future use. The marked objects are what the "sensitive" mode of the "zeroize-stack" attribute clears, so the LangRef entry is written around the direction of the fallback between the two. The metadata buys precision and is not itself part of the guarantee. Dropping it is always permitted, and where the marked set has stopped describing the frame the response is to clear more, at the limit every stack slot the function used, never to clear less. Absence of the metadata on an object is not a statement that the object is insensitive, and a transform may not shrink the set of objects a function clears on the strength of what is or is not marked. What makes losing the metadata safe is that the mode's floor already covers every frame object with no source-level provenance, such as spill slots, the callee-save area and alignment padding, whether or not anything is marked. Parsing, printing and bitcode serialization of metadata attachments are generic, so the round-trip test needs no new parser code; registering the kind gives consumers a fixed ID to look the attachment up by. No pass or backend reads the metadata yet, and which transforms should carry it across the objects they create is left to a later change. The name is a recommendation still awaiting sign-off on trailofbits/vspells-ct-internal-notes#64. --- llvm/docs/LangRef.md | 54 ++++++++++++++++++++- llvm/include/llvm/IR/FixedMetadataKinds.def | 1 + llvm/test/Assembler/sensitive-metadata.ll | 31 ++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 llvm/test/Assembler/sensitive-metadata.ll diff --git a/llvm/docs/LangRef.md b/llvm/docs/LangRef.md index b52eb395d5e82..678762a756d4c 100644 --- a/llvm/docs/LangRef.md +++ b/llvm/docs/LangRef.md @@ -2658,7 +2658,8 @@ fn -> other_fn -> other_fn ; fn is norecurse how much of the frame is cleared: - `"used"` clears every stack slot the function used. - - `"sensitive"` clears the slots that sensitivity metadata identifies, + - `"sensitive"` clears the slots that + {ref}`sensitivity metadata ` identifies, together with every slot whose contents cannot be traced back to a source-level object. The second half is not optional: spill slots, the callee-save area, and alignment padding can all hold copies of data that @@ -8012,6 +8013,57 @@ call void @llvm.memcpy.p1.p1.i64(ptr addrspace(1) %d, !"nvvm.l2_prefetch_size", !"128B" } ``` +(md_sensitive)= + +#### '`sensitive`' Metadata + +`sensitive` metadata may be attached to an `alloca` to record that the object +it allocates holds data that should not be left readable in the stack frame +once the function is done with it. It identifies the objects that the +`"sensitive"` mode of the `"zeroize-stack"` function attribute clears. + +This metadata is only used as a flag, so the associated node must be empty. Its +presence on the `alloca` is the entire signal; the contents of the node are +reserved for future use. + +```llvm +%round_keys = alloca [176 x i8], align 16, !sensitive !0 + +... +!0 = !{} +``` + +The metadata is a hint that lets a function clear less of its frame than it +would otherwise have to. It is not itself part of any guarantee, and no +guarantee is conditioned on it being present, accurate, or complete. The +obligation to clear a frame comes from the `"zeroize-stack"` attribute alone, +and the `"sensitive"` mode of that attribute clears every frame object whose +contents cannot be traced back to a source-level object — spill slots, the +callee-save area, alignment padding — whether or not anything is marked. + +Transforms must respect the one-directional rule this sets up: + +- Dropping the metadata is always permitted. A transform is never required to + preserve or propagate it in order to be correct, and no correctness argument + may rest on it having survived. Where the marked set has stopped describing + the frame, because a marked object was split, merged, replaced, or promoted + and the metadata did not follow, the response is to clear more, falling back + at the limit to clearing every stack slot the function used. It is never to + clear less. +- Attaching the metadata to an object that was not marked before is likewise + permitted, and can only widen what is cleared. +- The absence of the metadata on a frame object is not a statement that the + object is insensitive. A transform may not conclude from an object being + unmarked that its contents need not be cleared, and may not shrink the set of + objects a function clears on the strength of what is or is not marked. + Clearing only the marked objects together with the objects of unknown + provenance is the request that the `"sensitive"` mode expresses; it is not a + licence for a transform to narrow that set further. + +Whether an individual transform propagates this metadata across the objects it +creates is therefore a question of precision rather than of correctness, and is +specified separately. + (llvm.loop)= #### '`llvm.loop`' diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def index 350adfc27bef4..f415e8bf70fe6 100644 --- a/llvm/include/llvm/IR/FixedMetadataKinds.def +++ b/llvm/include/llvm/IR/FixedMetadataKinds.def @@ -67,3 +67,4 @@ LLVM_FIXED_MD_KIND(MD_mem_cache_hint, "mem.cache_hint", 52) LLVM_FIXED_MD_KIND(MD_block_uniformity_profile, "block.uniformity.profile", 53) LLVM_FIXED_MD_KIND(MD_callgraph, "callgraph", 54) LLVM_FIXED_MD_KIND(MD_metadata_section_kind, "metadata_section_kind", 55) +LLVM_FIXED_MD_KIND(MD_sensitive, "sensitive", 56) diff --git a/llvm/test/Assembler/sensitive-metadata.ll b/llvm/test/Assembler/sensitive-metadata.ll new file mode 100644 index 0000000000000..70af29adfdc8a --- /dev/null +++ b/llvm/test/Assembler/sensitive-metadata.ll @@ -0,0 +1,31 @@ +; Textual and bitcode round-trips of !sensitive on an alloca. +; RUN: llvm-as < %s | llvm-dis | FileCheck %s +; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s + +define void @marked() { +; CHECK-LABEL: define void @marked() { +; CHECK-NEXT: [[ROUND_KEYS:%.*]] = alloca [176 x i8], align 16, !sensitive [[META0:![0-9]+]] +; CHECK-NEXT: ret void +; + %round_keys = alloca [176 x i8], align 16, !sensitive !0 + ret void +} + +; The metadata is a flag, so every marked object shares the one empty node. +define void @marked_twice() { +; CHECK-LABEL: define void @marked_twice() { +; CHECK-NEXT: [[KEY:%.*]] = alloca i64, align 8, !sensitive [[META0]] +; CHECK-NEXT: [[NONCE:%.*]] = alloca i64, align 8, !sensitive [[META0]] +; CHECK-NEXT: [[SCRATCH:%.*]] = alloca i64, align 8 +; CHECK-NEXT: ret void +; + %key = alloca i64, align 8, !sensitive !0 + %nonce = alloca i64, align 8, !sensitive !0 + %scratch = alloca i64, align 8 + ret void +} + +!0 = !{} +;. +; CHECK: [[META0]] = !{} +;.