summaryrefslogtreecommitdiff
path: root/lib/StaticAnalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp102
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp27
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp4
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp3
-rw-r--r--lib/StaticAnalyzer/Core/Store.cpp12
5 files changed, 111 insertions, 37 deletions
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index 6e9b7fefa3d06..5730517289bb0 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -177,7 +177,10 @@ public:
II_wcsdup(nullptr), II_win_wcsdup(nullptr), II_g_malloc(nullptr),
II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr),
II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr),
- II_g_free(nullptr), II_g_memdup(nullptr) {}
+ II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr),
+ II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr),
+ II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr),
+ II_g_try_realloc_n(nullptr) {}
/// In pessimistic mode, the checker assumes that it does not know which
/// functions might free the memory.
@@ -241,7 +244,10 @@ private:
*II_if_nameindex, *II_if_freenameindex, *II_wcsdup,
*II_win_wcsdup, *II_g_malloc, *II_g_malloc0,
*II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0,
- *II_g_try_realloc, *II_g_free, *II_g_memdup;
+ *II_g_try_realloc, *II_g_free, *II_g_memdup,
+ *II_g_malloc_n, *II_g_malloc0_n, *II_g_realloc_n,
+ *II_g_try_malloc_n, *II_g_try_malloc0_n,
+ *II_g_try_realloc_n;
mutable Optional<uint64_t> KernelZeroFlagVal;
void initIdentifierInfo(ASTContext &C) const;
@@ -321,9 +327,12 @@ private:
bool &ReleasedAllocated,
bool ReturnsNullOnFailure = false) const;
- ProgramStateRef ReallocMem(CheckerContext &C, const CallExpr *CE,
- bool FreesMemOnFailure,
- ProgramStateRef State) const;
+ ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE,
+ bool FreesMemOnFailure,
+ ProgramStateRef State,
+ bool SuffixWithN = false) const;
+ static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
+ const Expr *BlockBytes);
static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
ProgramStateRef State);
@@ -569,6 +578,12 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
II_g_try_realloc = &Ctx.Idents.get("g_try_realloc");
II_g_free = &Ctx.Idents.get("g_free");
II_g_memdup = &Ctx.Idents.get("g_memdup");
+ II_g_malloc_n = &Ctx.Idents.get("g_malloc_n");
+ II_g_malloc0_n = &Ctx.Idents.get("g_malloc0_n");
+ II_g_realloc_n = &Ctx.Idents.get("g_realloc_n");
+ II_g_try_malloc_n = &Ctx.Idents.get("g_try_malloc_n");
+ II_g_try_malloc0_n = &Ctx.Idents.get("g_try_malloc0_n");
+ II_g_try_realloc_n = &Ctx.Idents.get("g_try_realloc_n");
}
bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
@@ -617,7 +632,10 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
FunI == II_g_malloc || FunI == II_g_malloc0 ||
FunI == II_g_realloc || FunI == II_g_try_malloc ||
FunI == II_g_try_malloc0 || FunI == II_g_try_realloc ||
- FunI == II_g_memdup)
+ FunI == II_g_memdup || FunI == II_g_malloc_n ||
+ FunI == II_g_malloc0_n || FunI == II_g_realloc_n ||
+ FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n ||
+ FunI == II_g_try_realloc_n)
return true;
}
@@ -767,6 +785,17 @@ llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
return None;
}
+SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
+ const Expr *BlockBytes) {
+ SValBuilder &SB = C.getSValBuilder();
+ SVal BlocksVal = C.getSVal(Blocks);
+ SVal BlockBytesVal = C.getSVal(BlockBytes);
+ ProgramStateRef State = C.getState();
+ SVal TotalSize = SB.evalBinOp(State, BO_Mul, BlocksVal, BlockBytesVal,
+ SB.getContext().getSizeType());
+ return TotalSize;
+}
+
void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
if (C.wasInlined)
return;
@@ -813,10 +842,10 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
State = ProcessZeroAllocation(C, CE, 0, State);
} else if (FunI == II_realloc || FunI == II_g_realloc ||
FunI == II_g_try_realloc) {
- State = ReallocMem(C, CE, false, State);
+ State = ReallocMemAux(C, CE, false, State);
State = ProcessZeroAllocation(C, CE, 1, State);
} else if (FunI == II_reallocf) {
- State = ReallocMem(C, CE, true, State);
+ State = ReallocMemAux(C, CE, true, State);
State = ProcessZeroAllocation(C, CE, 1, State);
} else if (FunI == II_calloc) {
State = CallocMem(C, CE, State);
@@ -874,6 +903,25 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
return;
State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State);
State = ProcessZeroAllocation(C, CE, 1, State);
+ } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n ||
+ FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) {
+ if (CE->getNumArgs() < 2)
+ return;
+ SVal Init = UndefinedVal();
+ if (FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) {
+ SValBuilder &SB = C.getSValBuilder();
+ Init = SB.makeZeroVal(SB.getContext().CharTy);
+ }
+ SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
+ State = MallocMemAux(C, CE, TotalSize, Init, State);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
+ } else if (FunI == II_g_realloc_n || FunI == II_g_try_realloc_n) {
+ if (CE->getNumArgs() < 3)
+ return;
+ State = ReallocMemAux(C, CE, false, State, true);
+ State = ProcessZeroAllocation(C, CE, 1, State);
+ State = ProcessZeroAllocation(C, CE, 2, State);
}
}
@@ -1976,14 +2024,17 @@ void MallocChecker::ReportUseZeroAllocated(CheckerContext &C,
}
}
-ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
- const CallExpr *CE,
- bool FreesOnFail,
- ProgramStateRef State) const {
+ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
+ const CallExpr *CE,
+ bool FreesOnFail,
+ ProgramStateRef State,
+ bool SuffixWithN) const {
if (!State)
return nullptr;
- if (CE->getNumArgs() < 2)
+ if (SuffixWithN && CE->getNumArgs() < 3)
+ return nullptr;
+ else if (CE->getNumArgs() < 2)
return nullptr;
const Expr *arg0Expr = CE->getArg(0);
@@ -1998,20 +2049,19 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
DefinedOrUnknownSVal PtrEQ =
svalBuilder.evalEQ(State, arg0Val, svalBuilder.makeNull());
- // Get the size argument. If there is no size arg then give up.
+ // Get the size argument.
const Expr *Arg1 = CE->getArg(1);
- if (!Arg1)
- return nullptr;
// Get the value of the size argument.
- SVal Arg1ValG = State->getSVal(Arg1, LCtx);
- if (!Arg1ValG.getAs<DefinedOrUnknownSVal>())
+ SVal TotalSize = State->getSVal(Arg1, LCtx);
+ if (SuffixWithN)
+ TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2));
+ if (!TotalSize.getAs<DefinedOrUnknownSVal>())
return nullptr;
- DefinedOrUnknownSVal Arg1Val = Arg1ValG.castAs<DefinedOrUnknownSVal>();
// Compare the size argument to 0.
DefinedOrUnknownSVal SizeZero =
- svalBuilder.evalEQ(State, Arg1Val,
+ svalBuilder.evalEQ(State, TotalSize.castAs<DefinedOrUnknownSVal>(),
svalBuilder.makeIntValWithPtrWidth(0, false));
ProgramStateRef StatePtrIsNull, StatePtrNotNull;
@@ -2025,8 +2075,8 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
// If the ptr is NULL and the size is not 0, the call is equivalent to
// malloc(size).
- if ( PrtIsNull && !SizeIsZero) {
- ProgramStateRef stateMalloc = MallocMemAux(C, CE, CE->getArg(1),
+ if (PrtIsNull && !SizeIsZero) {
+ ProgramStateRef stateMalloc = MallocMemAux(C, CE, TotalSize,
UndefinedVal(), StatePtrIsNull);
return stateMalloc;
}
@@ -2059,7 +2109,7 @@ ProgramStateRef MallocChecker::ReallocMem(CheckerContext &C,
if (ProgramStateRef stateFree =
FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) {
- ProgramStateRef stateRealloc = MallocMemAux(C, CE, CE->getArg(1),
+ ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize,
UnknownVal(), stateFree);
if (!stateRealloc)
return nullptr;
@@ -2090,12 +2140,8 @@ ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE,
return nullptr;
SValBuilder &svalBuilder = C.getSValBuilder();
- const LocationContext *LCtx = C.getLocationContext();
- SVal count = State->getSVal(CE->getArg(0), LCtx);
- SVal elementSize = State->getSVal(CE->getArg(1), LCtx);
- SVal TotalSize = svalBuilder.evalBinOp(State, BO_Mul, count, elementSize,
- svalBuilder.getContext().getSizeType());
SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
+ SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
return MallocMemAux(C, CE, TotalSize, zeroVal, State);
}
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index 7309741d22e39..d00182a871c1e 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -61,7 +61,9 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) {
return U->getSubExpr()->IgnoreParenCasts();
}
else if (const MemberExpr *ME = dyn_cast<MemberExpr>(E)) {
- if (ME->isArrow() || isDeclRefExprToReference(ME->getBase())) {
+ if (ME->isImplicitAccess()) {
+ return ME;
+ } else if (ME->isArrow() || isDeclRefExprToReference(ME->getBase())) {
return ME->getBase()->IgnoreParenCasts();
} else {
// If we have a member expr with a dot, the base must have been
@@ -73,9 +75,9 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) {
return IvarRef->getBase()->IgnoreParenCasts();
}
else if (const ArraySubscriptExpr *AE = dyn_cast<ArraySubscriptExpr>(E)) {
- return AE->getBase();
+ return getDerefExpr(AE->getBase());
}
- else if (isDeclRefExprToReference(E)) {
+ else if (isa<DeclRefExpr>(E)) {
return E;
}
break;
@@ -961,7 +963,24 @@ bool bugreporter::trackNullOrUndefValue(const ExplodedNode *N,
const Expr *Inner = nullptr;
if (const Expr *Ex = dyn_cast<Expr>(S)) {
Ex = Ex->IgnoreParenCasts();
- if (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex))
+
+ // Performing operator `&' on an lvalue expression is essentially a no-op.
+ // Then, if we are taking addresses of fields or elements, these are also
+ // unlikely to matter.
+ // FIXME: There's a hack in our Store implementation that always computes
+ // field offsets around null pointers as if they are always equal to 0.
+ // The idea here is to report accesses to fields as null dereferences
+ // even though the pointer value that's being dereferenced is actually
+ // the offset of the field rather than exactly 0.
+ // See the FIXME in StoreManager's getLValueFieldOrIvar() method.
+ // This code interacts heavily with this hack; otherwise the value
+ // would not be null at all for most fields, so we'd be unable to track it.
+ if (const auto *Op = dyn_cast<UnaryOperator>(Ex))
+ if (Op->getOpcode() == UO_AddrOf && Op->getSubExpr()->isLValue())
+ if (const Expr *DerefEx = getDerefExpr(Op->getSubExpr()))
+ Ex = DerefEx;
+
+ if (Ex && (ExplodedGraph::isInterestingLValueExpr(Ex) || CallEvent::isCallStmt(Ex)))
Inner = Ex;
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 9e6ec09010e91..8ee34190891ad 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1904,8 +1904,8 @@ void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
// Evaluate the LHS of the case value.
llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext());
- assert(V1.getBitWidth() == getContext().getTypeSize(CondE->getType()));
-
+ assert(V1.getBitWidth() == getContext().getIntWidth(CondE->getType()));
+
// Get the RHS of the case, if it exists.
llvm::APSInt V2;
if (const Expr *E = Case->getRHS())
diff --git a/lib/StaticAnalyzer/Core/RegionStore.cpp b/lib/StaticAnalyzer/Core/RegionStore.cpp
index dd7e9dd117815..3000e13d32c6f 100644
--- a/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -1338,6 +1338,9 @@ RegionStoreManager::getSizeInElements(ProgramStateRef state,
/// the array). This is called by ExprEngine when evaluating casts
/// from arrays to pointers.
SVal RegionStoreManager::ArrayToPointer(Loc Array, QualType T) {
+ if (Array.getAs<loc::ConcreteInt>())
+ return Array;
+
if (!Array.getAs<loc::MemRegionVal>())
return UnknownVal();
diff --git a/lib/StaticAnalyzer/Core/Store.cpp b/lib/StaticAnalyzer/Core/Store.cpp
index ba48a60d5a1cc..1af49f68cc055 100644
--- a/lib/StaticAnalyzer/Core/Store.cpp
+++ b/lib/StaticAnalyzer/Core/Store.cpp
@@ -404,9 +404,15 @@ SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) {
case loc::ConcreteIntKind:
// While these seem funny, this can happen through casts.
- // FIXME: What we should return is the field offset. For example,
- // add the field offset to the integer value. That way funny things
+ // FIXME: What we should return is the field offset, not base. For example,
+ // add the field offset to the integer value. That way things
// like this work properly: &(((struct foo *) 0xa)->f)
+ // However, that's not easy to fix without reducing our abilities
+ // to catch null pointer dereference. Eg., ((struct foo *)0x0)->f = 7
+ // is a null dereference even though we're dereferencing offset of f
+ // rather than null. Coming up with an approach that computes offsets
+ // over null pointers properly while still being able to catch null
+ // dereferences might be worth it.
return Base;
default:
@@ -431,7 +437,7 @@ SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
// If the base is an unknown or undefined value, just return it back.
// FIXME: For absolute pointer addresses, we just return that value back as
// well, although in reality we should return the offset added to that
- // value.
+ // value. See also the similar FIXME in getLValueFieldOrIvar().
if (Base.isUnknownOrUndef() || Base.getAs<loc::ConcreteInt>())
return Base;