diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 9c3e5d546001..c1f3a3014a19 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -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()) { @@ -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); } diff --git a/clang/test/CodeGen/bounds-checking.c b/clang/test/CodeGen/bounds-checking.c index ba8c18934388..25b767c7fa0d 100644 --- a/clang/test/CodeGen/bounds-checking.c +++ b/clang/test/CodeGen/bounds-checking.c @@ -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]; +} diff --git a/clang/test/CodeGen/sanitize-coverage.c b/clang/test/CodeGen/sanitize-coverage.c index 6fd0adcb4a88..984076fed0ce 100644 --- a/clang/test/CodeGen/sanitize-coverage.c +++ b/clang/test/CodeGen/sanitize-coverage.c @@ -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) diff --git a/llvm/bindings/go/llvm/ir_test.go b/llvm/bindings/go/llvm/ir_test.go index 61b482f2ef9a..1aeb6e69bafb 100644 --- a/llvm/bindings/go/llvm/ir_test.go +++ b/llvm/bindings/go/llvm/ir_test.go @@ -69,6 +69,7 @@ func TestAttributes(t *testing.T) { "noredzone", "noreturn", "nounwind", + "nosanitize_bounds", "nosanitize_coverage", "optnone", "optsize", diff --git a/llvm/docs/BitCodeFormat.rst b/llvm/docs/BitCodeFormat.rst index 8e81a7daa459..56268f1523cc 100644 --- a/llvm/docs/BitCodeFormat.rst +++ b/llvm/docs/BitCodeFormat.rst @@ -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 diff --git a/llvm/docs/LangRef.rst b/llvm/docs/LangRef.rst index df7065cecd25..3d54e25d4761 100644 --- a/llvm/docs/LangRef.rst +++ b/llvm/docs/LangRef.rst @@ -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. diff --git a/llvm/include/llvm/AsmParser/LLToken.h b/llvm/include/llvm/AsmParser/LLToken.h index faac67ebbab9..7b0cf69bce2d 100644 --- a/llvm/include/llvm/AsmParser/LLToken.h +++ b/llvm/include/llvm/AsmParser/LLToken.h @@ -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, diff --git a/llvm/include/llvm/Bitcode/LLVMBitCodes.h b/llvm/include/llvm/Bitcode/LLVMBitCodes.h index 6d0f51ce9c6d..a026b44bf32f 100644 --- a/llvm/include/llvm/Bitcode/LLVMBitCodes.h +++ b/llvm/include/llvm/Bitcode/LLVMBitCodes.h @@ -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 { diff --git a/llvm/include/llvm/IR/Attributes.td b/llvm/include/llvm/IR/Attributes.td index d7a79f90e05e..a0ab91382691 100644 --- a/llvm/include/llvm/IR/Attributes.td +++ b/llvm/include/llvm/IR/Attributes.td @@ -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]>; diff --git a/llvm/lib/AsmParser/LLLexer.cpp b/llvm/lib/AsmParser/LLLexer.cpp index a508660edfa5..3f0c60fd571a 100644 --- a/llvm/lib/AsmParser/LLLexer.cpp +++ b/llvm/lib/AsmParser/LLLexer.cpp @@ -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); diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index dee8c4daaada..5cd29e7f7923 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -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: diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index 4bba0b356675..6b5147ff7807 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -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: diff --git a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp index 4ad07cab001a..d7b7b73ed9d5 100644 --- a/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp +++ b/llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp @@ -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; diff --git a/llvm/lib/Transforms/Utils/CodeExtractor.cpp b/llvm/lib/Transforms/Utils/CodeExtractor.cpp index cec159f6a448..01d149c7d619 100644 --- a/llvm/lib/Transforms/Utils/CodeExtractor.cpp +++ b/llvm/lib/Transforms/Utils/CodeExtractor.cpp @@ -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: diff --git a/llvm/test/Bitcode/attributes.ll b/llvm/test/Bitcode/attributes.ll index 5d3828d2762d..ee582cab628d 100644 --- a/llvm/test/Bitcode/attributes.ll +++ b/llvm/test/Bitcode/attributes.ll @@ -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 } diff --git a/llvm/test/Bitcode/compatibility.ll b/llvm/test/Bitcode/compatibility.ll index 1f0da632343e..df8e0ac9ac3c 100644 --- a/llvm/test/Bitcode/compatibility.ll +++ b/llvm/test/Bitcode/compatibility.ll @@ -1510,7 +1510,7 @@ exit: ; 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 @@ 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 diff --git a/llvm/test/Instrumentation/BoundsChecking/nosanitize-bounds.ll b/llvm/test/Instrumentation/BoundsChecking/nosanitize-bounds.ll new file mode 100644 index 000000000000..03efb3b1cd67 --- /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 index 1fb811b1d3c9..c6d594d118d0 100644 --- 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 index be6dec78f9f5..411323178bde 100644 --- a/llvm/utils/llvm.grm +++ b/llvm/utils/llvm.grm @@ -176,6 +176,7 @@ FuncAttr ::= noreturn | 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 index 205db16b7d8c..9185a029a22e 100644 --- a/llvm/utils/vim/syntax/llvm.vim +++ b/llvm/utils/vim/syntax/llvm.vim @@ -139,6 +139,7 @@ syn keyword llvmKeyword \ 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 index 127f5d9867a0..d80c3778bbe7 100644 --- a/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml +++ b/llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml @@ -238,6 +238,7 @@ patterns: \\bnosync\\b|\ \\bnoundef\\b|\ \\bnounwind\\b|\ + \\bnosanitize_bounds\\b|\ \\bnosanitize_coverage\\b|\ \\bnull_pointer_is_valid\\b|\ \\boptforfuzzing\\b|\