Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a dynamic check for pointer arithmetic of null pointers #663

Merged
merged 4 commits into from
Aug 20, 2019
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions include/clang/Basic/CodeGenOptions.def
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@ CODEGENOPT(BranchTargetEnforcement, 1, 0)
/// Whether to emit unused static constants.
CODEGENOPT(KeepStaticConsts, 1, 0)

/// Whether to add null pointer checks for Checked C.
CODEGENOPT(CheckedCNullPtrChecks, 1, 1)

#undef CODEGENOPT
#undef ENUM_CODEGENOPT
#undef VALUE_CODEGENOPT
Expand Down
8 changes: 8 additions & 0 deletions include/clang/Driver/Options.td
Original file line number Diff line number Diff line change
Expand Up @@ -2621,6 +2621,14 @@ def fintegrated_as : Flag<["-"], "fintegrated-as">, Flags<[DriverOption]>,
def fno_integrated_as : Flag<["-"], "fno-integrated-as">,
Flags<[CC1Option, DriverOption]>, Group<f_Group>,
HelpText<"Disable the integrated assembler">;

def fcheckedc_null_ptr_checks : Flag<["-"], "fcheckedc-null-ptr-checks">,
Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Enable runtime null pointer checks">;
def fno_checkedc_null_ptr_checks : Flag<["-"], "fno-checkedc-null-ptr-checks">,
Group<f_Group>, Flags<[CC1Option]>,
HelpText<"Disable runtime null pointer checks">;

def : Flag<["-"], "integrated-as">, Alias<fintegrated_as>, Flags<[DriverOption]>;
def : Flag<["-"], "no-integrated-as">, Alias<fno_integrated_as>,
Flags<[CC1Option, DriverOption]>;
Expand Down
34 changes: 28 additions & 6 deletions lib/CodeGen/CGDynamicCheck.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,43 @@ void CodeGenFunction::EmitExplicitDynamicCheck(const Expr *Condition) {
// General Functions for inserting dynamic checks
//

void CodeGenFunction::EmitDynamicNonNullCheck(const Address BaseAddr,
const QualType BaseTy) {
if (!getLangOpts().CheckedC)
return;
static bool shouldEmitNonNullCheck(const CodeGenModule &CGM,
const QualType BaseTy) {
if (!CGM.getLangOpts().CheckedC)
return false;

if (!CGM.getCodeGenOpts().CheckedCNullPtrChecks)
return false;

if (!(BaseTy->isCheckedPointerType() || BaseTy->isCheckedArrayType()))
return false;

return true;
}

void CodeGenFunction::EmitDynamicNonNullCheck(const Address BaseAddr,
const QualType BaseTy) {
if (!shouldEmitNonNullCheck(CGM, BaseTy))
return;

++NumDynamicChecksNonNull;
++NumDynamicChecksNonNull;

Value *ConditionVal =
Value *ConditionVal =
Builder.CreateIsNotNull(BaseAddr.getPointer(), "_Dynamic_check.non_null");
EmitDynamicCheckBlocks(ConditionVal);
}

void CodeGenFunction::EmitDynamicNonNullCheck(Value *Val,
const QualType BaseTy) {
if (!shouldEmitNonNullCheck(CGM, BaseTy))
return;

++NumDynamicChecksNonNull;

Value *ConditionVal = Builder.CreateIsNotNull(Val, "_Dynamic_check.non_null");
EmitDynamicCheckBlocks(ConditionVal); EmitDynamicCheckBlocks(ConditionVal);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like EmitDynamicCheckBlocks is being called twice here.

}

// TODO: This is currently unused. It may never be used.
void CodeGenFunction::EmitDynamicOverflowCheck(const Address BaseAddr,
const QualType BaseTy,
Expand Down
11 changes: 11 additions & 0 deletions lib/CodeGen/CGExprScalar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2319,6 +2319,11 @@ static BinOpInfo createBinOpInfoFromIncDec(const UnaryOperator *E,
return BinOp;
}

static void emitDynamicNonNullCheck(CodeGenFunction &CGF,
Value *Val, QualType Ty) {
CGF.EmitDynamicNonNullCheck(Val, Ty);
}

llvm::Value *ScalarExprEmitter::EmitIncDecConsiderOverflowBehavior(
const UnaryOperator *E, llvm::Value *InVal, bool IsInc) {
llvm::Value *Amount =
Expand Down Expand Up @@ -2427,6 +2432,9 @@ ScalarExprEmitter::EmitScalarPrePostIncDec(const UnaryOperator *E, LValue LV,

// Next most common: pointer increment.
} else if (const PointerType *ptr = type->getAs<PointerType>()) {
// Insert a dynamic check for arithmetic on null checked pointers.
emitDynamicNonNullCheck(CGF, value, type);

QualType type = ptr->getPointeeType();

// VLA types don't have constant size.
Expand Down Expand Up @@ -3162,6 +3170,9 @@ static Value *emitPointerArithmetic(CodeGenFunction &CGF,
std::swap(pointerOperand, indexOperand);
}

// Insert a dynamic check for arithmetic on null checked pointers.
emitDynamicNonNullCheck(CGF, pointer, pointerOperand->getType());

bool isSigned = indexOperand->getType()->isSignedIntegerOrEnumerationType();

unsigned width = cast<llvm::IntegerType>(index->getType())->getBitWidth();
Expand Down
1 change: 1 addition & 0 deletions lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -2923,6 +2923,7 @@ class CodeGenFunction : public CodeGenTypeCache {

void EmitExplicitDynamicCheck(const Expr *Condition);
void EmitDynamicNonNullCheck(const Address BaseAddr, const QualType BaseTy);
void EmitDynamicNonNullCheck(llvm::Value *Val, const QualType BaseTy);
void EmitDynamicOverflowCheck(const Address BaseAddr, const QualType BaseTy,
const Address PtrAddr);
/// \brief Emit a dynamic bounds check.
Expand Down
3 changes: 3 additions & 0 deletions lib/Driver/ToolChains/Clang.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4721,6 +4721,9 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
Args.AddLastArg(CmdArgs, options::OPT_fno_checkedc_extension);
Args.AddLastArg(CmdArgs, options::OPT_fdump_inferred_bounds);

Args.AddLastArg(CmdArgs, options::OPT_fcheckedc_null_ptr_checks,
options::OPT_fno_checkedc_null_ptr_checks);

// -fno-declspec is default, except for PS4.
if (Args.hasFlag(options::OPT_fdeclspec, options::OPT_fno_declspec,
RawTriple.isPS4()))
Expand Down
2 changes: 2 additions & 0 deletions lib/Frontend/CompilerInvocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1322,6 +1322,8 @@ static bool ParseCodeGenArgs(CodeGenOptions &Opts, ArgList &Args, InputKind IK,

Opts.DefaultFunctionAttrs = Args.getAllArgValues(OPT_default_function_attr);

Opts.CheckedCNullPtrChecks = !Args.hasArg(OPT_fno_checkedc_null_ptr_checks);

return Success;
}

Expand Down
112 changes: 112 additions & 0 deletions test/CheckedC/dynamic-checks/ptr-arithmetic-null-checks-code-gen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
// RUN: %clang_cc1 %s -emit-llvm -o - | FileCheck %s
// RUN: %clang_cc1 %s -emit-llvm -fno-checkedc-null-ptr-checks -o - | FileCheck %s --check-prefix NOCHECK

#include <stdchecked.h>
#include <stddef.h>

array_ptr<char> p : count(3) = "abc";
array_ptr<char> q : count(3) = "abc";
// NOCHECK-NOT: _Dynamic_check.non_null

void f1() {
// CHECK-LABEL: f1
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG0]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
p++;
}

void f2() {
// CHECK-LABEL: f2
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG0]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
++p;
}

void f3() {
// CHECK-LABEL: f3
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG0]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
p--;
}

void f4() {
// CHECK-LABEL: f4
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG0]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
--p;
}

void f5() {
// CHECK-LABEL: f5
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG0]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
p += 5;
}

void f6() {
// CHECK-LABEL: f6
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG0]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
p -= 10;
}

void f7() {
// CHECK-LABEL: f7
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG0]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
p = 2 + p;
}

void f8() {
// CHECK-LABEL: f8
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG0]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
p = p - 100;
}

void f9() {
// CHECK-LABEL: f9
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG0]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
p[1]++;
}

void f10() {
// CHECK-LABEL: f10
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG0]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]

// CHECK: [[REG1:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK: [[REG2:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG2]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
p[1] = p[2] + (*p)++;
}

void f11(array_ptr<char> a);
void f12() {
// CHECK-LABEL: f12
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG0]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
f11(++p);
}

void f13() {
// CHECK-LABEL: f13
// CHECK: [[REG0:%[a-zA-Z0-9.]*]] = load i8*, i8** @p, align 8
// CHECK: [[REG1:%[a-zA-Z0-9.]*]] = load i8*, i8** @q, align 8
// CHECK-NEXT: [[REG_DYNN:%_Dynamic_check.non_null[a-zA-Z0-9.]*]] = icmp ne i8* [[REG1]], null
// CHECK-NEXT: br i1 [[REG_DYNN]], label %[[LAB_DYSUC:_Dynamic_check.succeeded[a-zA-Z0-9.]*]], label %[[LAB_DYFAIL:_Dynamic_check.failed[a-zA-Z0-9.]*]]
p[q[1]++]++;
}