diff options
| author | Csaba Dabis <dabis.csaba98@gmail.com> | 2019-07-09 23:33:23 +0000 |
|---|---|---|
| committer | Csaba Dabis <dabis.csaba98@gmail.com> | 2019-07-09 23:33:23 +0000 |
| commit | 27cf6664437efd640bb6db5594bafcce68fa2854 (patch) | |
| tree | 6f6b345ad05324a6f921ca201db85e30e26a27c3 /clang/lib/StaticAnalyzer | |
| parent | abce8c457dd3de6b156756e547cc0eefb7653c79 (diff) | |
| download | bcm5719-llvm-27cf6664437efd640bb6db5594bafcce68fa2854.tar.gz bcm5719-llvm-27cf6664437efd640bb6db5594bafcce68fa2854.zip | |
[analyzer] CastValueChecker: Model casts
Summary:
It models the LLVM casts:
- `cast<>`
- `dyn_cast<>`
- `cast_or_null<>`
- `dyn_cast_or_null<>`
It has a very basic support without checking the `classof()` function.
Reviewed By: NoQ
Tags: #clang
Differential Revision: https://reviews.llvm.org/D64374
llvm-svn: 365582
Diffstat (limited to 'clang/lib/StaticAnalyzer')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp | 190 |
2 files changed, 191 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt index 63ff7705318..7cbd8c2a713 100644 --- a/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt +++ b/clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -16,6 +16,7 @@ add_clang_library(clangStaticAnalyzerCheckers CallAndMessageChecker.cpp CastSizeChecker.cpp CastToStructChecker.cpp + CastValueChecker.cpp CheckObjCDealloc.cpp CheckObjCInstMethSignature.cpp CheckSecuritySyntaxOnly.cpp diff --git a/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp new file mode 100644 index 00000000000..ff5d12c27c6 --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp @@ -0,0 +1,190 @@ +//===- CastValueChecker - Model implementation of custom RTTIs --*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This defines CastValueChecker which models casts of custom RTTIs. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/Optional.h" + +using namespace clang; +using namespace ento; + +namespace { +class CastValueChecker : public Checker<eval::Call> { + using CastCheck = + std::function<void(const CastValueChecker *, const CallExpr *, + DefinedOrUnknownSVal, CheckerContext &)>; + +public: + // We have three cases to evaluate a cast: + // 1) The parameter is non-null, the return value is non-null + // 2) The parameter is non-null, the return value is null + // 3) The parameter is null, the return value is null + // + // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3. + bool evalCall(const CallEvent &Call, CheckerContext &C) const; + +private: + // These are known in the LLVM project. + const CallDescriptionMap<CastCheck> CDM = { + {{{"llvm", "cast"}, 1}, &CastValueChecker::evalCast}, + {{{"llvm", "dyn_cast"}, 1}, &CastValueChecker::evalDynCast}, + {{{"llvm", "cast_or_null"}, 1}, &CastValueChecker::evalCastOrNull}, + {{{"llvm", "dyn_cast_or_null"}, 1}, + &CastValueChecker::evalDynCastOrNull}}; + + void evalCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const; + void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const; + void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const; + void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const; +}; +} // namespace + +static std::string getCastName(const Expr *Cast) { + return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString(); +} + +static void evalNonNullParamNonNullReturn(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) { + ProgramStateRef State = C.getState()->assume(ParamDV, true); + if (!State) + return; + + State = State->BindExpr(CE, C.getLocationContext(), ParamDV, false); + + std::string CastFromName = getCastName(CE->getArg(0)); + std::string CastToName = getCastName(CE); + + const NoteTag *CastTag = C.getNoteTag( + [CastFromName, CastToName](BugReport &) -> std::string { + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + + Out << "Assuming dynamic cast from '" << CastFromName << "' to '" + << CastToName << "' succeeds"; + return Out.str(); + }, + /*IsPrunable=*/true); + + C.addTransition(State, CastTag); +} + +static void evalNonNullParamNullReturn(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) { + ProgramStateRef State = C.getState()->assume(ParamDV, true); + if (!State) + return; + + State = State->BindExpr(CE, C.getLocationContext(), + C.getSValBuilder().makeNull(), false); + + std::string CastFromName = getCastName(CE->getArg(0)); + std::string CastToName = getCastName(CE); + + const NoteTag *CastTag = C.getNoteTag( + [CastFromName, CastToName](BugReport &) -> std::string { + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + + Out << "Assuming dynamic cast from '" << CastFromName << "' to '" + << CastToName << "' fails"; + return Out.str(); + }, + /*IsPrunable=*/true); + + C.addTransition(State, CastTag); +} + +static void evalNullParamNullReturn(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) { + ProgramStateRef State = C.getState()->assume(ParamDV, false); + if (!State) + return; + + State = State->BindExpr(CE, C.getLocationContext(), + C.getSValBuilder().makeNull(), false); + + const NoteTag *CastTag = + C.getNoteTag("Assuming null pointer is passed into cast", + /*IsPrunable=*/true); + + C.addTransition(State, CastTag); +} + +void CastValueChecker::evalCast(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const { + evalNonNullParamNonNullReturn(CE, ParamDV, C); +} + +void CastValueChecker::evalDynCast(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const { + evalNonNullParamNonNullReturn(CE, ParamDV, C); + evalNonNullParamNullReturn(CE, ParamDV, C); +} + +void CastValueChecker::evalCastOrNull(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const { + evalNonNullParamNonNullReturn(CE, ParamDV, C); + evalNullParamNullReturn(CE, ParamDV, C); +} + +void CastValueChecker::evalDynCastOrNull(const CallExpr *CE, + DefinedOrUnknownSVal ParamDV, + CheckerContext &C) const { + evalNonNullParamNonNullReturn(CE, ParamDV, C); + evalNonNullParamNullReturn(CE, ParamDV, C); + evalNullParamNullReturn(CE, ParamDV, C); +} + +bool CastValueChecker::evalCall(const CallEvent &Call, + CheckerContext &C) const { + const CastCheck *Check = CDM.lookup(Call); + if (!Check) + return false; + + const auto *CE = cast<CallExpr>(Call.getOriginExpr()); + if (!CE) + return false; + + // If we cannot obtain both of the classes we cannot be sure how to model it. + if (!CE->getType()->getPointeeCXXRecordDecl() || + !CE->getArg(0)->getType()->getPointeeCXXRecordDecl()) + return false; + + SVal ParamV = Call.getArgSVal(0); + auto ParamDV = ParamV.getAs<DefinedOrUnknownSVal>(); + if (!ParamDV) + return false; + + (*Check)(this, CE, *ParamDV, C); + return true; +} + +void ento::registerCastValueChecker(CheckerManager &Mgr) { + Mgr.registerChecker<CastValueChecker>(); +} + +bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) { + return true; +} |

