Skip to content

Commit

Permalink
[SanitizerBounds] Add support for NoSanitizeBounds function
Browse files Browse the repository at this point in the history
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
  • Loading branch information
lzto authored and melver committed Mar 1, 2022
1 parent 6d658f3 commit 17ce89f
Show file tree
Hide file tree
Showing 21 changed files with 68 additions and 3 deletions.
4 changes: 4 additions & 0 deletions clang/lib/CodeGen/CodeGenFunction.cpp
Expand Up @@ -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>()) {
Expand All @@ -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);
}
Expand Down
9 changes: 9 additions & 0 deletions clang/test/CodeGen/bounds-checking.c
Expand Up @@ -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];
}
2 changes: 2 additions & 0 deletions clang/test/CodeGen/sanitize-coverage.c
Expand Up @@ -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)
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions llvm/bindings/go/llvm/ir_test.go
Expand Up @@ -69,6 +69,7 @@ func TestAttributes(t *testing.T) {
"noredzone",
"noreturn",
"nounwind",
"nosanitize_bounds",
"nosanitize_coverage",
"optnone",
"optsize",
Expand Down
1 change: 1 addition & 0 deletions llvm/docs/BitCodeFormat.rst
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions llvm/docs/LangRef.rst
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/AsmParser/LLToken.h
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Bitcode/LLVMBitCodes.h
Expand Up @@ -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 {
Expand Down
3 changes: 3 additions & 0 deletions llvm/include/llvm/IR/Attributes.td
Expand Up @@ -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]>;

Expand Down
1 change: 1 addition & 0 deletions llvm/lib/AsmParser/LLLexer.cpp
Expand Up @@ -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);
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Bitcode/Reader/BitcodeReader.cpp
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Bitcode/Writer/BitcodeWriter.cpp
Expand Up @@ -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:
Expand Down
3 changes: 3 additions & 0 deletions llvm/lib/Transforms/Instrumentation/BoundsChecking.cpp
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Transforms/Utils/CodeExtractor.cpp
Expand Up @@ -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:
Expand Down
7 changes: 7 additions & 0 deletions llvm/test/Bitcode/attributes.ll
Expand Up @@ -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 }
Expand Down Expand Up @@ -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 }
8 changes: 6 additions & 2 deletions llvm/test/Bitcode/compatibility.ll
Expand Up @@ -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
Expand Down Expand Up @@ -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 }
Expand Down Expand Up @@ -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

Expand Down
17 changes: 17 additions & 0 deletions 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()
}

2 changes: 1 addition & 1 deletion llvm/utils/emacs/llvm-mode.el
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions llvm/utils/llvm.grm
Expand Up @@ -176,6 +176,7 @@ FuncAttr ::= noreturn
| sanitize_thread
| sanitize_memory
| mustprogress
| nosanitize_bounds
| nosanitize_coverage
;

Expand Down
1 change: 1 addition & 0 deletions llvm/utils/vim/syntax/llvm.vim
Expand Up @@ -139,6 +139,7 @@ syn keyword llvmKeyword
\ nosync
\ noundef
\ nounwind
\ nosanitize_bounds
\ nosanitize_coverage
\ null_pointer_is_valid
\ optforfuzzing
Expand Down
1 change: 1 addition & 0 deletions llvm/utils/vscode/llvm/syntaxes/ll.tmLanguage.yaml
Expand Up @@ -238,6 +238,7 @@ patterns:
\\bnosync\\b|\
\\bnoundef\\b|\
\\bnounwind\\b|\
\\bnosanitize_bounds\\b|\
\\bnosanitize_coverage\\b|\
\\bnull_pointer_is_valid\\b|\
\\boptforfuzzing\\b|\
Expand Down

0 comments on commit 17ce89f

Please sign in to comment.