Skip to content

Commit

Permalink
[ubsan] Add a check for pointer overflow UB
Browse files Browse the repository at this point in the history
Check pointer arithmetic for overflow.

For some more background on this check, see:

  https://wdtz.org/catching-pointer-overflow-bugs.html
  https://reviews.llvm.org/D20322

Patch by Will Dietz and John Regehr!

This version of the patch is different from the original in a few ways:

  - It introduces the EmitCheckedInBoundsGEP utility which inserts
    checks when the pointer overflow check is enabled.

  - It does some constant-folding to reduce instrumentation overhead.

  - It does not check some GEPs in CGExprCXX. I'm not sure that
    inserting checks here, or in CGClass, would catch many bugs.

Possible future directions for this check:

  - Introduce CGF.EmitCheckedStructGEP, to detect overflows when
    accessing structures.

Testing: Apart from the added lit test, I ran check-llvm and check-clang
with a stage2, ubsan-instrumented clang. Will and John have also done
extensive testing on numerous open source projects.

Differential Revision: https://reviews.llvm.org/D33305

llvm-svn: 304459
  • Loading branch information
vedantk committed Jun 1, 2017
1 parent 532a9e8 commit a125eb5
Show file tree
Hide file tree
Showing 7 changed files with 391 additions and 58 deletions.
2 changes: 2 additions & 0 deletions clang/docs/UndefinedBehaviorSanitizer.rst
Expand Up @@ -106,6 +106,8 @@ Available checks are:
invalid pointers. These checks are made in terms of
``__builtin_object_size``, and consequently may be able to detect more
problems at higher optimization levels.
- ``-fsanitize=pointer-overflow``: Performing pointer arithmetic which
overflows.
- ``-fsanitize=return``: In C++, reaching the end of a
value-returning function without returning a value.
- ``-fsanitize=returns-nonnull-attribute``: Returning null pointer
Expand Down
7 changes: 4 additions & 3 deletions clang/include/clang/Basic/Sanitizers.def
Expand Up @@ -73,6 +73,7 @@ SANITIZER("nullability-return", NullabilityReturn)
SANITIZER_GROUP("nullability", Nullability,
NullabilityArg | NullabilityAssign | NullabilityReturn)
SANITIZER("object-size", ObjectSize)
SANITIZER("pointer-overflow", PointerOverflow)
SANITIZER("return", Return)
SANITIZER("returns-nonnull-attribute", ReturnsNonnullAttribute)
SANITIZER("shift-base", ShiftBase)
Expand Down Expand Up @@ -108,9 +109,9 @@ SANITIZER("safe-stack", SafeStack)
SANITIZER_GROUP("undefined", Undefined,
Alignment | Bool | ArrayBounds | Enum | FloatCastOverflow |
FloatDivideByZero | IntegerDivideByZero | NonnullAttribute |
Null | ObjectSize | Return | ReturnsNonnullAttribute |
Shift | SignedIntegerOverflow | Unreachable | VLABound |
Function | Vptr)
Null | ObjectSize | PointerOverflow | Return |
ReturnsNonnullAttribute | Shift | SignedIntegerOverflow |
Unreachable | VLABound | Function | Vptr)

// -fsanitize=undefined-trap is an alias for -fsanitize=undefined.
SANITIZER_GROUP("undefined-trap", UndefinedTrap, Undefined)
Expand Down
33 changes: 21 additions & 12 deletions clang/lib/CodeGen/CGExpr.cpp
Expand Up @@ -3002,9 +3002,10 @@ static llvm::Value *emitArraySubscriptGEP(CodeGenFunction &CGF,
llvm::Value *ptr,
ArrayRef<llvm::Value*> indices,
bool inbounds,
SourceLocation loc,
const llvm::Twine &name = "arrayidx") {
if (inbounds) {
return CGF.Builder.CreateInBoundsGEP(ptr, indices, name);
return CGF.EmitCheckedInBoundsGEP(ptr, indices, loc, name);
} else {
return CGF.Builder.CreateGEP(ptr, indices, name);
}
Expand Down Expand Up @@ -3035,8 +3036,9 @@ static QualType getFixedSizeElementType(const ASTContext &ctx,
}

static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr,
ArrayRef<llvm::Value*> indices,
ArrayRef<llvm::Value *> indices,
QualType eltType, bool inbounds,
SourceLocation loc,
const llvm::Twine &name = "arrayidx") {
// All the indices except that last must be zero.
#ifndef NDEBUG
Expand All @@ -3057,7 +3059,7 @@ static Address emitArraySubscriptGEP(CodeGenFunction &CGF, Address addr,
getArrayElementAlign(addr.getAlignment(), indices.back(), eltSize);

llvm::Value *eltPtr =
emitArraySubscriptGEP(CGF, addr.getPointer(), indices, inbounds, name);
emitArraySubscriptGEP(CGF, addr.getPointer(), indices, inbounds, loc, name);
return Address(eltPtr, eltAlign);
}

Expand Down Expand Up @@ -3110,7 +3112,8 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
Address Addr = EmitExtVectorElementLValue(LV);

QualType EltType = LV.getType()->castAs<VectorType>()->getElementType();
Addr = emitArraySubscriptGEP(*this, Addr, Idx, EltType, /*inbounds*/ true);
Addr = emitArraySubscriptGEP(*this, Addr, Idx, EltType, /*inbounds*/ true,
E->getExprLoc());
return MakeAddrLValue(Addr, EltType, LV.getBaseInfo());
}

Expand Down Expand Up @@ -3138,7 +3141,8 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
}

Addr = emitArraySubscriptGEP(*this, Addr, Idx, vla->getElementType(),
!getLangOpts().isSignedOverflowDefined());
!getLangOpts().isSignedOverflowDefined(),
E->getExprLoc());

} else if (const ObjCObjectType *OIT = E->getType()->getAs<ObjCObjectType>()){
// Indexing over an interface, as in "NSString *P; P[4];"
Expand All @@ -3163,8 +3167,8 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
// Do the GEP.
CharUnits EltAlign =
getArrayElementAlign(Addr.getAlignment(), Idx, InterfaceSize);
llvm::Value *EltPtr =
emitArraySubscriptGEP(*this, Addr.getPointer(), ScaledIdx, false);
llvm::Value *EltPtr = emitArraySubscriptGEP(
*this, Addr.getPointer(), ScaledIdx, false, E->getExprLoc());
Addr = Address(EltPtr, EltAlign);

// Cast back.
Expand All @@ -3189,14 +3193,16 @@ LValue CodeGenFunction::EmitArraySubscriptExpr(const ArraySubscriptExpr *E,
Addr = emitArraySubscriptGEP(*this, ArrayLV.getAddress(),
{CGM.getSize(CharUnits::Zero()), Idx},
E->getType(),
!getLangOpts().isSignedOverflowDefined());
!getLangOpts().isSignedOverflowDefined(),
E->getExprLoc());
BaseInfo = ArrayLV.getBaseInfo();
} else {
// The base must be a pointer; emit it with an estimate of its alignment.
Addr = EmitPointerWithAlignment(E->getBase(), &BaseInfo);
auto *Idx = EmitIdxAfterBase(/*Promote*/true);
Addr = emitArraySubscriptGEP(*this, Addr, Idx, E->getType(),
!getLangOpts().isSignedOverflowDefined());
!getLangOpts().isSignedOverflowDefined(),
E->getExprLoc());
}

LValue LV = MakeAddrLValue(Addr, E->getType(), BaseInfo);
Expand Down Expand Up @@ -3368,7 +3374,8 @@ LValue CodeGenFunction::EmitOMPArraySectionExpr(const OMPArraySectionExpr *E,
else
Idx = Builder.CreateNSWMul(Idx, NumElements);
EltPtr = emitArraySubscriptGEP(*this, Base, Idx, VLA->getElementType(),
!getLangOpts().isSignedOverflowDefined());
!getLangOpts().isSignedOverflowDefined(),
E->getExprLoc());
} else if (const Expr *Array = isSimpleArrayDecayOperand(E->getBase())) {
// If this is A[i] where A is an array, the frontend will have decayed the
// base to be a ArrayToPointerDecay implicit cast. While correct, it is
Expand All @@ -3387,13 +3394,15 @@ LValue CodeGenFunction::EmitOMPArraySectionExpr(const OMPArraySectionExpr *E,
// Propagate the alignment from the array itself to the result.
EltPtr = emitArraySubscriptGEP(
*this, ArrayLV.getAddress(), {CGM.getSize(CharUnits::Zero()), Idx},
ResultExprTy, !getLangOpts().isSignedOverflowDefined());
ResultExprTy, !getLangOpts().isSignedOverflowDefined(),
E->getExprLoc());
BaseInfo = ArrayLV.getBaseInfo();
} else {
Address Base = emitOMPArraySectionBase(*this, E->getBase(), BaseInfo,
BaseTy, ResultExprTy, IsLowerBound);
EltPtr = emitArraySubscriptGEP(*this, Base, Idx, ResultExprTy,
!getLangOpts().isSignedOverflowDefined());
!getLangOpts().isSignedOverflowDefined(),
E->getExprLoc());
}

return MakeAddrLValue(EltPtr, ResultExprTy, BaseInfo);
Expand Down

0 comments on commit a125eb5

Please sign in to comment.