diff options
Diffstat (limited to 'lib/StaticAnalyzer/Checkers/ValistChecker.cpp')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/ValistChecker.cpp | 85 |
1 files changed, 61 insertions, 24 deletions
diff --git a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp index 0b7a4865ddc2..d12ba6258073 100644 --- a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -54,11 +54,11 @@ public: void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; private: - const MemRegion *getVAListAsRegion(SVal SV, CheckerContext &C) const; + const MemRegion *getVAListAsRegion(SVal SV, const Expr *VAExpr, + bool &IsSymbolic, CheckerContext &C) const; StringRef getVariableNameFromRegion(const MemRegion *Reg) const; const ExplodedNode *getStartCallSite(const ExplodedNode *N, - const MemRegion *Reg, - CheckerContext &C) const; + const MemRegion *Reg) const; void reportUninitializedAccess(const MemRegion *VAList, StringRef Msg, CheckerContext &C) const; @@ -138,14 +138,21 @@ void ValistChecker::checkPreCall(const CallEvent &Call, for (auto FuncInfo : VAListAccepters) { if (!Call.isCalled(FuncInfo.Func)) continue; + bool Symbolic; const MemRegion *VAList = - getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos), C); + getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos), + Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C); if (!VAList) return; if (C.getState()->contains<InitializedVALists>(VAList)) return; + // We did not see va_start call, but the source of the region is unknown. + // Be conservative and assume the best. + if (Symbolic) + return; + SmallString<80> Errmsg("Function '"); Errmsg += FuncInfo.Func.getFunctionName(); Errmsg += "' is called with an uninitialized va_list argument"; @@ -155,13 +162,41 @@ void ValistChecker::checkPreCall(const CallEvent &Call, } } +const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E, + bool &IsSymbolic, + CheckerContext &C) const { + const MemRegion *Reg = SV.getAsRegion(); + if (!Reg) + return nullptr; + // TODO: In the future this should be abstracted away by the analyzer. + bool VaListModelledAsArray = false; + if (const auto *Cast = dyn_cast<CastExpr>(E)) { + QualType Ty = Cast->getType(); + VaListModelledAsArray = + Ty->isPointerType() && Ty->getPointeeType()->isRecordType(); + } + if (const auto *DeclReg = Reg->getAs<DeclRegion>()) { + if (isa<ParmVarDecl>(DeclReg->getDecl())) + Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion(); + } + IsSymbolic = Reg && Reg->getAs<SymbolicRegion>(); + // Some VarRegion based VA lists reach here as ElementRegions. + const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg); + return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg; +} + void ValistChecker::checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const { ProgramStateRef State = C.getState(); - SVal VAListSVal = State->getSVal(VAA->getSubExpr(), C.getLocationContext()); - const MemRegion *VAList = getVAListAsRegion(VAListSVal, C); + const Expr *VASubExpr = VAA->getSubExpr(); + SVal VAListSVal = State->getSVal(VASubExpr, C.getLocationContext()); + bool Symbolic; + const MemRegion *VAList = + getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C); if (!VAList) return; + if (Symbolic) + return; if (!State->contains<InitializedVALists>(VAList)) reportUninitializedAccess( VAList, "va_arg() is called on an uninitialized va_list", C); @@ -183,22 +218,13 @@ void ValistChecker::checkDeadSymbols(SymbolReaper &SR, N); } -const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, - CheckerContext &C) const { - const MemRegion *Reg = SV.getAsRegion(); - const auto *TReg = dyn_cast_or_null<TypedValueRegion>(Reg); - // Some VarRegion based VLAs reach here as ElementRegions. - const auto *EReg = dyn_cast_or_null<ElementRegion>(TReg); - return EReg ? EReg->getSuperRegion() : TReg; -} - // This function traverses the exploded graph backwards and finds the node where // the va_list is initialized. That node is used for uniquing the bug paths. // It is not likely that there are several different va_lists that belongs to // different stack frames, so that case is not yet handled. -const ExplodedNode *ValistChecker::getStartCallSite(const ExplodedNode *N, - const MemRegion *Reg, - CheckerContext &C) const { +const ExplodedNode * +ValistChecker::getStartCallSite(const ExplodedNode *N, + const MemRegion *Reg) const { const LocationContext *LeakContext = N->getLocationContext(); const ExplodedNode *StartCallNode = N; @@ -252,7 +278,7 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, BT_leakedvalist->setSuppressOnSink(true); } - const ExplodedNode *StartNode = getStartCallSite(N, Reg, C); + const ExplodedNode *StartNode = getStartCallSite(N, Reg); PathDiagnosticLocation LocUsedForUniqueing; if (const Stmt *StartCallStmt = PathDiagnosticLocation::getStmt(StartNode)) @@ -278,13 +304,17 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, void ValistChecker::checkVAListStartCall(const CallEvent &Call, CheckerContext &C, bool IsCopy) const { - const MemRegion *VAList = getVAListAsRegion(Call.getArgSVal(0), C); - ProgramStateRef State = C.getState(); + bool Symbolic; + const MemRegion *VAList = + getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C); if (!VAList) return; + ProgramStateRef State = C.getState(); + if (IsCopy) { - const MemRegion *Arg2 = getVAListAsRegion(Call.getArgSVal(1), C); + const MemRegion *Arg2 = + getVAListAsRegion(Call.getArgSVal(1), Call.getArgExpr(1), Symbolic, C); if (Arg2) { if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) { RegionVector LeakedVALists{VAList}; @@ -292,7 +322,7 @@ void ValistChecker::checkVAListStartCall(const CallEvent &Call, reportLeakedVALists(LeakedVALists, "va_list", " is copied onto itself", C, N, true); return; - } else if (!State->contains<InitializedVALists>(Arg2)) { + } else if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) { if (State->contains<InitializedVALists>(VAList)) { State = State->remove<InitializedVALists>(VAList); RegionVector LeakedVALists{VAList}; @@ -321,10 +351,17 @@ void ValistChecker::checkVAListStartCall(const CallEvent &Call, void ValistChecker::checkVAListEndCall(const CallEvent &Call, CheckerContext &C) const { - const MemRegion *VAList = getVAListAsRegion(Call.getArgSVal(0), C); + bool Symbolic; + const MemRegion *VAList = + getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C); if (!VAList) return; + // We did not see va_start call, but the source of the region is unknown. + // Be conservative and assume the best. + if (Symbolic) + return; + if (!C.getState()->contains<InitializedVALists>(VAList)) { reportUninitializedAccess( VAList, "va_end() is called on an uninitialized va_list", C); |