[SanitizerBounds] Add support for NoSanitizeBounds function

Currently adding attribute no_sanitize("bounds") isn't disabling
-fsanitize=local-bounds (also enabled in -fsanitize=bounds). The Clang
frontend handles fsanitize=array-bounds which can already be disabled by
no_sanitize("bounds"). However, instrumentation added by the
BoundsChecking pass in the middle-end cannot be disabled by the
attribute.

The fix is very similar to D102772 that added the ability to selectively
disable sanitizer pass on certain functions.

In this patch, if no_sanitize("bounds") is provided, an additional
function attribute (NoSanitizeBounds) is attached to IR to let the
BoundsChecking pass know we want to disable local-bounds checking. In
order to support this feature, the IR is extended (similar to D102772)
to make Clang able to preserve the information and let BoundsChecking
pass know bounds checking is disabled for certain function.

Reviewed By: melver

Differential Revision: https://reviews.llvm.org/D119816
This commit is contained in:
Tong Zhang 2022-03-01 10:58:10 +01:00 committed by Marco Elver
parent 6d658f37a4
commit 17ce89fa80
21 changed files with 68 additions and 3 deletions
clang
llvm
bindings/go/llvm
docs
include/llvm
lib
AsmParser
Bitcode
Transforms
test
Bitcode
Instrumentation/BoundsChecking
utils
emacs
llvm.grm
vim/syntax
vscode/llvm/syntaxes

View File

@ -740,6 +740,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
} while (false);
if (D) {
const bool SanitizeBounds = SanOpts.hasOneOf(SanitizerKind::Bounds);
bool NoSanitizeCoverage = false;
for (auto Attr : D->specific_attrs<NoSanitizeAttr>()) {
@ -760,6 +761,9 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
NoSanitizeCoverage = true;
}
if (SanitizeBounds && !SanOpts.hasOneOf(SanitizerKind::Bounds))
Fn->addFnAttr(llvm::Attribute::NoSanitizeBounds);
if (NoSanitizeCoverage && CGM.getCodeGenOpts().hasSanitizeCoverage())
Fn->addFnAttr(llvm::Attribute::NoSanitizeCoverage);
}

View File

@ -49,3 +49,12 @@ int f5(union U *u, int i) {
return u->c[i];
// CHECK: }
}
__attribute__((no_sanitize("bounds")))
int f6(int i) {
int b[64];
// CHECK-NOT: call void @llvm.trap()
// CHECK-NOT: trap:
// CHECK-NOT: cont:
return b[i];
}

View File

@ -53,6 +53,7 @@ void test_no_sanitize_combined(int n) {
// ASAN-NOT: call void @__asan_report_store
// MSAN-NOT: call void @__msan_warning
// BOUNDS-NOT: call void @__ubsan_handle_out_of_bounds
// BOUNDS-NOT: call void @llvm.trap()
// TSAN-NOT: call void @__tsan_func_entry
// UBSAN-NOT: call void @__ubsan_handle
if (n)
@ -72,6 +73,7 @@ void test_no_sanitize_separate(int n) {
// ASAN-NOT: call void @__asan_report_store
// MSAN-NOT: call void @__msan_warning
// BOUNDS-NOT: call void @__ubsan_handle_out_of_bounds
// BOUNDS-NOT: call void @llvm.trap()
// TSAN-NOT: call void @__tsan_func_entry
// UBSAN-NOT: call void @__ubsan_handle
if (n)

View File

@ -69,6 +69,7 @@ func TestAttributes(t *testing.T) {
"noredzone",
"noreturn",
"nounwind",
"nosanitize_bounds",
"nosanitize_coverage",
"optnone",
"optsize",

View File

@ -1078,6 +1078,7 @@ The integer codes are mapped to well-known attributes as follows.
* code 76: ``nosanitize_coverage``
* code 77: ``elementtype``
* code 78: ``disable_sanitizer_instrumentation``
* code 79: ``nosanitize_bounds``
.. note::
The ``allocsize`` attribute has a special encoding for its arguments. Its two

View File

@ -1783,6 +1783,9 @@ example:
trap or generate asynchronous exceptions. Exception handling schemes
that are recognized by LLVM to handle asynchronous exceptions, such
as SEH, will still provide their implementation defined semantics.
``nosanitize_bounds``
This attribute indicates that bounds checking sanitizer instrumentation
is disabled for this function.
``nosanitize_coverage``
This attribute indicates that SanitizerCoverage instrumentation is disabled
for this function.

View File

@ -219,6 +219,7 @@ enum Kind {
kw_nosync,
kw_nocf_check,
kw_nounwind,
kw_nosanitize_bounds,
kw_nosanitize_coverage,
kw_null_pointer_is_valid,
kw_optforfuzzing,

View File

@ -677,6 +677,7 @@ enum AttributeKindCodes {
ATTR_KIND_NO_SANITIZE_COVERAGE = 76,
ATTR_KIND_ELEMENTTYPE = 77,
ATTR_KIND_DISABLE_SANITIZER_INSTRUMENTATION = 78,
ATTR_KIND_NO_SANITIZE_BOUNDS = 79,
};
enum ComdatSelectionKindCodes {

View File

@ -175,6 +175,9 @@ def NoProfile : EnumAttr<"noprofile", [FnAttr]>;
/// Function doesn't unwind stack.
def NoUnwind : EnumAttr<"nounwind", [FnAttr]>;
/// No SanitizeBounds instrumentation.
def NoSanitizeBounds : EnumAttr<"nosanitize_bounds", [FnAttr]>;
/// No SanitizeCoverage instrumentation.
def NoSanitizeCoverage : EnumAttr<"nosanitize_coverage", [FnAttr]>;

View File

@ -672,6 +672,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(nocf_check);
KEYWORD(noundef);
KEYWORD(nounwind);
KEYWORD(nosanitize_bounds);
KEYWORD(nosanitize_coverage);
KEYWORD(null_pointer_is_valid);
KEYWORD(optforfuzzing);

View File

@ -1493,6 +1493,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::NoProfile;
case bitc::ATTR_KIND_NO_UNWIND:
return Attribute::NoUnwind;
case bitc::ATTR_KIND_NO_SANITIZE_BOUNDS:
return Attribute::NoSanitizeBounds;
case bitc::ATTR_KIND_NO_SANITIZE_COVERAGE:
return Attribute::NoSanitizeCoverage;
case bitc::ATTR_KIND_NULL_POINTER_IS_VALID:

View File

@ -688,6 +688,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_NO_PROFILE;
case Attribute::NoUnwind:
return bitc::ATTR_KIND_NO_UNWIND;
case Attribute::NoSanitizeBounds:
return bitc::ATTR_KIND_NO_SANITIZE_BOUNDS;
case Attribute::NoSanitizeCoverage:
return bitc::ATTR_KIND_NO_SANITIZE_COVERAGE;
case Attribute::NullPointerIsValid:

View File

@ -142,6 +142,9 @@ static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB) {
static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,
ScalarEvolution &SE) {
if (F.hasFnAttribute(Attribute::NoSanitizeBounds))
return false;
const DataLayout &DL = F.getParent()->getDataLayout();
ObjectSizeOpts EvalOpts;
EvalOpts.RoundToAlign = true;

View File

@ -939,6 +939,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
case Attribute::NonLazyBind:
case Attribute::NoRedZone:
case Attribute::NoUnwind:
case Attribute::NoSanitizeBounds:
case Attribute::NoSanitizeCoverage:
case Attribute::NullPointerIsValid:
case Attribute::OptForFuzzing:

View File

@ -526,6 +526,12 @@ define void @f85() uwtable(async) {
ret void;
}
; CHECK: define void @f86() #52
define void @f86() nosanitize_bounds
{
ret void;
}
; CHECK: attributes #0 = { noreturn }
; CHECK: attributes #1 = { nounwind }
; CHECK: attributes #2 = { readnone }
@ -578,4 +584,5 @@ define void @f85() uwtable(async) {
; CHECK: attributes #49 = { noprofile }
; CHECK: attributes #50 = { disable_sanitizer_instrumentation }
; CHECK: attributes #51 = { uwtable(sync) }
; CHECK: attributes #52 = { nosanitize_bounds }
; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin }

View File

@ -1510,7 +1510,7 @@ exit:
; CHECK: select <2 x i1> <i1 true, i1 false>, <2 x i8> <i8 2, i8 3>, <2 x i8> <i8 3, i8 2>
call void @f.nobuiltin() builtin
; CHECK: call void @f.nobuiltin() #48
; CHECK: call void @f.nobuiltin() #49
call fastcc noalias i32* @f.noalias() noinline
; CHECK: call fastcc noalias i32* @f.noalias() #12
@ -1930,6 +1930,9 @@ declare void @f.allocsize_two(i32, i32) allocsize(1, 0)
; CHECK: Function Attrs: allocsize(1,0)
; CHECK: declare void @f.allocsize_two(i32, i32)
declare void @f.nosanitize_bounds() nosanitize_bounds
; CHECK: declare void @f.nosanitize_bounds() #48
; CHECK: attributes #0 = { alignstack=4 }
; CHECK: attributes #1 = { alignstack=8 }
; CHECK: attributes #2 = { alwaysinline }
@ -1978,7 +1981,8 @@ declare void @f.allocsize_two(i32, i32) allocsize(1, 0)
; CHECK: attributes #45 = { disable_sanitizer_instrumentation }
; CHECK: attributes #46 = { allocsize(0) }
; CHECK: attributes #47 = { allocsize(1,0) }
; CHECK: attributes #48 = { builtin }
; CHECK: attributes #48 = { nosanitize_bounds }
; CHECK: attributes #49 = { builtin }
;; Metadata

View File

@ -0,0 +1,17 @@
; RUN: opt < %s -passes=bounds-checking -S | FileCheck %s
target datalayout = "e-p:64:64:64-p1:16:16:16-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
; CHECK: @foo
define i32 @foo(i32 %i) nosanitize_bounds {
entry:
%i.addr = alloca i32, align 4
%b = alloca [64 x i32], align 16
store i32 %i, i32* %i.addr, align 4
%0 = load i32, i32* %i.addr, align 4
%idxprom = sext i32 %0 to i64
%arrayidx = getelementptr inbounds [64 x i32], [64 x i32]* %b, i64 0, i64 %idxprom
%1 = load i32, i32* %arrayidx, align 4
ret i32 %1
; CHECK-NOT: call void @llvm.trap()
}

View File

@ -25,7 +25,7 @@
'("alwaysinline" "argmemonly" "allocsize" "builtin" "cold" "convergent" "dereferenceable" "dereferenceable_or_null" "hot" "inaccessiblememonly"
"inaccessiblemem_or_argmemonly" "inalloca" "inlinehint" "jumptable" "minsize" "mustprogress" "naked" "nobuiltin" "nonnull"
"nocallback" "nocf_check" "noduplicate" "nofree" "noimplicitfloat" "noinline" "nomerge" "nonlazybind" "noprofile" "noredzone" "noreturn"
"norecurse" "nosync" "noundef" "nounwind" "nosanitize_coverage" "null_pointer_is_valid" "optforfuzzing" "optnone" "optsize" "preallocated" "readnone" "readonly" "returned" "returns_twice"
"norecurse" "nosync" "noundef" "nounwind" "nosanitize_bounds" "nosanitize_coverage" "null_pointer_is_valid" "optforfuzzing" "optnone" "optsize" "preallocated" "readnone" "readonly" "returned" "returns_twice"
"shadowcallstack" "speculatable" "speculative_load_hardening" "ssp" "sspreq" "sspstrong" "safestack" "sanitize_address" "sanitize_hwaddress" "sanitize_memtag"
"sanitize_thread" "sanitize_memory" "strictfp" "swifterror" "uwtable" "vscale_range" "willreturn" "writeonly" "immarg") 'symbols) . font-lock-constant-face)
;; Variables

View File

@ -176,6 +176,7 @@ FuncAttr ::= noreturn
| sanitize_thread
| sanitize_memory
| mustprogress
| nosanitize_bounds
| nosanitize_coverage
;

View File

@ -139,6 +139,7 @@ syn keyword llvmKeyword
\ nosync
\ noundef
\ nounwind
\ nosanitize_bounds
\ nosanitize_coverage
\ null_pointer_is_valid
\ optforfuzzing

View File

@ -238,6 +238,7 @@ patterns:
\\bnosync\\b|\
\\bnoundef\\b|\
\\bnounwind\\b|\
\\bnosanitize_bounds\\b|\
\\bnosanitize_coverage\\b|\
\\bnull_pointer_is_valid\\b|\
\\boptforfuzzing\\b|\