Skip to content

Commit

Permalink
Reland "Add sanitizer metadata attributes to clang IR gen."
Browse files Browse the repository at this point in the history
RE-LAND (reverts a revert):
This reverts commit 8e1f47b.

This patch adds generation of sanitizer metadata attributes (which were
added in D126100) to the clang frontend.

We still currently generate the llvm.asan.globals that's consumed by
the IR pass, but the plan is to eventually migrate off of that onto
purely debuginfo and these IR attributes.

Reviewed By: vitalybuka, kstoimenov

Differential Revision: https://reviews.llvm.org/D126929
  • Loading branch information
hctim committed Jun 13, 2022
1 parent 0539b45 commit 77475ff
Show file tree
Hide file tree
Showing 8 changed files with 188 additions and 34 deletions.
17 changes: 5 additions & 12 deletions clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2760,21 +2760,14 @@ bool CodeGenModule::isInNoSanitizeList(SanitizerMask Kind, llvm::Function *Fn,
return false;
}

bool CodeGenModule::isInNoSanitizeList(llvm::GlobalVariable *GV,
bool CodeGenModule::isInNoSanitizeList(SanitizerMask Kind,
llvm::GlobalVariable *GV,
SourceLocation Loc, QualType Ty,
StringRef Category) const {
// For now globals can be ignored only in ASan and KASan.
const SanitizerMask EnabledAsanMask =
LangOpts.Sanitize.Mask &
(SanitizerKind::Address | SanitizerKind::KernelAddress |
SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress |
SanitizerKind::MemTag);
if (!EnabledAsanMask)
return false;
const auto &NoSanitizeL = getContext().getNoSanitizeList();
if (NoSanitizeL.containsGlobal(EnabledAsanMask, GV->getName(), Category))
if (NoSanitizeL.containsGlobal(Kind, GV->getName(), Category))
return true;
if (NoSanitizeL.containsLocation(EnabledAsanMask, Loc, Category))
if (NoSanitizeL.containsLocation(Kind, Loc, Category))
return true;
// Check global type.
if (!Ty.isNull()) {
Expand All @@ -2786,7 +2779,7 @@ bool CodeGenModule::isInNoSanitizeList(llvm::GlobalVariable *GV,
// Only record types (classes, structs etc.) are ignored.
if (Ty->isRecordType()) {
std::string TypeStr = Ty.getAsString(getContext().getPrintingPolicy());
if (NoSanitizeL.containsType(EnabledAsanMask, TypeStr, Category))
if (NoSanitizeL.containsType(Kind, TypeStr, Category))
return true;
}
}
Expand Down
5 changes: 3 additions & 2 deletions clang/lib/CodeGen/CodeGenModule.h
Original file line number Diff line number Diff line change
Expand Up @@ -1314,8 +1314,9 @@ class CodeGenModule : public CodeGenTypeCache {
bool isInNoSanitizeList(SanitizerMask Kind, llvm::Function *Fn,
SourceLocation Loc) const;

bool isInNoSanitizeList(llvm::GlobalVariable *GV, SourceLocation Loc,
QualType Ty, StringRef Category = StringRef()) const;
bool isInNoSanitizeList(SanitizerMask Kind, llvm::GlobalVariable *GV,
SourceLocation Loc, QualType Ty,
StringRef Category = StringRef()) const;

/// Imbue XRay attributes to a function, applying the always/never attribute
/// lists in the process. Returns true if we did imbue attributes this way,
Expand Down
75 changes: 56 additions & 19 deletions clang/lib/CodeGen/SanitizerMetadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,66 @@ using namespace CodeGen;

SanitizerMetadata::SanitizerMetadata(CodeGenModule &CGM) : CGM(CGM) {}

// TODO(hctim): Can be removed when we migrate off of llvm.asan.globals. This
// prevents llvm.asan.globals from being emitted for
// __attribute__((disable_sanitizer_instrumentation)) and uses of
// -fsanitize-ignorelist when a sanitizer isn't enabled.
static bool isAsanHwasanOrMemTag(const SanitizerSet &SS) {
return SS.hasOneOf(SanitizerKind::Address | SanitizerKind::KernelAddress |
SanitizerKind::HWAddress | SanitizerKind::KernelHWAddress |
SanitizerKind::MemTag);
SanitizerKind::HWAddress | SanitizerKind::MemTag);
}

SanitizerMask expandKernelSanitizerMasks(SanitizerMask Mask) {
if (Mask & (SanitizerKind::Address | SanitizerKind::KernelAddress))
Mask |= SanitizerKind::Address | SanitizerKind::KernelAddress;
// Note: KHWASan doesn't support globals.
return Mask;
}

void SanitizerMetadata::reportGlobal(llvm::GlobalVariable *GV,
SourceLocation Loc, StringRef Name,
QualType Ty, bool IsDynInit,
bool IsExcluded) {
IsDynInit &= !CGM.isInNoSanitizeList(GV, Loc, Ty, "init");
IsExcluded |= CGM.isInNoSanitizeList(GV, Loc, Ty);
QualType Ty,
SanitizerMask NoSanitizeAttrMask,
bool IsDynInit) {
SanitizerSet FsanitizeArgument = CGM.getLangOpts().Sanitize;
if (!isAsanHwasanOrMemTag(FsanitizeArgument))
return;

FsanitizeArgument.Mask = expandKernelSanitizerMasks(FsanitizeArgument.Mask);
NoSanitizeAttrMask = expandKernelSanitizerMasks(NoSanitizeAttrMask);
SanitizerSet NoSanitizeAttrSet = {NoSanitizeAttrMask &
FsanitizeArgument.Mask};

llvm::GlobalVariable::SanitizerMetadata Meta;
if (GV->hasSanitizerMetadata())
Meta = GV->getSanitizerMetadata();

Meta.NoAddress |= NoSanitizeAttrSet.hasOneOf(SanitizerKind::Address);
Meta.NoAddress |= CGM.isInNoSanitizeList(
FsanitizeArgument.Mask & SanitizerKind::Address, GV, Loc, Ty);

Meta.NoHWAddress |= NoSanitizeAttrSet.hasOneOf(SanitizerKind::HWAddress);
Meta.NoHWAddress |= CGM.isInNoSanitizeList(
FsanitizeArgument.Mask & SanitizerKind::HWAddress, GV, Loc, Ty);

Meta.NoMemtag |= NoSanitizeAttrSet.hasOneOf(SanitizerKind::MemTag);
Meta.NoMemtag |= CGM.isInNoSanitizeList(
FsanitizeArgument.Mask & SanitizerKind::MemTag, GV, Loc, Ty);

if (FsanitizeArgument.has(SanitizerKind::Address)) {
// TODO(hctim): Make this conditional when we migrate off llvm.asan.globals.
IsDynInit &= !CGM.isInNoSanitizeList(SanitizerKind::Address |
SanitizerKind::KernelAddress,
GV, Loc, Ty, "init");
Meta.IsDynInit = IsDynInit;
}

bool IsExcluded = Meta.NoAddress || Meta.NoHWAddress || Meta.NoMemtag;

GV->setSanitizerMetadata(Meta);

// TODO(hctim): Code below can be removed when we migrate off of
// llvm.asan.globals onto the new metadata attributes.
llvm::Metadata *LocDescr = nullptr;
llvm::Metadata *GlobalName = nullptr;
llvm::LLVMContext &VMContext = CGM.getLLVMContext();
Expand Down Expand Up @@ -77,23 +124,13 @@ void SanitizerMetadata::reportGlobal(llvm::GlobalVariable *GV, const VarDecl &D,

return NoSanitizeMask;
};
reportGlobal(GV, D.getLocation(), OS.str(), D.getType(), IsDynInit,
SanitizerSet{getNoSanitizeMask(D)}.has(SanitizerKind::Address));
}

void SanitizerMetadata::reportGlobal(llvm::GlobalVariable *GV,
SourceLocation Loc, StringRef Name,
QualType Ty, bool IsDynInit) {
if (!isAsanHwasanOrMemTag(CGM.getLangOpts().Sanitize))
return;
reportGlobal(GV, Loc, Name, Ty, IsDynInit, false);
reportGlobal(GV, D.getLocation(), OS.str(), D.getType(), getNoSanitizeMask(D),
IsDynInit);
}

void SanitizerMetadata::disableSanitizerForGlobal(llvm::GlobalVariable *GV) {
// For now, just make sure the global is not modified by the ASan
// instrumentation.
if (isAsanHwasanOrMemTag(CGM.getLangOpts().Sanitize))
reportGlobal(GV, SourceLocation(), "", QualType(), false, true);
reportGlobal(GV, SourceLocation(), "", QualType(), SanitizerKind::All);
}

void SanitizerMetadata::disableSanitizerForInstruction(llvm::Instruction *I) {
Expand Down
5 changes: 4 additions & 1 deletion clang/lib/CodeGen/SanitizerMetadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

#include "clang/AST/Type.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Sanitizers.h"
#include "clang/Basic/SourceLocation.h"

namespace llvm {
Expand All @@ -40,7 +41,9 @@ class SanitizerMetadata {
void reportGlobal(llvm::GlobalVariable *GV, const VarDecl &D,
bool IsDynInit = false);
void reportGlobal(llvm::GlobalVariable *GV, SourceLocation Loc,
StringRef Name, QualType Ty = {}, bool IsDynInit = false);
StringRef Name, QualType Ty = {},
SanitizerMask NoSanitizeAttrMask = {},
bool IsDynInit = false);
void disableSanitizerForGlobal(llvm::GlobalVariable *GV);
void disableSanitizerForInstruction(llvm::Instruction *I);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
global:always_ignored
[address]
global:asan_ignored
[hwaddress]
global:hwasan_ignored
[memtag]
global:memtag_ignored
[cfi-vcall|cfi-icall]
src:*
3 changes: 3 additions & 0 deletions clang/test/CodeGen/asan-globals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ void func() {
const char *literal = "Hello, world!";
}

// ASAN: @{{.*}}dyn_init_global{{.*}} ={{.*}} global {{.*}}, sanitize_address_dyninit
// KASAN: @{{.*}}dyn_init_global{{.*}} ={{.*}} global {{.*}}, sanitize_address_dyninit

// ASAN: sectioned_global{{.*}} global { i32, [28 x i8] }{{.*}}, align 32
// KASAN: sectioned_global{{.*}} global i32
// ASAN: @__special_global{{.*}} global { i32, [28 x i8] }{{.*}}, align 32
Expand Down
17 changes: 17 additions & 0 deletions clang/test/CodeGen/sanitize-init-order.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,29 @@ const volatile PODWithCtor array[5][5];

// Check that ASan init-order checking ignores structs with trivial default
// constructor.

// CHECK: @s1 ={{.*}} global
// CHECK-NOT: sanitize_address_dyninit
// CHECK: @s2 ={{.*}} global
// CHECK-NOT: sanitize_address_dyninit
// CHECK: @s3 ={{.*}} global {{.*}}, sanitize_address_dyninit
// CHECK: @{{.*}}array{{.*}} ={{.*}} global {{.*}}, sanitize_address_dyninit

// CHECK: !llvm.asan.globals = !{![[GLOB_1:[0-9]+]], ![[GLOB_2:[0-9]+]], ![[GLOB_3:[0-9]+]], ![[GLOB_4:[0-9]+]]
// CHECK: ![[GLOB_1]] = !{%struct.PODStruct* {{.*}}, i1 false, i1 false}
// CHECK: ![[GLOB_2]] = !{%struct.PODWithDtor* {{.*}}, i1 false, i1 false}
// CHECK: ![[GLOB_3]] = !{%struct.PODWithCtorAndDtor* {{.*}}, i1 true, i1 false}
// CHECK: ![[GLOB_4]] = !{{{.*}}class.NS::PODWithCtor{{.*}}, i1 true, i1 false}

// IGNORELIST: @s1 ={{.*}} global
// IGNORELIST-NOT: sanitize_address_dyninit
// IGNORELIST: @s2 ={{.*}} global
// IGNORELIST-NOT: sanitize_address_dyninit
// IGNORELIST: @s3 ={{.*}} global
// IGNORELIST-NOT: sanitize_address_dyninit
// IGNORELIST: @{{.*}}array{{.*}} ={{.*}} global
// IGNORELIST-NOT: sanitize_address_dyninit

// IGNORELIST: !llvm.asan.globals = !{![[GLOB_1:[0-9]+]], ![[GLOB_2:[0-9]+]], ![[GLOB_3:[0-9]+]], ![[GLOB_4:[0-9]+]]}
// IGNORELIST: ![[GLOB_1]] = !{%struct.PODStruct* {{.*}}, i1 false, i1 false}
// IGNORELIST: ![[GLOB_2]] = !{%struct.PODWithDtor* {{.*}}, i1 false, i1 false}
Expand Down
91 changes: 91 additions & 0 deletions clang/test/CodeGen/sanitizer-special-case-list-globals.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
/// Verify that ignorelist sections correctly select sanitizers to apply
/// ignorelist entries to.

// RUN: %clang_cc1 -emit-llvm %s -o -\
// RUN: -fsanitize-ignorelist=%S/Inputs/sanitizer-special-case-list-globals.txt \
// RUN: | FileCheck %s --check-prefix=NONE

// RUN: %clang_cc1 -fsanitize=address -emit-llvm %s -o -\
// RUN: -fsanitize-ignorelist=%S/Inputs/sanitizer-special-case-list-globals.txt \
// RUN: | FileCheck %s --check-prefix=ASAN

/// Note: HWASan effectively reorders globals (it puts the unsanitized ones
/// first), which is hard to check for, as 'CHECK-DAG' doesn't play terribly
/// nicely with 'CHECK-NOT'. This is why the 'always_ignored' and
/// 'hwasan_ignored' comes first in this file.
// RUN: %clang_cc1 -fsanitize=hwaddress -emit-llvm %s -o -\
// RUN: -fsanitize-ignorelist=%S/Inputs/sanitizer-special-case-list-globals.txt \
// RUN: | FileCheck %s --check-prefix=HWASAN

/// TODO(hctim): Move over to memtag-globals when it's implemented. For now
/// though, it's fine, the frontend still annotates based on any memtag sanitizer
/// being used.
// RUN: %clang_cc1 -fsanitize=memtag-heap -triple=aarch64-linux-android31 -emit-llvm %s -o -\
// RUN: -fsanitize-ignorelist=%S/Inputs/sanitizer-special-case-list-globals.txt \
// RUN: | FileCheck %s --check-prefix=MEMTAG

/// Check that the '[cfi-vcall|cfi-icall] src:*' rule in the ignorelist doesn't change
/// anything for ASan.
// RUN: %clang_cc1 -fsanitize=address -emit-llvm %s -o -\
// RUN: -fsanitize-ignorelist=%S/Inputs/sanitizer-special-case-list-globals.txt \
// RUN: | FileCheck %s --check-prefix=ASAN

/// Check that -fsanitize=kernel-address picks up the '[address]' groups.
// RUN: %clang_cc1 -fsanitize=kernel-address -mllvm -hwasan-kernel=1 -emit-llvm %s -o -\
// RUN: -fsanitize-ignorelist=%S/Inputs/sanitizer-special-case-list-globals.txt \
// RUN: | FileCheck %s --check-prefix=ASAN

/// KHWASan doesn't instrument global variables.
// RUN: %clang_cc1 -fsanitize=kernel-hwaddress -mllvm -hwasan-kernel=1 -emit-llvm %s -o -\
// RUN: -fsanitize-ignorelist=%S/Inputs/sanitizer-special-case-list-globals.txt \
// RUN: | FileCheck %s --check-prefix=NONE

/// Check that the '[cfi-vcall|cfi-icall] src:*' rule doesnt' emit anything for
/// GVs.
// RUN: %clang_cc1 -fsanitize=cfi-vcall,cfi-icall -emit-llvm %s -o -\
// RUN: -fsanitize-ignorelist=%S/Inputs/sanitizer-special-case-list-globals.txt \
// RUN: | FileCheck %s --check-prefix=NONE

// NONE: @always_ignored ={{.*}} global
// NONE-NOT: no_sanitize
// ASAN: @always_ignored ={{.*}} global {{.*}}, no_sanitize_address
// HWASAN: @always_ignored ={{.*}} global {{.*}}, no_sanitize_hwaddress
// MEMTAG: @always_ignored ={{.*}} global {{.*}}, no_sanitize_memtag
unsigned always_ignored;

// NONE: @hwasan_ignored ={{.*}} global
// NONE-NOT: no_sanitize
// ASAN: @hwasan_ignored ={{.*}} global
// ASAN-NOT: no_sanitize_address
// HWASAN: @hwasan_ignored ={{.*}} global {{.*}}, no_sanitize_hwaddress
// MEMTAG: @hwasan_ignored ={{.*}} global
// MEMTAG-NOT: no_sanitize_memtag
unsigned hwasan_ignored;

// NONE: @asan_ignored ={{.*}} global
// NONE-NOT: asan_ignored
// ASAN: @asan_ignored ={{.*}} global {{.*}}, no_sanitize_address
// HWASAN: @asan_ignored.hwasan = {{.*}} global
// HWASAN-NOT: no_sanitize_hwaddress
// MEMTAG: @asan_ignored ={{.*}} global
// MEMTAG-NOT: no_sanitize_memtag
unsigned asan_ignored;

// NONE: @memtag_ignored ={{.*}} global
// NONE-NOT: memtag_ignored
// ASAN: @memtag_ignored ={{.*}} global
// ASAN-NOT: no_sanitize_address
// HWASAN: @memtag_ignored.hwasan = {{.*}} global
// HWASAN-NOT: no_sanitize_hwaddress
// MEMTAG: @memtag_ignored ={{.*}} global {{.*}}, no_sanitize_memtag
unsigned memtag_ignored;

// NONE: @never_ignored ={{.*}} global
// NONE-NOT: never_ignored
// ASAN: @never_ignored ={{.*}} global
// ASAN-NOT: no_sanitize_address
// HWASAN: @never_ignored.hwasan ={{.*}} global
// HWASAN-NOT: no_sanitize_hwaddress
// MEMTAG: @never_ignored ={{.*}} global
// MEMTAG-NOT: no_sanitize_memtag
unsigned never_ignored;

0 comments on commit 77475ff

Please sign in to comment.