[SanitizerBounds] Add support for NoSanitizeBounds function
authorTong Zhang <ztong0001@gmail.com>
Tue, 1 Mar 2022 09:58:10 +0000 (10:58 +0100)
committerMarco Elver <elver@google.com>
Tue, 1 Mar 2022 17:47:02 +0000 (18:47 +0100)
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

21 files changed:
clang/lib/CodeGen/CodeGenFunction.cpp
clang/test/CodeGen/bounds-checking.c
clang/test/CodeGen/sanitize-coverage.c
llvm/bindings/go/llvm/ir_test.go
llvm/docs/BitCodeFormat.rst
llvm/docs/LangRef.rst
llvm/include/llvm/AsmParser/LLToken.h
llvm/include/llvm/Bitcode/LLVMBitCodes.h
llvm/include/llvm/IR/Attributes.td
llvm/lib/AsmParser/LLLexer.cpp
llvm/lib/Bitcode/Reader/BitcodeReader.cpp
llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
llvm/lib/Transforms/Utils/CodeExtractor.cpp
llvm/test/Bitcode/attributes.ll
llvm/test/Bitcode/compatibility.ll
llvm/test/Instrumentation/BoundsChecking/nosanitize-bounds.ll [new file with mode: 0644]
llvm/utils/emacs/llvm-mode.el
llvm/utils/llvm.grm
llvm/utils/vim/syntax/llvm.vim
llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml

index 9c3e5d5..c1f3a30 100644 (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);
   }
index ba8c189..25b767c 100644 (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];
+}
index 6fd0adc..984076f 100644 (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)
index 61b482f..1aeb6e6 100644 (file)
@@ -69,6 +69,7 @@ func TestAttributes(t *testing.T) {
                "noredzone",
                "noreturn",
                "nounwind",
+               "nosanitize_bounds",
                "nosanitize_coverage",
                "optnone",
                "optsize",
index 8e81a7d..56268f1 100644 (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
index df7065c..3d54e25 100644 (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.
index faac67e..7b0cf69 100644 (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,
index 6d0f51c..a026b44 100644 (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 {
index d7a79f9..a0ab913 100644 (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]>;
 
index a508660..3f0c60f 100644 (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);
index dee8c4d..5cd29e7 100644 (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:
index 4bba0b3..6b5147f 100644 (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:
index 4ad07ca..d7b7b73 100644 (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;
index cec159f..01d149c 100644 (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:
index 5d3828d..ee582ca 100644 (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 }
index 1f0da63..df8e0ac 100644 (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
 
diff --git a/llvm/test/Instrumentation/BoundsChecking/nosanitize-bounds.ll b/llvm/test/Instrumentation/BoundsChecking/nosanitize-bounds.ll
new file mode 100644 (file)
index 0000000..03efb3b
--- /dev/null
@@ -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()
+}
+
index 1fb811b..c6d594d 100644 (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
index be6dec7..4113231 100644 (file)
@@ -176,6 +176,7 @@ FuncAttr      ::= noreturn
  | sanitize_thread
  | sanitize_memory
  | mustprogress
+ | nosanitize_bounds
  | nosanitize_coverage
  ;
 
index 205db16..9185a02 100644 (file)
@@ -139,6 +139,7 @@ syn keyword llvmKeyword
       \ nosync
       \ noundef
       \ nounwind
+      \ nosanitize_bounds
       \ nosanitize_coverage
       \ null_pointer_is_valid
       \ optforfuzzing
index 127f5d9..d80c377 100644 (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|\