Skip to content

Commit

Permalink
Add sanitizer-specific GlobalValue attributes.
Browse files Browse the repository at this point in the history
Plan is the migrate the global variable metadata for sanitizers, that's
currently carried around generally in the 'llvm.asan.globals' section,
onto the global variable itself.

This patch adds the attribute and plumbs it through the LLVM IR and
bitcode formats, but is a no-op other than that so far.

Reviewed By: vitalybuka, kstoimenov

Differential Revision: https://reviews.llvm.org/D126100
  • Loading branch information
hctim committed Jun 10, 2022
1 parent 0bb317b commit 8db981d
Show file tree
Hide file tree
Showing 12 changed files with 200 additions and 5 deletions.
1 change: 1 addition & 0 deletions llvm/include/llvm/AsmParser/LLParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -515,6 +515,7 @@ namespace llvm {
bool parseGlobalValueVector(SmallVectorImpl<Constant *> &Elts,
Optional<unsigned> *InRangeOp = nullptr);
bool parseOptionalComdat(StringRef GlobalName, Comdat *&C);
bool parseSanitizer(GlobalVariable *GV);
bool parseMetadataAsValue(Value *&V, PerFunctionState &PFS);
bool parseValueAsMetadata(Metadata *&MD, const Twine &TypeMsg,
PerFunctionState *PFS);
Expand Down
13 changes: 13 additions & 0 deletions llvm/include/llvm/AsmParser/LLToken.h
Original file line number Diff line number Diff line change
Expand Up @@ -391,6 +391,19 @@ enum Kind {
kw_bit,
kw_varFlags,

// GV's with __attribute__((no_sanitize("address"))), or things in
// -fsanitize-ignorelist when built with ASan.
kw_no_sanitize_address,
// GV's with __attribute__((no_sanitize("hwaddress"))), or things in
// -fsanitize-ignorelist when built with HWASan.
kw_no_sanitize_hwaddress,
// GV's with __attribute__((no_sanitize("memtag"))), or things in
// -fsanitize-ignorelist when built with memory tagging.
kw_no_sanitize_memtag,
// GV's where the clang++ frontend (when ASan is used) notes that this is
// dynamically initialized, and thus needs ODR detection.
kw_sanitize_address_dyninit,

// Unsigned Valued tokens (UIntVal).
LabelID, // 42:
GlobalID, // @42
Expand Down
45 changes: 42 additions & 3 deletions llvm/include/llvm/IR/GlobalValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -79,14 +79,15 @@ class GlobalValue : public Constant {
ValueType(Ty), Visibility(DefaultVisibility),
UnnamedAddrVal(unsigned(UnnamedAddr::None)),
DllStorageClass(DefaultStorageClass), ThreadLocal(NotThreadLocal),
HasLLVMReservedName(false), IsDSOLocal(false), HasPartition(false) {
HasLLVMReservedName(false), IsDSOLocal(false), HasPartition(false),
HasSanitizerMetadata(false) {
setLinkage(Linkage);
setName(Name);
}

Type *ValueType;

static const unsigned GlobalValueSubClassDataBits = 16;
static const unsigned GlobalValueSubClassDataBits = 15;

// All bitfields use unsigned as the underlying type so that MSVC will pack
// them.
Expand All @@ -111,9 +112,14 @@ class GlobalValue : public Constant {
/// https://lld.llvm.org/Partitions.html).
unsigned HasPartition : 1;

/// True if this symbol has sanitizer metadata available. Should only happen
/// if sanitizers were enabled when building the translation unit which
/// contains this GV.
unsigned HasSanitizerMetadata : 1;

private:
// Give subclasses access to what otherwise would be wasted padding.
// (16 + 4 + 2 + 2 + 2 + 3 + 1 + 1 + 1) == 32.
// (15 + 4 + 2 + 2 + 2 + 3 + 1 + 1 + 1 + 1) == 32.
unsigned SubClassData : GlobalValueSubClassDataBits;

friend class Constant;
Expand Down Expand Up @@ -288,6 +294,39 @@ class GlobalValue : public Constant {
StringRef getPartition() const;
void setPartition(StringRef Part);

// ASan, HWASan and Memtag sanitizers have some instrumentation that applies
// specifically to global variables. This instrumentation is implicitly
// applied to all global variables when built with -fsanitize=*. What we need
// is a way to persist the information that a certain global variable should
// *not* have sanitizers applied, which occurs if:
// 1. The global variable is in the sanitizer ignore list, or
// 2. The global variable is created by the sanitizers itself for internal
// usage, or
// 3. The global variable has __attribute__((no_sanitize("..."))) or
// __attribute__((disable_sanitizer_instrumentation)).
//
// This is important, a some IR passes like GlobalMerge can delete global
// variables and replace them with new ones. If the old variables were marked
// to be unsanitized, then the new ones should also be.
struct SanitizerMetadata {
SanitizerMetadata()
: NoAddress(false), NoHWAddress(false), NoMemtag(false),
IsDynInit(false) {}
unsigned NoAddress : 1;
unsigned NoHWAddress : 1;
unsigned NoMemtag : 1;

// ASan-specific metadata. Is this global variable dynamically initialized
// (from a C++ language perspective), and should therefore be checked for
// ODR violations.
unsigned IsDynInit : 1;
};

bool hasSanitizerMetadata() const { return HasSanitizerMetadata; }
const SanitizerMetadata &getSanitizerMetadata() const;
void setSanitizerMetadata(const SanitizerMetadata &Meta);
void removeSanitizerMetadata();

static LinkageTypes getLinkOnceLinkage(bool ODR) {
return ODR ? LinkOnceODRLinkage : LinkOnceAnyLinkage;
}
Expand Down
5 changes: 5 additions & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,11 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(prefix);
KEYWORD(prologue);

KEYWORD(no_sanitize_address);
KEYWORD(no_sanitize_hwaddress);
KEYWORD(no_sanitize_memtag);
KEYWORD(sanitize_address_dyninit);

KEYWORD(ccc);
KEYWORD(fastcc);
KEYWORD(coldcc);
Expand Down
42 changes: 42 additions & 0 deletions llvm/lib/AsmParser/LLParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1103,6 +1103,45 @@ bool LLParser::parseAliasOrIFunc(const std::string &Name, LocTy NameLoc,
return false;
}

static bool isSanitizer(lltok::Kind Kind) {
switch (Kind) {
case lltok::kw_no_sanitize_address:
case lltok::kw_no_sanitize_hwaddress:
case lltok::kw_no_sanitize_memtag:
case lltok::kw_sanitize_address_dyninit:
return true;
default:
return false;
}
}

bool LLParser::parseSanitizer(GlobalVariable *GV) {
using SanitizerMetadata = GlobalValue::SanitizerMetadata;
SanitizerMetadata Meta;
if (GV->hasSanitizerMetadata())
Meta = GV->getSanitizerMetadata();

switch (Lex.getKind()) {
case lltok::kw_no_sanitize_address:
Meta.NoAddress = true;
break;
case lltok::kw_no_sanitize_hwaddress:
Meta.NoHWAddress = true;
break;
case lltok::kw_no_sanitize_memtag:
Meta.NoMemtag = true;
break;
case lltok::kw_sanitize_address_dyninit:
Meta.IsDynInit = true;
break;
default:
return tokError("non-sanitizer token passed to LLParser::parseSanitizer()");
}
GV->setSanitizerMetadata(Meta);
Lex.Lex();
return false;
}

/// parseGlobal
/// ::= GlobalVar '=' OptionalLinkage OptionalPreemptionSpecifier
/// OptionalVisibility OptionalDLLStorageClass
Expand Down Expand Up @@ -1221,6 +1260,9 @@ bool LLParser::parseGlobal(const std::string &Name, LocTy NameLoc,
} else if (Lex.getKind() == lltok::MetadataVar) {
if (parseGlobalObjectMetadataAttachment(*GV))
return true;
} else if (isSanitizer(Lex.getKind())) {
if (parseSanitizer(GV))
return true;
} else {
Comdat *C;
if (parseOptionalComdat(Name, C))
Expand Down
19 changes: 19 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3441,6 +3441,19 @@ static void inferDSOLocal(GlobalValue *GV) {
GV->setDSOLocal(true);
}

GlobalValue::SanitizerMetadata deserializeSanitizerMetadata(unsigned V) {
GlobalValue::SanitizerMetadata Meta;
if (V & (1 << 0))
Meta.NoAddress = true;
if (V & (1 << 1))
Meta.NoHWAddress = true;
if (V & (1 << 2))
Meta.NoMemtag = true;
if (V & (1 << 3))
Meta.IsDynInit = true;
return Meta;
}

Error BitcodeReader::parseGlobalVarRecord(ArrayRef<uint64_t> Record) {
// v1: [pointer type, isconst, initid, linkage, alignment, section,
// visibility, threadlocal, unnamed_addr, externally_initialized,
Expand Down Expand Up @@ -3544,6 +3557,12 @@ Error BitcodeReader::parseGlobalVarRecord(ArrayRef<uint64_t> Record) {
if (Record.size() > 15)
NewGV->setPartition(StringRef(Strtab.data() + Record[14], Record[15]));

if (Record.size() > 16 && Record[16] != UINT_MAX) {
llvm::GlobalValue::SanitizerMetadata Meta =
deserializeSanitizerMetadata(Record[16]);
NewGV->setSanitizerMetadata(Meta);
}

return Error::success();
}

Expand Down
17 changes: 15 additions & 2 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,14 @@ static StringEncoding getStringEncoding(StringRef Str) {
return SE_Fixed7;
}

static_assert(sizeof(GlobalValue::SanitizerMetadata) <= sizeof(unsigned),
"Sanitizer Metadata is too large for naive serialization.");
static unsigned
serializeSanitizerMetadata(const GlobalValue::SanitizerMetadata &Meta) {
return Meta.NoAddress | (Meta.NoHWAddress << 1) |
(Meta.NoMemtag << 2) | (Meta.IsDynInit << 3);
}

/// Emit top-level description of module, including target triple, inline asm,
/// descriptors for global variables, and function prototype info.
/// Returns the bit offset to backpatch with the location of the real VST.
Expand Down Expand Up @@ -1346,7 +1354,7 @@ void ModuleBitcodeWriter::writeModuleInfo() {
// GLOBALVAR: [strtab offset, strtab size, type, isconst, initid,
// linkage, alignment, section, visibility, threadlocal,
// unnamed_addr, externally_initialized, dllstorageclass,
// comdat, attributes, DSO_Local]
// comdat, attributes, DSO_Local, GlobalSanitizer]
Vals.push_back(addToStrtab(GV.getName()));
Vals.push_back(GV.getName().size());
Vals.push_back(VE.getTypeID(GV.getValueType()));
Expand All @@ -1363,7 +1371,7 @@ void ModuleBitcodeWriter::writeModuleInfo() {
GV.isExternallyInitialized() ||
GV.getDLLStorageClass() != GlobalValue::DefaultStorageClass ||
GV.hasComdat() || GV.hasAttributes() || GV.isDSOLocal() ||
GV.hasPartition()) {
GV.hasPartition() || GV.hasSanitizerMetadata()) {
Vals.push_back(getEncodedVisibility(GV));
Vals.push_back(getEncodedThreadLocalMode(GV));
Vals.push_back(getEncodedUnnamedAddr(GV));
Expand All @@ -1377,6 +1385,11 @@ void ModuleBitcodeWriter::writeModuleInfo() {
Vals.push_back(GV.isDSOLocal());
Vals.push_back(addToStrtab(GV.getPartition()));
Vals.push_back(GV.getPartition().size());

if (GV.hasSanitizerMetadata())
Vals.push_back(serializeSanitizerMetadata(GV.getSanitizerMetadata()));
else
Vals.push_back(UINT_MAX);
} else {
AbbrevToUse = SimpleGVarAbbrev;
}
Expand Down
13 changes: 13 additions & 0 deletions llvm/lib/IR/AsmWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3535,6 +3535,19 @@ void AssemblyWriter::printGlobal(const GlobalVariable *GV) {
Out << '"';
}

using SanitizerMetadata = llvm::GlobalValue::SanitizerMetadata;
if (GV->hasSanitizerMetadata()) {
SanitizerMetadata MD = GV->getSanitizerMetadata();
if (MD.NoAddress)
Out << ", no_sanitize_address";
if (MD.NoHWAddress)
Out << ", no_sanitize_hwaddress";
if (MD.NoMemtag)
Out << ", no_sanitize_memtag";
if (MD.IsDynInit)
Out << ", sanitize_address_dyninit";
}

maybePrintComdat(Out, *GV);
if (MaybeAlign A = GV->getAlign())
Out << ", align " << A->value();
Expand Down
25 changes: 25 additions & 0 deletions llvm/lib/IR/Globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ void GlobalValue::copyAttributesFrom(const GlobalValue *Src) {
setDLLStorageClass(Src->getDLLStorageClass());
setDSOLocal(Src->isDSOLocal());
setPartition(Src->getPartition());
if (Src->hasSanitizerMetadata())
setSanitizerMetadata(Src->getSanitizerMetadata());
else
removeSanitizerMetadata();
}

void GlobalValue::removeFromParent() {
Expand Down Expand Up @@ -217,6 +221,27 @@ void GlobalValue::setPartition(StringRef S) {
HasPartition = !S.empty();
}

using SanitizerMetadata = GlobalValue::SanitizerMetadata;
const SanitizerMetadata &GlobalValue::getSanitizerMetadata() const {
assert(hasSanitizerMetadata());
assert(getContext().pImpl->GlobalValueSanitizerMetadata.count(this));
return getContext().pImpl->GlobalValueSanitizerMetadata[this];
}

void GlobalValue::setSanitizerMetadata(const SanitizerMetadata &Meta) {
getContext().pImpl->GlobalValueSanitizerMetadata[this] = Meta;
HasSanitizerMetadata = true;
}

void GlobalValue::removeSanitizerMetadata() {
DenseMap<const GlobalValue *, SanitizerMetadata> &MetadataMap =
getContext().pImpl->GlobalValueSanitizerMetadata;
auto It = MetadataMap.find(this);
if (It != MetadataMap.end())
MetadataMap.erase(It);
HasSanitizerMetadata = false;
}

StringRef GlobalObject::getSectionImpl() const {
assert(hasSection());
return getContext().pImpl->GlobalObjectSections[this];
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/IR/LLVMContextImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -1503,6 +1503,9 @@ class LLVMContextImpl {
/// Collection of per-GlobalValue partitions used in this context.
DenseMap<const GlobalValue *, StringRef> GlobalValuePartitions;

DenseMap<const GlobalValue *, GlobalValue::SanitizerMetadata>
GlobalValueSanitizerMetadata;

/// DiscriminatorTable - This table maps file:line locations to an
/// integer representing the next DWARF path discriminator to assign to
/// instructions in different blocks at the same location.
Expand Down
10 changes: 10 additions & 0 deletions llvm/test/Assembler/globalvariable-attributes.ll
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,23 @@
@g2 = global i32 2, align 4 "key3" = "value3"
@g3 = global i32 2 #0
@g4 = global i32 2, align 4 "key5" = "value5" #0
@g5 = global i32 2, no_sanitize_address, align 4
@g6 = global i32 2, no_sanitize_hwaddress, align 4
@g7 = global i32 2, no_sanitize_memtag, align 4
@g8 = global i32 2, sanitize_address_dyninit, align 4
@g9 = global i32 2, no_sanitize_address, no_sanitize_hwaddress, no_sanitize_memtag, align 4

attributes #0 = { "string" = "value" nobuiltin norecurse }

; CHECK: @g1 = global i32 7 #0
; CHECK: @g2 = global i32 2, align 4 #1
; CHECK: @g3 = global i32 2 #2
; CHECK: @g4 = global i32 2, align 4 #3
; CHECK: @g5 = global i32 2, no_sanitize_address, align 4
; CHECK: @g6 = global i32 2, no_sanitize_hwaddress, align 4
; CHECK: @g7 = global i32 2, no_sanitize_memtag, align 4
; CHECK: @g8 = global i32 2, sanitize_address_dyninit, align 4
; CHECK: @g9 = global i32 2, no_sanitize_address, no_sanitize_hwaddress, no_sanitize_memtag, align 4

; CHECK: attributes #0 = { "key"="value" "key2"="value2" }
; CHECK: attributes #1 = { "key3"="value3" }
Expand Down
12 changes: 12 additions & 0 deletions llvm/test/Bitcode/compatibility.ll
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,18 @@ declare void @g.f1()
@llvm.global_dtors = appending global [1 x %pri.func.data] [%pri.func.data { i32 0, void ()* @g.f1, i8* @g.used3 }], section "llvm.metadata"
; CHECK: @llvm.global_dtors = appending global [1 x %pri.func.data] [%pri.func.data { i32 0, void ()* @g.f1, i8* @g.used3 }], section "llvm.metadata"

; Global Variables -- sanitizers
@g.no_sanitize_address = global i32 0, no_sanitize_address
@g.no_sanitize_hwaddress = global i32 0, no_sanitize_hwaddress
@g.no_sanitize_memtag = global i32 0, no_sanitize_memtag
@g.no_sanitize_multiple = global i32 0, no_sanitize_address, no_sanitize_hwaddress, no_sanitize_memtag
@g.sanitize_address_dyninit = global i32 0, sanitize_address_dyninit
; CHECK: @g.no_sanitize_address = global i32 0, no_sanitize_address
; CHECK: @g.no_sanitize_hwaddress = global i32 0, no_sanitize_hwaddress
; CHECK: @g.no_sanitize_memtag = global i32 0, no_sanitize_memtag
; CHECK: @g.no_sanitize_multiple = global i32 0, no_sanitize_address, no_sanitize_hwaddress, no_sanitize_memtag
; CHECK: @g.sanitize_address_dyninit = global i32 0, sanitize_address_dyninit

;; Aliases
; Format: @<Name> = [Linkage] [Visibility] [DLLStorageClass] [ThreadLocal]
; [unnamed_addr] alias <AliaseeTy> @<Aliasee>
Expand Down

0 comments on commit 8db981d

Please sign in to comment.