[UBSan] Add returns-nonnull sanitizer.
Summary: This patch adds a runtime check verifying that functions annotated with "returns_nonnull" attribute do in fact return nonnull pointers. It is based on suggestion by Jakub Jelinek: http://lists.cs.uiuc.edu/pipermail/llvm-commits/Week-of-Mon-20140623/223693.html. Test Plan: regression test suite Reviewers: rsmith Reviewed By: rsmith Subscribers: cfe-commits Differential Revision: http://reviews.llvm.org/D4849 llvm-svn: 215485
This commit is contained in:
parent
cfe8fc3e28
commit
de443c5002
|
@ -986,6 +986,8 @@ are listed below.
|
|||
more problems at higher optimization levels.
|
||||
- ``-fsanitize=return``: In C++, reaching the end of a
|
||||
value-returning function without returning a value.
|
||||
- ``-fsanitize=returns-nonnull-attribute``: Returning null pointer
|
||||
from a function which is declared to never return null.
|
||||
- ``-fsanitize=shift``: Shift operators where the amount shifted is
|
||||
greater or equal to the promoted bit-width of the left hand side
|
||||
or less than zero, or where the left hand side is negative. For a
|
||||
|
|
|
@ -62,6 +62,7 @@ SANITIZER("integer-divide-by-zero", IntegerDivideByZero)
|
|||
SANITIZER("null", Null)
|
||||
SANITIZER("object-size", ObjectSize)
|
||||
SANITIZER("return", Return)
|
||||
SANITIZER("returns-nonnull-attribute", ReturnsNonnullAttribute)
|
||||
SANITIZER("shift", Shift)
|
||||
SANITIZER("signed-integer-overflow", SignedIntegerOverflow)
|
||||
SANITIZER("unreachable", Unreachable)
|
||||
|
@ -78,9 +79,9 @@ SANITIZER("dataflow", DataFlow)
|
|||
// ABI or address space layout implications, and only catch undefined behavior.
|
||||
SANITIZER_GROUP("undefined", Undefined,
|
||||
Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow |
|
||||
FloatDivideByZero | Function | IntegerDivideByZero | Null |
|
||||
ObjectSize | Return | Shift | SignedIntegerOverflow |
|
||||
Unreachable | VLABound | Vptr)
|
||||
FloatDivideByZero | Function | IntegerDivideByZero | Null |
|
||||
ObjectSize | Return | ReturnsNonnullAttribute | Shift |
|
||||
SignedIntegerOverflow | Unreachable | VLABound | Vptr)
|
||||
|
||||
// -fsanitize=undefined-trap includes
|
||||
// all sanitizers included by -fsanitize=undefined, except those that require
|
||||
|
@ -88,9 +89,9 @@ SANITIZER_GROUP("undefined", Undefined,
|
|||
// -fsanitize-undefined-trap-on-error flag.
|
||||
SANITIZER_GROUP("undefined-trap", UndefinedTrap,
|
||||
Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow |
|
||||
FloatDivideByZero | IntegerDivideByZero | Null | ObjectSize |
|
||||
Return | Shift | SignedIntegerOverflow | Unreachable |
|
||||
VLABound)
|
||||
FloatDivideByZero | IntegerDivideByZero | Null |
|
||||
ObjectSize | Return | ReturnsNonnullAttribute | Shift |
|
||||
SignedIntegerOverflow | Unreachable | VLABound)
|
||||
|
||||
SANITIZER_GROUP("integer", Integer,
|
||||
SignedIntegerOverflow | UnsignedIntegerOverflow | Shift |
|
||||
|
|
|
@ -1998,7 +1998,24 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
|
|||
llvm_unreachable("Invalid ABI kind for return argument");
|
||||
}
|
||||
|
||||
llvm::Instruction *Ret = RV ? Builder.CreateRet(RV) : Builder.CreateRetVoid();
|
||||
llvm::Instruction *Ret;
|
||||
if (RV) {
|
||||
if (SanOpts->ReturnsNonnullAttribute &&
|
||||
CurGD.getDecl()->hasAttr<ReturnsNonNullAttr>()) {
|
||||
SanitizerScope SanScope(this);
|
||||
llvm::Value *Cond =
|
||||
Builder.CreateICmpNE(RV, llvm::Constant::getNullValue(RV->getType()));
|
||||
llvm::Constant *StaticData[] = {
|
||||
EmitCheckSourceLocation(EndLoc)
|
||||
};
|
||||
EmitCheck(Cond, "nonnull_return", StaticData, ArrayRef<llvm::Value *>(),
|
||||
CRK_Recoverable);
|
||||
}
|
||||
Ret = Builder.CreateRet(RV);
|
||||
} else {
|
||||
Ret = Builder.CreateRetVoid();
|
||||
}
|
||||
|
||||
if (!RetDbgLoc.isUnknown())
|
||||
Ret->setDebugLoc(RetDbgLoc);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
|
||||
// RUN: %clang_cc1 -fsanitize-undefined-trap-on-error -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-TRAP
|
||||
// RUN: %clang_cc1 -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
|
||||
// RUN: %clang_cc1 -fsanitize-undefined-trap-on-error -fsanitize=alignment,null,object-size,shift,return,signed-integer-overflow,vla-bound,float-cast-overflow,integer-divide-by-zero,bool,returns-nonnull-attribute -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-TRAP
|
||||
// RUN: %clang_cc1 -fsanitize=null -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-NULL
|
||||
// RUN: %clang_cc1 -fsanitize=signed-integer-overflow -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s --check-prefix=CHECK-OVERFLOW
|
||||
|
||||
|
@ -432,6 +432,20 @@ _Bool sour_bool(_Bool *p) {
|
|||
return *p;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @ret_nonnull
|
||||
__attribute__((returns_nonnull))
|
||||
int *ret_nonnull(int *a) {
|
||||
// CHECK: [[OK:%.*]] = icmp ne i32* {{.*}}, null
|
||||
// CHECK: br i1 [[OK]]
|
||||
// CHECK: call void @__ubsan_handle_nonnull_return
|
||||
|
||||
// CHECK-TRAP: [[OK:%.*]] = icmp ne i32* {{.*}}, null
|
||||
// CHECK-TRAP: br i1 [[OK]]
|
||||
// CHECK-TRAP: call void @llvm.trap() [[NR_NUW]]
|
||||
// CHECK-TRAP: unreachable
|
||||
return a;
|
||||
}
|
||||
|
||||
// CHECK: ![[WEIGHT_MD]] = metadata !{metadata !"branch_weights", i32 1048575, i32 1}
|
||||
|
||||
// CHECK-TRAP: attributes [[NR_NUW]] = { noreturn nounwind }
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize-undefined-trap-on-error -fsanitize=undefined-trap %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP
|
||||
// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool),?){14}"}}
|
||||
// CHECK-UNDEFINED-TRAP: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute),?){15}"}}
|
||||
// CHECK-UNDEFINED-TRAP: "-fsanitize-undefined-trap-on-error"
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
|
||||
// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool),?){16}"}}
|
||||
// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute),?){17}"}}
|
||||
|
||||
// RUN: %clang -target x86_64-apple-darwin10 -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-DARWIN
|
||||
// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool),?){15}"}}
|
||||
// CHECK-UNDEFINED-DARWIN: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|array-bounds|enum|bool|returns-nonnull-attribute),?){16}"}}
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=integer %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INTEGER
|
||||
// CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift),?){4}"}}
|
||||
|
@ -16,7 +16,7 @@
|
|||
// CHECK-BOUNDS: "-fsanitize={{((array-bounds|local-bounds),?){2}"}}
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-sanitize=thread -fno-sanitize=float-cast-overflow,vptr,bool,enum %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
|
||||
// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|object-size|array-bounds),?){12}"}}
|
||||
// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|object-size|array-bounds|returns-nonnull-attribute),?){13}"}}
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined -fsanitize-undefined-trap-on-error %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED-TRAP-ON-ERROR-UNDEF
|
||||
// CHECK-UNDEFINED-TRAP-ON-ERROR-UNDEF: '-fsanitize=undefined' not allowed with '-fsanitize-undefined-trap-on-error'
|
||||
|
|
|
@ -308,3 +308,22 @@ void __ubsan::__ubsan_handle_function_type_mismatch_abort(
|
|||
FunctionTypeMismatchData *Data, ValueHandle Function) {
|
||||
handleFunctionTypeMismatch(Data, Function, true);
|
||||
}
|
||||
|
||||
static void handleNonnullReturn(NonNullReturnData *Data, bool Abort) {
|
||||
SourceLocation Loc = Data->Loc.acquire();
|
||||
if (Loc.isDisabled())
|
||||
return;
|
||||
|
||||
ScopedReport R(Abort);
|
||||
|
||||
Diag(Loc, DL_Error, "null pointer returned from function declared to never "
|
||||
"return null");
|
||||
}
|
||||
|
||||
void __ubsan::__ubsan_handle_nonnull_return(NonNullReturnData *Data) {
|
||||
handleNonnullReturn(Data, false);
|
||||
}
|
||||
|
||||
void __ubsan::__ubsan_handle_nonnull_return_abort(NonNullReturnData *Data) {
|
||||
handleNonnullReturn(Data, true);
|
||||
}
|
||||
|
|
|
@ -121,6 +121,13 @@ RECOVERABLE(function_type_mismatch,
|
|||
FunctionTypeMismatchData *Data,
|
||||
ValueHandle Val)
|
||||
|
||||
struct NonNullReturnData {
|
||||
SourceLocation Loc;
|
||||
};
|
||||
|
||||
/// \brief Handle returning null from function with returns_nonnull attribute.
|
||||
RECOVERABLE(nonnull_return, NonNullReturnData *Data)
|
||||
|
||||
}
|
||||
|
||||
#endif // UBSAN_HANDLERS_H
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// RUN: %clangxx -fsanitize=returns-nonnull-attribute %s -O3 -o %t
|
||||
// RUN: %run %t foo
|
||||
// RUN: %run %t 2>&1 | FileCheck %s
|
||||
|
||||
__attribute__((returns_nonnull))
|
||||
char *foo(char *a) {
|
||||
return a;
|
||||
// CHECK: nonnull.cpp:[[@LINE+1]]:1: runtime error: null pointer returned from function declared to never return null
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
return foo(argv[1]) == 0;
|
||||
}
|
Loading…
Reference in New Issue