diff options
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r-- | lib/StaticAnalyzer/Checkers/CastValueChecker.cpp | 105 |
1 files changed, 100 insertions, 5 deletions
diff --git a/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp index 1580f7f31c..5c4daa3e42 100644 --- a/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp @@ -16,6 +16,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/DeclTemplate.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -30,7 +31,7 @@ using namespace ento; namespace { class CastValueChecker : public Checker<eval::Call> { - enum class CallKind { Function, Method }; + enum class CallKind { Function, Method, InstanceOf }; using CastCheck = std::function<void(const CastValueChecker *, const CallEvent &Call, @@ -45,6 +46,10 @@ public: // // 4) castAs: Has no parameter, the return value is non-null. // 5) getAs: Has no parameter, the return value is null or non-null. + // + // We have two cases to check the parameter is an instance of the given type. + // 1) isa: The parameter is non-null, returns boolean. + // 2) isa_and_nonnull: The parameter is null or non-null, returns boolean. bool evalCall(const CallEvent &Call, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; @@ -63,7 +68,11 @@ private: {{{"clang", "castAs"}, 0}, {&CastValueChecker::evalCastAs, CallKind::Method}}, {{{"clang", "getAs"}, 0}, - {&CastValueChecker::evalGetAs, CallKind::Method}}}; + {&CastValueChecker::evalGetAs, CallKind::Method}}, + {{{"llvm", "isa"}, 1}, + {&CastValueChecker::evalIsa, CallKind::InstanceOf}}, + {{{"llvm", "isa_and_nonnull"}, 1}, + {&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}}; void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C) const; @@ -77,6 +86,10 @@ private: CheckerContext &C) const; void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, CheckerContext &C) const; + void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV, + CheckerContext &C) const; + void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV, + CheckerContext &C) const; }; } // namespace @@ -189,6 +202,42 @@ static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV, getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast)); } +static void addInstanceOfTransition(const CallEvent &Call, + DefinedOrUnknownSVal DV, + ProgramStateRef State, CheckerContext &C, + bool IsInstanceOf) { + const FunctionDecl *FD = Call.getDecl()->getAsFunction(); + QualType CastToTy = FD->getTemplateSpecializationArgs()->get(0).getAsType(); + QualType CastFromTy = getRecordType(Call.parameters()[0]->getType()); + + const MemRegion *MR = DV.getAsRegion(); + const DynamicCastInfo *CastInfo = + getDynamicCastInfo(State, MR, CastFromTy, CastToTy); + + bool CastSucceeds; + if (CastInfo) + CastSucceeds = IsInstanceOf && CastInfo->succeeds(); + else + CastSucceeds = IsInstanceOf || CastFromTy == CastToTy; + + if (isInfeasibleCast(CastInfo, CastSucceeds)) { + C.generateSink(State, C.getPredecessor()); + return; + } + + // Store the type and the cast information. + bool IsKnownCast = CastInfo || CastFromTy == CastToTy; + if (!IsKnownCast) + State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy, + Call.getResultType(), IsInstanceOf); + + C.addTransition( + State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeTruthVal(CastSucceeds)), + getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), CastSucceeds, + IsKnownCast)); +} + //===----------------------------------------------------------------------===// // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null. //===----------------------------------------------------------------------===// @@ -278,6 +327,41 @@ void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, } //===----------------------------------------------------------------------===// +// Evaluating isa, isa_and_nonnull. +//===----------------------------------------------------------------------===// + +void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV, + CheckerContext &C) const { + ProgramStateRef NonNullState, NullState; + std::tie(NonNullState, NullState) = C.getState()->assume(DV); + + if (NonNullState) { + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); + } + + if (NullState) { + C.generateSink(NullState, C.getPredecessor()); + } +} + +void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call, + DefinedOrUnknownSVal DV, + CheckerContext &C) const { + ProgramStateRef NonNullState, NullState; + std::tie(NonNullState, NullState) = C.getState()->assume(DV); + + if (NonNullState) { + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); + } + + if (NullState) { + addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false); + } +} + +//===----------------------------------------------------------------------===// // Main logic to evaluate a call. //===----------------------------------------------------------------------===// @@ -287,12 +371,14 @@ bool CastValueChecker::evalCall(const CallEvent &Call, if (!Lookup) return false; + const CastCheck &Check = Lookup->first; + CallKind Kind = Lookup->second; + // We need to obtain the record type of the call's result to model it. - if (!getRecordType(Call.getResultType())->isRecordType()) + if (Kind != CallKind::InstanceOf && + !getRecordType(Call.getResultType())->isRecordType()) return false; - const CastCheck &Check = Lookup->first; - CallKind Kind = Lookup->second; Optional<DefinedOrUnknownSVal> DV; switch (Kind) { @@ -304,6 +390,15 @@ bool CastValueChecker::evalCall(const CallEvent &Call, DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); break; } + case CallKind::InstanceOf: { + // We need to obtain the only template argument to determinte the type. + const FunctionDecl *FD = Call.getDecl()->getAsFunction(); + if (!FD || !FD->getTemplateSpecializationArgs()) + return false; + + DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>(); + break; + } case CallKind::Method: const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call); if (!InstanceCall) |