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
clang
llvm
bindings/go/llvm
docs
include/llvm
lib
AsmParser
Bitcode
Transforms
test
utils
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -69,6 +69,7 @@ func TestAttributes(t *testing.T) {
|
|||
"noredzone",
|
||||
"noreturn",
|
||||
"nounwind",
|
||||
"nosanitize_bounds",
|
||||
"nosanitize_coverage",
|
||||
"optnone",
|
||||
"optsize",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]>;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 }
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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"
|
||||
"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
|
||||
|
|
|
@ -176,6 +176,7 @@ FuncAttr ::= noreturn
|
|||
| sanitize_thread
|
||||
| sanitize_memory
|
||||
| mustprogress
|
||||
| nosanitize_bounds
|
||||
| nosanitize_coverage
|
||||
;
|
||||
|
||||
|
|
|
@ -139,6 +139,7 @@ syn keyword llvmKeyword
|
|||
\ nosync
|
||||
\ noundef
|
||||
\ nounwind
|
||||
\ nosanitize_bounds
|
||||
\ nosanitize_coverage
|
||||
\ null_pointer_is_valid
|
||||
\ optforfuzzing
|
||||
|
|
|
@ -238,6 +238,7 @@ patterns:
|
|||
\\bnosync\\b|\
|
||||
\\bnoundef\\b|\
|
||||
\\bnounwind\\b|\
|
||||
\\bnosanitize_bounds\\b|\
|
||||
\\bnosanitize_coverage\\b|\
|
||||
\\bnull_pointer_is_valid\\b|\
|
||||
\\boptforfuzzing\\b|\
|
||||
|
|
Loading…
Reference in New Issue