[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:
Alexey Samsonov 2014-08-13 00:26:40 +00:00
parent cfe8fc3e28
commit de443c5002
8 changed files with 86 additions and 13 deletions

View File

@ -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

View File

@ -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 |

View File

@ -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);
}

View File

@ -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 }

View File

@ -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'

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}