diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -740,6 +740,7 @@ } while (false); if (D) { + const bool SanitizeBounds = SanOpts.hasOneOf(SanitizerKind::Bounds); bool NoSanitizeCoverage = false; for (auto Attr : D->specific_attrs()) { @@ -760,6 +761,9 @@ NoSanitizeCoverage = true; } + if (SanitizeBounds && !SanOpts.hasOneOf(SanitizerKind::Bounds)) + Fn->addFnAttr(llvm::Attribute::NoSanitizeBounds); + if (NoSanitizeCoverage && CGM.getCodeGenOpts().hasSanitizeCoverage()) Fn->addFnAttr(llvm::Attribute::NoSanitizeCoverage); } diff --git a/clang/test/CodeGen/bounds-checking.c b/clang/test/CodeGen/bounds-checking.c --- a/clang/test/CodeGen/bounds-checking.c +++ b/clang/test/CodeGen/bounds-checking.c @@ -49,3 +49,12 @@ 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]; +} diff --git a/clang/test/CodeGen/sanitize-coverage.c b/clang/test/CodeGen/sanitize-coverage.c --- a/clang/test/CodeGen/sanitize-coverage.c +++ b/clang/test/CodeGen/sanitize-coverage.c @@ -53,6 +53,7 @@ // 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 @@ // 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) diff --git a/llvm/bindings/go/llvm/ir_test.go b/llvm/bindings/go/llvm/ir_test.go --- a/llvm/bindings/go/llvm/ir_test.go +++ b/llvm/bindings/go/llvm/ir_test.go @@ -69,6 +69,7 @@ "noredzone", "noreturn", "nounwind", + "nosanitize_bounds", "nosanitize_coverage", "optnone", "optsize", diff --git a/llvm/docs/BitCodeFormat.rst b/llvm/docs/BitCodeFormat.rst --- a/llvm/docs/BitCodeFormat.rst +++ b/llvm/docs/BitCodeFormat.rst @@ -1078,6 +1078,7 @@ * 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 diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -1783,6 +1783,9 @@ 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. diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -219,6 +219,7 @@ kw_nosync, kw_nocf_check, kw_nounwind, + kw_nosanitize_bounds, kw_nosanitize_coverage, kw_null_pointer_is_valid, kw_optforfuzzing, diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -677,6 +677,7 @@ ATTR_KIND_NO_SANITIZE_COVERAGE = 76, ATTR_KIND_ELEMENTTYPE = 77, ATTR_KIND_DISABLE_SANITIZER_INSTRUMENTATION = 78, + ATTR_KIND_NO_SANITIZE_BOUNDS = 79, }; enum ComdatSelectionKindCodes { diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -175,6 +175,9 @@ /// 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]>; diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -672,6 +672,7 @@ KEYWORD(nocf_check); KEYWORD(noundef); KEYWORD(nounwind); + KEYWORD(nosanitize_bounds); KEYWORD(nosanitize_coverage); KEYWORD(null_pointer_is_valid); KEYWORD(optforfuzzing); diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -1493,6 +1493,8 @@ 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: diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -688,6 +688,8 @@ 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: diff --git a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp --- a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp +++ b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp @@ -142,6 +142,9 @@ 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; diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -939,6 +939,7 @@ case Attribute::NonLazyBind: case Attribute::NoRedZone: case Attribute::NoUnwind: + case Attribute::NoSanitizeBounds: case Attribute::NoSanitizeCoverage: case Attribute::NullPointerIsValid: case Attribute::OptForFuzzing: diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -526,6 +526,12 @@ 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 @@ ; CHECK: attributes #49 = { noprofile } ; CHECK: attributes #50 = { disable_sanitizer_instrumentation } ; CHECK: attributes #51 = { uwtable(sync) } +; CHECK: attributes #52 = { nosanitize_bounds } ; CHECK: attributes #[[NOBUILTIN]] = { nobuiltin } diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll --- a/llvm/test/Bitcode/compatibility.ll +++ b/llvm/test/Bitcode/compatibility.ll @@ -1510,7 +1510,7 @@ ; CHECK: select <2 x i1> , <2 x i8> , <2 x i8> 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 @@ ; 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 @@ ; 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 diff --git a/llvm/test/Instrumentation/BoundsChecking/nosanitize-bounds.ll b/llvm/test/Instrumentation/BoundsChecking/nosanitize-bounds.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Instrumentation/BoundsChecking/nosanitize-bounds.ll @@ -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() +} + diff --git a/llvm/utils/emacs/llvm-mode.el b/llvm/utils/emacs/llvm-mode.el --- a/llvm/utils/emacs/llvm-mode.el +++ b/llvm/utils/emacs/llvm-mode.el @@ -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 diff --git a/llvm/utils/llvm.grm b/llvm/utils/llvm.grm --- a/llvm/utils/llvm.grm +++ b/llvm/utils/llvm.grm @@ -176,6 +176,7 @@ | sanitize_thread | sanitize_memory | mustprogress + | nosanitize_bounds | nosanitize_coverage ; diff --git a/llvm/utils/vim/syntax/llvm.vim b/llvm/utils/vim/syntax/llvm.vim --- a/llvm/utils/vim/syntax/llvm.vim +++ b/llvm/utils/vim/syntax/llvm.vim @@ -139,6 +139,7 @@ \ nosync \ noundef \ nounwind + \ nosanitize_bounds \ nosanitize_coverage \ null_pointer_is_valid \ optforfuzzing diff --git a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml --- a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml +++ b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml @@ -238,6 +238,7 @@ \\bnosync\\b|\ \\bnoundef\\b|\ \\bnounwind\\b|\ + \\bnosanitize_bounds\\b|\ \\bnosanitize_coverage\\b|\ \\bnull_pointer_is_valid\\b|\ \\boptforfuzzing\\b|\