Skip to content

Commit

Permalink
[MTE] Apply alignment / size in AsmPrinter rather than IR (llvm#111918)
Browse files Browse the repository at this point in the history
This makes sure no optimizations are applied that assume the
bigger alignment or size, which could be incorrect if we link
together with non-instrumented code.
  • Loading branch information
fmayer authored Dec 17, 2024
1 parent 58cfa39 commit 514580b
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 170 deletions.
45 changes: 40 additions & 5 deletions clang/lib/CodeGen/SanitizerMetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,37 @@ static SanitizerMask expandKernelSanitizerMasks(SanitizerMask Mask) {
return Mask;
}

static bool shouldTagGlobal(const llvm::GlobalVariable &G) {
// For now, don't instrument constant data, as it'll be in .rodata anyway. It
// may be worth instrumenting these in future to stop them from being used as
// gadgets.
if (G.getName().starts_with("llvm.") || G.isThreadLocal() || G.isConstant())
return false;

// Globals can be placed implicitly or explicitly in sections. There's two
// different types of globals that meet this criteria that cause problems:
// 1. Function pointers that are going into various init arrays (either
// explicitly through `__attribute__((section(<foo>)))` or implicitly
// through `__attribute__((constructor)))`, such as ".(pre)init(_array)",
// ".fini(_array)", ".ctors", and ".dtors". These function pointers end up
// overaligned and overpadded, making iterating over them problematic, and
// each function pointer is individually tagged (so the iteration over
// them causes SIGSEGV/MTE[AS]ERR).
// 2. Global variables put into an explicit section, where the section's name
// is a valid C-style identifier. The linker emits a `__start_<name>` and
// `__stop_<name>` symbol for the section, so that you can iterate over
// globals within this section. Unfortunately, again, these globals would
// be tagged and so iteration causes SIGSEGV/MTE[AS]ERR.
//
// To mitigate both these cases, and because specifying a section is rare
// outside of these two cases, disable MTE protection for globals in any
// section.
if (G.hasSection())
return false;

return true;
}

void SanitizerMetadata::reportGlobal(llvm::GlobalVariable *GV,
SourceLocation Loc, StringRef Name,
QualType Ty,
Expand All @@ -57,11 +88,15 @@ void SanitizerMetadata::reportGlobal(llvm::GlobalVariable *GV,
Meta.NoHWAddress |= CGM.isInNoSanitizeList(
FsanitizeArgument.Mask & SanitizerKind::HWAddress, GV, Loc, Ty);

Meta.Memtag |=
static_cast<bool>(FsanitizeArgument.Mask & SanitizerKind::MemtagGlobals);
Meta.Memtag &= !NoSanitizeAttrSet.hasOneOf(SanitizerKind::MemTag);
Meta.Memtag &= !CGM.isInNoSanitizeList(
FsanitizeArgument.Mask & SanitizerKind::MemTag, GV, Loc, Ty);
if (shouldTagGlobal(*GV)) {
Meta.Memtag |= static_cast<bool>(FsanitizeArgument.Mask &
SanitizerKind::MemtagGlobals);
Meta.Memtag &= !NoSanitizeAttrSet.hasOneOf(SanitizerKind::MemTag);
Meta.Memtag &= !CGM.isInNoSanitizeList(
FsanitizeArgument.Mask & SanitizerKind::MemTag, GV, Loc, Ty);
} else {
Meta.Memtag = false;
}

Meta.IsDynInit = IsDynInit && !Meta.NoAddress &&
FsanitizeArgument.has(SanitizerKind::Address) &&
Expand Down
5 changes: 4 additions & 1 deletion clang/test/CodeGen/memtag-globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
// RUN: FileCheck %s --check-prefix=IGNORELIST

int global;
int __attribute__((__section__("my_section"))) section_global;
int __attribute__((no_sanitize("memtag"))) attributed_global;
int __attribute__((disable_sanitizer_instrumentation)) disable_instrumentation_global;
int ignorelisted_global;
Expand All @@ -24,6 +25,8 @@ void func() {
// CHECK: @{{.*}}extra_global{{.*}} ={{.*}} sanitize_memtag
// CHECK: @{{.*}}global{{.*}} ={{.*}} sanitize_memtag

// CHECK: @{{.*}}section_global{{.*}} =
// CHECK-NOT: sanitize_memtag
// CHECK: @{{.*}}attributed_global{{.*}} =
// CHECK-NOT: sanitize_memtag
// CHECK: @{{.*}}disable_instrumentation_global{{.*}} =
Expand All @@ -32,7 +35,7 @@ void func() {
// CHECK-NOT: sanitize_memtag

// CHECK: @{{.*}}static_var{{.*}} ={{.*}} sanitize_memtag
// CHECK: @{{.*}} = {{.*}} c"Hello, world!\00"{{.*}} sanitize_memtag
// CHECK: @{{.*}} = {{.*}} c"Hello, world!\00"{{.*}}
// CHECK: @{{.*}}external_global{{.*}} ={{.*}} sanitize_memtag

// IGNORELIST: @{{.*}}extra_global{{.*}} ={{.*}} sanitize_memtag
Expand Down
38 changes: 38 additions & 0 deletions llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2406,12 +2406,50 @@ void AsmPrinter::emitRemarksSection(remarks::RemarkStreamer &RS) {
OutStreamer->emitBinaryData(Buf);
}

static void tagGlobalDefinition(Module &M, GlobalVariable *G) {
Constant *Initializer = G->getInitializer();
uint64_t SizeInBytes =
M.getDataLayout().getTypeAllocSize(Initializer->getType());

uint64_t NewSize = alignTo(SizeInBytes, 16);
if (SizeInBytes != NewSize) {
// Pad the initializer out to the next multiple of 16 bytes.
llvm::SmallVector<uint8_t> Init(NewSize - SizeInBytes, 0);
Constant *Padding = ConstantDataArray::get(M.getContext(), Init);
Initializer = ConstantStruct::getAnon({Initializer, Padding});
auto *NewGV = new GlobalVariable(
M, Initializer->getType(), G->isConstant(), G->getLinkage(),
Initializer, "", G, G->getThreadLocalMode(), G->getAddressSpace());
NewGV->copyAttributesFrom(G);
NewGV->setComdat(G->getComdat());
NewGV->copyMetadata(G, 0);

NewGV->takeName(G);
G->replaceAllUsesWith(NewGV);
G->eraseFromParent();
G = NewGV;
}

if (G->getAlign().valueOrOne() < 16)
G->setAlignment(Align(16));

// Ensure that tagged globals don't get merged by ICF - as they should have
// different tags at runtime.
G->setUnnamedAddr(GlobalValue::UnnamedAddr::None);
}

bool AsmPrinter::doFinalization(Module &M) {
// Set the MachineFunction to nullptr so that we can catch attempted
// accesses to MF specific features at the module level and so that
// we can conditionalize accesses based on whether or not it is nullptr.
MF = nullptr;

for (GlobalVariable &G : make_early_inc_range(M.globals())) {
if (G.isDeclaration() || !G.isTagged())
continue;
tagGlobalDefinition(M, &G);
}

// Gather all GOT equivalent globals in the module. We really need two
// passes over the globals: one to compute and another to avoid its emission
// in EmitGlobalVariable, otherwise we would not be able to handle cases
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/IR/Verifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -808,6 +808,10 @@ void Verifier::visitGlobalValue(const GlobalValue &GV) {
"visibility must be dso_local!",
&GV);

if (GV.isTagged()) {
Check(!GV.hasSection(), "tagged GlobalValue must not be in section.", &GV);
}

forEachUser(&GV, GlobalValueVisited, [&](const Value *V) -> bool {
if (const Instruction *I = dyn_cast<Instruction>(V)) {
if (!I->getParent() || !I->getParent()->getParent())
Expand Down
2 changes: 0 additions & 2 deletions llvm/lib/Target/AArch64/AArch64.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,6 @@ FunctionPass *createAArch64PostLegalizerLowering();
FunctionPass *createAArch64PostSelectOptimize();
FunctionPass *createAArch64StackTaggingPass(bool IsOptNone);
FunctionPass *createAArch64StackTaggingPreRAPass();
ModulePass *createAArch64GlobalsTaggingPass();
ModulePass *createAArch64Arm64ECCallLoweringPass();

void initializeAArch64A53Fix835769Pass(PassRegistry&);
Expand All @@ -89,7 +88,6 @@ void initializeAArch64ConditionalComparesPass(PassRegistry &);
void initializeAArch64DAGToDAGISelLegacyPass(PassRegistry &);
void initializeAArch64DeadRegisterDefinitionsPass(PassRegistry&);
void initializeAArch64ExpandPseudoPass(PassRegistry &);
void initializeAArch64GlobalsTaggingPass(PassRegistry &);
void initializeAArch64LoadStoreOptPass(PassRegistry&);
void initializeAArch64LowerHomogeneousPrologEpilogPass(PassRegistry &);
void initializeAArch64MIPeepholeOptPass(PassRegistry &);
Expand Down
155 changes: 0 additions & 155 deletions llvm/lib/Target/AArch64/AArch64GlobalsTagging.cpp

This file was deleted.

2 changes: 0 additions & 2 deletions llvm/lib/Target/AArch64/AArch64TargetMachine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,6 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeAArch64Target() {
initializeAArch64StackTaggingPreRAPass(*PR);
initializeAArch64LowerHomogeneousPrologEpilogPass(*PR);
initializeAArch64DAGToDAGISelLegacyPass(*PR);
initializeAArch64GlobalsTaggingPass(*PR);
}

void AArch64TargetMachine::reset() { SubtargetMap.clear(); }
Expand Down Expand Up @@ -642,7 +641,6 @@ void AArch64PassConfig::addIRPasses() {
if (getOptLevel() == CodeGenOptLevel::Aggressive && EnableSelectOpt)
addPass(createSelectOptimizePass());

addPass(createAArch64GlobalsTaggingPass());
addPass(createAArch64StackTaggingPass(
/*IsOptNone=*/TM->getOptLevel() == CodeGenOptLevel::None));

Expand Down
1 change: 0 additions & 1 deletion llvm/lib/Target/AArch64/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ add_llvm_target(AArch64CodeGen
AArch64FastISel.cpp
AArch64A53Fix835769.cpp
AArch64FrameLowering.cpp
AArch64GlobalsTagging.cpp
AArch64CompressJumpTables.cpp
AArch64ConditionOptimizer.cpp
AArch64RedundantCopyElimination.cpp
Expand Down
2 changes: 0 additions & 2 deletions llvm/test/CodeGen/AArch64/O0-pipeline.ll
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,6 @@
; CHECK-NEXT: Instrument function entry/exit with calls to e.g. mcount() (post inlining)
; CHECK-NEXT: Scalarize Masked Memory Intrinsics
; CHECK-NEXT: Expand reduction intrinsics
; CHECK-NEXT: AArch64 Globals Tagging
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Dominator Tree Construction
; CHECK-NEXT: Natural Loop Information
; CHECK-NEXT: Lazy Branch Probability Analysis
Expand Down
1 change: 0 additions & 1 deletion llvm/test/CodeGen/AArch64/O3-pipeline.ll
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,6 @@
; CHECK-NEXT: Lazy Block Frequency Analysis
; CHECK-NEXT: Optimization Remark Emitter
; CHECK-NEXT: Optimize selects
; CHECK-NEXT: AArch64 Globals Tagging
; CHECK-NEXT: Stack Safety Analysis
; CHECK-NEXT: FunctionPass Manager
; CHECK-NEXT: Dominator Tree Construction
Expand Down
22 changes: 22 additions & 0 deletions llvm/unittests/IR/VerifierTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "llvm/IR/DerivedTypes.h"
#include "llvm/IR/Function.h"
#include "llvm/IR/GlobalAlias.h"
#include "llvm/IR/GlobalValue.h"
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/IRBuilder.h"
#include "llvm/IR/Instructions.h"
Expand Down Expand Up @@ -415,5 +416,26 @@ TEST(VerifierTest, GetElementPtrInst) {
<< Error;
}

TEST(VerifierTest, DetectTaggedGlobalInSection) {
LLVMContext C;
Module M("M", C);
GlobalVariable *GV = new GlobalVariable(
Type::getInt64Ty(C), false, GlobalValue::InternalLinkage,
ConstantInt::get(Type::getInt64Ty(C), 1));
GV->setDSOLocal(true);
GlobalValue::SanitizerMetadata MD{};
MD.Memtag = true;
GV->setSanitizerMetadata(MD);
GV->setSection("foo");
M.insertGlobalVariable(GV);

std::string Error;
raw_string_ostream ErrorOS(Error);
EXPECT_TRUE(verifyModule(M, &ErrorOS));
EXPECT_TRUE(
StringRef(Error).starts_with("tagged GlobalValue must not be in section"))
<< Error;
}

} // end anonymous namespace
} // end namespace llvm
Loading

0 comments on commit 514580b

Please sign in to comment.