forked from OSchip/llvm-project
[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:
parent
6d658f37a4
commit
17ce89fa80
|
@ -740,6 +740,7 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
|
||||||
} while (false);
|
} while (false);
|
||||||
|
|
||||||
if (D) {
|
if (D) {
|
||||||
|
const bool SanitizeBounds = SanOpts.hasOneOf(SanitizerKind::Bounds);
|
||||||
bool NoSanitizeCoverage = false;
|
bool NoSanitizeCoverage = false;
|
||||||
|
|
||||||
for (auto Attr : D->specific_attrs<NoSanitizeAttr>()) {
|
for (auto Attr : D->specific_attrs<NoSanitizeAttr>()) {
|
||||||
|
@ -760,6 +761,9 @@ void CodeGenFunction::StartFunction(GlobalDecl GD, QualType RetTy,
|
||||||
NoSanitizeCoverage = true;
|
NoSanitizeCoverage = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (SanitizeBounds && !SanOpts.hasOneOf(SanitizerKind::Bounds))
|
||||||
|
Fn->addFnAttr(llvm::Attribute::NoSanitizeBounds);
|
||||||
|
|
||||||
if (NoSanitizeCoverage && CGM.getCodeGenOpts().hasSanitizeCoverage())
|
if (NoSanitizeCoverage && CGM.getCodeGenOpts().hasSanitizeCoverage())
|
||||||
Fn->addFnAttr(llvm::Attribute::NoSanitizeCoverage);
|
Fn->addFnAttr(llvm::Attribute::NoSanitizeCoverage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,3 +49,12 @@ int f5(union U *u, int i) {
|
||||||
return u->c[i];
|
return u->c[i];
|
||||||
// CHECK: }
|
// 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];
|
||||||
|
}
|
||||||
|
|
|
@ -53,6 +53,7 @@ void test_no_sanitize_combined(int n) {
|
||||||
// ASAN-NOT: call void @__asan_report_store
|
// ASAN-NOT: call void @__asan_report_store
|
||||||
// MSAN-NOT: call void @__msan_warning
|
// MSAN-NOT: call void @__msan_warning
|
||||||
// BOUNDS-NOT: call void @__ubsan_handle_out_of_bounds
|
// BOUNDS-NOT: call void @__ubsan_handle_out_of_bounds
|
||||||
|
// BOUNDS-NOT: call void @llvm.trap()
|
||||||
// TSAN-NOT: call void @__tsan_func_entry
|
// TSAN-NOT: call void @__tsan_func_entry
|
||||||
// UBSAN-NOT: call void @__ubsan_handle
|
// UBSAN-NOT: call void @__ubsan_handle
|
||||||
if (n)
|
if (n)
|
||||||
|
@ -72,6 +73,7 @@ void test_no_sanitize_separate(int n) {
|
||||||
// ASAN-NOT: call void @__asan_report_store
|
// ASAN-NOT: call void @__asan_report_store
|
||||||
// MSAN-NOT: call void @__msan_warning
|
// MSAN-NOT: call void @__msan_warning
|
||||||
// BOUNDS-NOT: call void @__ubsan_handle_out_of_bounds
|
// BOUNDS-NOT: call void @__ubsan_handle_out_of_bounds
|
||||||
|
// BOUNDS-NOT: call void @llvm.trap()
|
||||||
// TSAN-NOT: call void @__tsan_func_entry
|
// TSAN-NOT: call void @__tsan_func_entry
|
||||||
// UBSAN-NOT: call void @__ubsan_handle
|
// UBSAN-NOT: call void @__ubsan_handle
|
||||||
if (n)
|
if (n)
|
||||||
|
|
|
@ -69,6 +69,7 @@ func TestAttributes(t *testing.T) {
|
||||||
"noredzone",
|
"noredzone",
|
||||||
"noreturn",
|
"noreturn",
|
||||||
"nounwind",
|
"nounwind",
|
||||||
|
"nosanitize_bounds",
|
||||||
"nosanitize_coverage",
|
"nosanitize_coverage",
|
||||||
"optnone",
|
"optnone",
|
||||||
"optsize",
|
"optsize",
|
||||||
|
|
|
@ -1078,6 +1078,7 @@ The integer codes are mapped to well-known attributes as follows.
|
||||||
* code 76: ``nosanitize_coverage``
|
* code 76: ``nosanitize_coverage``
|
||||||
* code 77: ``elementtype``
|
* code 77: ``elementtype``
|
||||||
* code 78: ``disable_sanitizer_instrumentation``
|
* code 78: ``disable_sanitizer_instrumentation``
|
||||||
|
* code 79: ``nosanitize_bounds``
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
The ``allocsize`` attribute has a special encoding for its arguments. Its two
|
The ``allocsize`` attribute has a special encoding for its arguments. Its two
|
||||||
|
|
|
@ -1783,6 +1783,9 @@ example:
|
||||||
trap or generate asynchronous exceptions. Exception handling schemes
|
trap or generate asynchronous exceptions. Exception handling schemes
|
||||||
that are recognized by LLVM to handle asynchronous exceptions, such
|
that are recognized by LLVM to handle asynchronous exceptions, such
|
||||||
as SEH, will still provide their implementation defined semantics.
|
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``
|
``nosanitize_coverage``
|
||||||
This attribute indicates that SanitizerCoverage instrumentation is disabled
|
This attribute indicates that SanitizerCoverage instrumentation is disabled
|
||||||
for this function.
|
for this function.
|
||||||
|
|
|
@ -219,6 +219,7 @@ enum Kind {
|
||||||
kw_nosync,
|
kw_nosync,
|
||||||
kw_nocf_check,
|
kw_nocf_check,
|
||||||
kw_nounwind,
|
kw_nounwind,
|
||||||
|
kw_nosanitize_bounds,
|
||||||
kw_nosanitize_coverage,
|
kw_nosanitize_coverage,
|
||||||
kw_null_pointer_is_valid,
|
kw_null_pointer_is_valid,
|
||||||
kw_optforfuzzing,
|
kw_optforfuzzing,
|
||||||
|
|
|
@ -677,6 +677,7 @@ enum AttributeKindCodes {
|
||||||
ATTR_KIND_NO_SANITIZE_COVERAGE = 76,
|
ATTR_KIND_NO_SANITIZE_COVERAGE = 76,
|
||||||
ATTR_KIND_ELEMENTTYPE = 77,
|
ATTR_KIND_ELEMENTTYPE = 77,
|
||||||
ATTR_KIND_DISABLE_SANITIZER_INSTRUMENTATION = 78,
|
ATTR_KIND_DISABLE_SANITIZER_INSTRUMENTATION = 78,
|
||||||
|
ATTR_KIND_NO_SANITIZE_BOUNDS = 79,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum ComdatSelectionKindCodes {
|
enum ComdatSelectionKindCodes {
|
||||||
|
|
|
@ -175,6 +175,9 @@ def NoProfile : EnumAttr<"noprofile", [FnAttr]>;
|
||||||
/// Function doesn't unwind stack.
|
/// Function doesn't unwind stack.
|
||||||
def NoUnwind : EnumAttr<"nounwind", [FnAttr]>;
|
def NoUnwind : EnumAttr<"nounwind", [FnAttr]>;
|
||||||
|
|
||||||
|
/// No SanitizeBounds instrumentation.
|
||||||
|
def NoSanitizeBounds : EnumAttr<"nosanitize_bounds", [FnAttr]>;
|
||||||
|
|
||||||
/// No SanitizeCoverage instrumentation.
|
/// No SanitizeCoverage instrumentation.
|
||||||
def NoSanitizeCoverage : EnumAttr<"nosanitize_coverage", [FnAttr]>;
|
def NoSanitizeCoverage : EnumAttr<"nosanitize_coverage", [FnAttr]>;
|
||||||
|
|
||||||
|
|
|
@ -672,6 +672,7 @@ lltok::Kind LLLexer::LexIdentifier() {
|
||||||
KEYWORD(nocf_check);
|
KEYWORD(nocf_check);
|
||||||
KEYWORD(noundef);
|
KEYWORD(noundef);
|
||||||
KEYWORD(nounwind);
|
KEYWORD(nounwind);
|
||||||
|
KEYWORD(nosanitize_bounds);
|
||||||
KEYWORD(nosanitize_coverage);
|
KEYWORD(nosanitize_coverage);
|
||||||
KEYWORD(null_pointer_is_valid);
|
KEYWORD(null_pointer_is_valid);
|
||||||
KEYWORD(optforfuzzing);
|
KEYWORD(optforfuzzing);
|
||||||
|
|
|
@ -1493,6 +1493,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
|
||||||
return Attribute::NoProfile;
|
return Attribute::NoProfile;
|
||||||
case bitc::ATTR_KIND_NO_UNWIND:
|
case bitc::ATTR_KIND_NO_UNWIND:
|
||||||
return Attribute::NoUnwind;
|
return Attribute::NoUnwind;
|
||||||
|
case bitc::ATTR_KIND_NO_SANITIZE_BOUNDS:
|
||||||
|
return Attribute::NoSanitizeBounds;
|
||||||
case bitc::ATTR_KIND_NO_SANITIZE_COVERAGE:
|
case bitc::ATTR_KIND_NO_SANITIZE_COVERAGE:
|
||||||
return Attribute::NoSanitizeCoverage;
|
return Attribute::NoSanitizeCoverage;
|
||||||
case bitc::ATTR_KIND_NULL_POINTER_IS_VALID:
|
case bitc::ATTR_KIND_NULL_POINTER_IS_VALID:
|
||||||
|
|
|
@ -688,6 +688,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
|
||||||
return bitc::ATTR_KIND_NO_PROFILE;
|
return bitc::ATTR_KIND_NO_PROFILE;
|
||||||
case Attribute::NoUnwind:
|
case Attribute::NoUnwind:
|
||||||
return bitc::ATTR_KIND_NO_UNWIND;
|
return bitc::ATTR_KIND_NO_UNWIND;
|
||||||
|
case Attribute::NoSanitizeBounds:
|
||||||
|
return bitc::ATTR_KIND_NO_SANITIZE_BOUNDS;
|
||||||
case Attribute::NoSanitizeCoverage:
|
case Attribute::NoSanitizeCoverage:
|
||||||
return bitc::ATTR_KIND_NO_SANITIZE_COVERAGE;
|
return bitc::ATTR_KIND_NO_SANITIZE_COVERAGE;
|
||||||
case Attribute::NullPointerIsValid:
|
case Attribute::NullPointerIsValid:
|
||||||
|
|
|
@ -142,6 +142,9 @@ static void insertBoundsCheck(Value *Or, BuilderTy &IRB, GetTrapBBT GetTrapBB) {
|
||||||
|
|
||||||
static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,
|
static bool addBoundsChecking(Function &F, TargetLibraryInfo &TLI,
|
||||||
ScalarEvolution &SE) {
|
ScalarEvolution &SE) {
|
||||||
|
if (F.hasFnAttribute(Attribute::NoSanitizeBounds))
|
||||||
|
return false;
|
||||||
|
|
||||||
const DataLayout &DL = F.getParent()->getDataLayout();
|
const DataLayout &DL = F.getParent()->getDataLayout();
|
||||||
ObjectSizeOpts EvalOpts;
|
ObjectSizeOpts EvalOpts;
|
||||||
EvalOpts.RoundToAlign = true;
|
EvalOpts.RoundToAlign = true;
|
||||||
|
|
|
@ -939,6 +939,7 @@ Function *CodeExtractor::constructFunction(const ValueSet &inputs,
|
||||||
case Attribute::NonLazyBind:
|
case Attribute::NonLazyBind:
|
||||||
case Attribute::NoRedZone:
|
case Attribute::NoRedZone:
|
||||||
case Attribute::NoUnwind:
|
case Attribute::NoUnwind:
|
||||||
|
case Attribute::NoSanitizeBounds:
|
||||||
case Attribute::NoSanitizeCoverage:
|
case Attribute::NoSanitizeCoverage:
|
||||||
case Attribute::NullPointerIsValid:
|
case Attribute::NullPointerIsValid:
|
||||||
case Attribute::OptForFuzzing:
|
case Attribute::OptForFuzzing:
|
||||||
|
|
|
@ -526,6 +526,12 @@ define void @f85() uwtable(async) {
|
||||||
ret void;
|
ret void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
; CHECK: define void @f86() #52
|
||||||
|
define void @f86() nosanitize_bounds
|
||||||
|
{
|
||||||
|
ret void;
|
||||||
|
}
|
||||||
|
|
||||||
; CHECK: attributes #0 = { noreturn }
|
; CHECK: attributes #0 = { noreturn }
|
||||||
; CHECK: attributes #1 = { nounwind }
|
; CHECK: attributes #1 = { nounwind }
|
||||||
; CHECK: attributes #2 = { readnone }
|
; CHECK: attributes #2 = { readnone }
|
||||||
|
@ -578,4 +584,5 @@ define void @f85() uwtable(async) {
|
||||||
; CHECK: attributes #49 = { noprofile }
|
; CHECK: attributes #49 = { noprofile }
|
||||||
; CHECK: attributes #50 = { disable_sanitizer_instrumentation }
|
; CHECK: attributes #50 = { disable_sanitizer_instrumentation }
|
||||||
; CHECK: attributes #51 = { uwtable(sync) }
|
; CHECK: attributes #51 = { uwtable(sync) }
|
||||||
|
; CHECK: attributes #52 = { nosanitize_bounds }
|
||||||
; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin }
|
; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin }
|
||||||
|
|
|
@ -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>
|
; 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
|
call void @f.nobuiltin() builtin
|
||||||
; CHECK: call void @f.nobuiltin() #48
|
; CHECK: call void @f.nobuiltin() #49
|
||||||
|
|
||||||
call fastcc noalias i32* @f.noalias() noinline
|
call fastcc noalias i32* @f.noalias() noinline
|
||||||
; CHECK: call fastcc noalias i32* @f.noalias() #12
|
; 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: Function Attrs: allocsize(1,0)
|
||||||
; CHECK: declare void @f.allocsize_two(i32, i32)
|
; 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 #0 = { alignstack=4 }
|
||||||
; CHECK: attributes #1 = { alignstack=8 }
|
; CHECK: attributes #1 = { alignstack=8 }
|
||||||
; CHECK: attributes #2 = { alwaysinline }
|
; 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 #45 = { disable_sanitizer_instrumentation }
|
||||||
; CHECK: attributes #46 = { allocsize(0) }
|
; CHECK: attributes #46 = { allocsize(0) }
|
||||||
; CHECK: attributes #47 = { allocsize(1,0) }
|
; CHECK: attributes #47 = { allocsize(1,0) }
|
||||||
; CHECK: attributes #48 = { builtin }
|
; CHECK: attributes #48 = { nosanitize_bounds }
|
||||||
|
; CHECK: attributes #49 = { builtin }
|
||||||
|
|
||||||
;; Metadata
|
;; Metadata
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
'("alwaysinline" "argmemonly" "allocsize" "builtin" "cold" "convergent" "dereferenceable" "dereferenceable_or_null" "hot" "inaccessiblememonly"
|
'("alwaysinline" "argmemonly" "allocsize" "builtin" "cold" "convergent" "dereferenceable" "dereferenceable_or_null" "hot" "inaccessiblememonly"
|
||||||
"inaccessiblemem_or_argmemonly" "inalloca" "inlinehint" "jumptable" "minsize" "mustprogress" "naked" "nobuiltin" "nonnull"
|
"inaccessiblemem_or_argmemonly" "inalloca" "inlinehint" "jumptable" "minsize" "mustprogress" "naked" "nobuiltin" "nonnull"
|
||||||
"nocallback" "nocf_check" "noduplicate" "nofree" "noimplicitfloat" "noinline" "nomerge" "nonlazybind" "noprofile" "noredzone" "noreturn"
|
"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"
|
"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)
|
"sanitize_thread" "sanitize_memory" "strictfp" "swifterror" "uwtable" "vscale_range" "willreturn" "writeonly" "immarg") 'symbols) . font-lock-constant-face)
|
||||||
;; Variables
|
;; Variables
|
||||||
|
|
|
@ -176,6 +176,7 @@ FuncAttr ::= noreturn
|
||||||
| sanitize_thread
|
| sanitize_thread
|
||||||
| sanitize_memory
|
| sanitize_memory
|
||||||
| mustprogress
|
| mustprogress
|
||||||
|
| nosanitize_bounds
|
||||||
| nosanitize_coverage
|
| nosanitize_coverage
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -139,6 +139,7 @@ syn keyword llvmKeyword
|
||||||
\ nosync
|
\ nosync
|
||||||
\ noundef
|
\ noundef
|
||||||
\ nounwind
|
\ nounwind
|
||||||
|
\ nosanitize_bounds
|
||||||
\ nosanitize_coverage
|
\ nosanitize_coverage
|
||||||
\ null_pointer_is_valid
|
\ null_pointer_is_valid
|
||||||
\ optforfuzzing
|
\ optforfuzzing
|
||||||
|
|
|
@ -238,6 +238,7 @@ patterns:
|
||||||
\\bnosync\\b|\
|
\\bnosync\\b|\
|
||||||
\\bnoundef\\b|\
|
\\bnoundef\\b|\
|
||||||
\\bnounwind\\b|\
|
\\bnounwind\\b|\
|
||||||
|
\\bnosanitize_bounds\\b|\
|
||||||
\\bnosanitize_coverage\\b|\
|
\\bnosanitize_coverage\\b|\
|
||||||
\\bnull_pointer_is_valid\\b|\
|
\\bnull_pointer_is_valid\\b|\
|
||||||
\\boptforfuzzing\\b|\
|
\\boptforfuzzing\\b|\
|
||||||
|
|
Loading…
Reference in New Issue