diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2020-07-31 21:22:58 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2020-07-31 21:22:58 +0000 |
commit | 5ffd83dbcc34f10e07f6d3e968ae6365869615f4 (patch) | |
tree | 0e9f5cf729dde39f949698fddef45a34e2bc7f44 /contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp | |
parent | 1799696096df87b52968b8996d00c91e0a5de8d9 (diff) | |
parent | cfca06d7963fa0909f90483b42a6d7d194d01e08 (diff) |
Notes
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp')
-rw-r--r-- | contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp | 149 |
1 files changed, 122 insertions, 27 deletions
diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp index 6ffc89745365..534b5d68434f 100644 --- a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -14,57 +14,97 @@ // //===----------------------------------------------------------------------===// -#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/AST/Attr.h" +#include "clang/Analysis/AnyCall.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.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/StringExtras.h" using namespace clang; using namespace ento; namespace { class NonNullParamChecker - : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > { + : public Checker<check::PreCall, check::BeginFunction, + EventDispatcher<ImplicitNullDerefEvent>> { mutable std::unique_ptr<BugType> BTAttrNonNull; mutable std::unique_ptr<BugType> BTNullRefArg; public: - void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkBeginFunction(CheckerContext &C) const; std::unique_ptr<PathSensitiveBugReport> - genReportNullAttrNonNull(const ExplodedNode *ErrorN, - const Expr *ArgE, + genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE, unsigned IdxOfArg) const; std::unique_ptr<PathSensitiveBugReport> genReportReferenceToNullPointer(const ExplodedNode *ErrorN, const Expr *ArgE) const; }; -} // end anonymous namespace -/// \return Bitvector marking non-null attributes. -static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { +template <class CallType> +void setBitsAccordingToFunctionAttributes(const CallType &Call, + llvm::SmallBitVector &AttrNonNull) { const Decl *FD = Call.getDecl(); - unsigned NumArgs = Call.getNumArgs(); - llvm::SmallBitVector AttrNonNull(NumArgs); + for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { if (!NonNull->args_size()) { - AttrNonNull.set(0, NumArgs); + // Lack of attribute parameters means that all of the parameters are + // implicitly marked as non-null. + AttrNonNull.set(); break; } + for (const ParamIdx &Idx : NonNull->args()) { + // 'nonnull' attribute's parameters are 1-based and should be adjusted to + // match actual AST parameter/argument indices. unsigned IdxAST = Idx.getASTIndex(); - if (IdxAST >= NumArgs) + if (IdxAST >= AttrNonNull.size()) continue; AttrNonNull.set(IdxAST); } } +} + +template <class CallType> +void setBitsAccordingToParameterAttributes(const CallType &Call, + llvm::SmallBitVector &AttrNonNull) { + for (const ParmVarDecl *Parameter : Call.parameters()) { + unsigned ParameterIndex = Parameter->getFunctionScopeIndex(); + if (ParameterIndex == AttrNonNull.size()) + break; + + if (Parameter->hasAttr<NonNullAttr>()) + AttrNonNull.set(ParameterIndex); + } +} + +template <class CallType> +llvm::SmallBitVector getNonNullAttrsImpl(const CallType &Call, + unsigned ExpectedSize) { + llvm::SmallBitVector AttrNonNull(ExpectedSize); + + setBitsAccordingToFunctionAttributes(Call, AttrNonNull); + setBitsAccordingToParameterAttributes(Call, AttrNonNull); + return AttrNonNull; } +/// \return Bitvector marking non-null attributes. +llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { + return getNonNullAttrsImpl(Call, Call.getNumArgs()); +} + +/// \return Bitvector marking non-null attributes. +llvm::SmallBitVector getNonNullAttrs(const AnyCall &Call) { + return getNonNullAttrsImpl(Call, Call.param_size()); +} +} // end anonymous namespace + void NonNullParamChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { if (!Call.getDecl()) @@ -74,7 +114,7 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, unsigned NumArgs = Call.getNumArgs(); ProgramStateRef state = C.getState(); - ArrayRef<ParmVarDecl*> parms = Call.parameters(); + ArrayRef<ParmVarDecl *> parms = Call.parameters(); for (unsigned idx = 0; idx < NumArgs; ++idx) { // For vararg functions, a corresponding parameter decl may not exist. @@ -82,15 +122,11 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, // Check if the parameter is a reference. We want to report when reference // to a null pointer is passed as a parameter. - bool haveRefTypeParam = + bool HasRefTypeParam = HasParam ? parms[idx]->getType()->isReferenceType() : false; - bool haveAttrNonNull = AttrNonNull[idx]; + bool ExpectedToBeNonNull = AttrNonNull.test(idx); - // Check if the parameter is also marked 'nonnull'. - if (!haveAttrNonNull && HasParam) - haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); - - if (!haveAttrNonNull && !haveRefTypeParam) + if (!ExpectedToBeNonNull && !HasRefTypeParam) continue; // If the value is unknown or undefined, we can't perform this check. @@ -100,10 +136,10 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, if (!DV) continue; - assert(!haveRefTypeParam || DV->getAs<Loc>()); + assert(!HasRefTypeParam || DV->getAs<Loc>()); // Process the case when the argument is not a location. - if (haveAttrNonNull && !DV->getAs<Loc>()) { + if (ExpectedToBeNonNull && !DV->getAs<Loc>()) { // If the argument is a union type, we want to handle a potential // transparent_union GCC extension. if (!ArgE) @@ -144,9 +180,9 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { std::unique_ptr<BugReport> R; - if (haveAttrNonNull) + if (ExpectedToBeNonNull) R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1); - else if (haveRefTypeParam) + else if (HasRefTypeParam) R = genReportReferenceToNullPointer(errorNode, ArgE); // Highlight the range of the argument that was null. @@ -163,8 +199,8 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, if (stateNull) { if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { ImplicitNullDerefEvent event = { - V, false, N, &C.getBugReporter(), - /*IsDirectDereference=*/haveRefTypeParam}; + V, false, N, &C.getBugReporter(), + /*IsDirectDereference=*/HasRefTypeParam}; dispatchEvent(event); } } @@ -179,6 +215,65 @@ void NonNullParamChecker::checkPreCall(const CallEvent &Call, C.addTransition(state); } +/// We want to trust developer annotations and consider all 'nonnull' parameters +/// as non-null indeed. Each marked parameter will get a corresponding +/// constraint. +/// +/// This approach will not only help us to get rid of some false positives, but +/// remove duplicates and shorten warning traces as well. +/// +/// \code +/// void foo(int *x) [[gnu::nonnull]] { +/// // . . . +/// *x = 42; // we don't want to consider this as an error... +/// // . . . +/// } +/// +/// foo(nullptr); // ...and report here instead +/// \endcode +void NonNullParamChecker::checkBeginFunction(CheckerContext &Context) const { + // Planned assumption makes sense only for top-level functions. + // Inlined functions will get similar constraints as part of 'checkPreCall'. + if (!Context.inTopFrame()) + return; + + const LocationContext *LocContext = Context.getLocationContext(); + + const Decl *FD = LocContext->getDecl(); + // AnyCall helps us here to avoid checking for FunctionDecl and ObjCMethodDecl + // separately and aggregates interfaces of these classes. + auto AbstractCall = AnyCall::forDecl(FD); + if (!AbstractCall) + return; + + ProgramStateRef State = Context.getState(); + llvm::SmallBitVector ParameterNonNullMarks = getNonNullAttrs(*AbstractCall); + + for (const ParmVarDecl *Parameter : AbstractCall->parameters()) { + // 1. Check parameter if it is annotated as non-null + if (!ParameterNonNullMarks.test(Parameter->getFunctionScopeIndex())) + continue; + + // 2. Check that parameter is a pointer. + // Nonnull attribute can be applied to non-pointers (by default + // __attribute__(nonnull) implies "all parameters"). + if (!Parameter->getType()->isPointerType()) + continue; + + Loc ParameterLoc = State->getLValue(Parameter, LocContext); + // We never consider top-level function parameters undefined. + auto StoredVal = + State->getSVal(ParameterLoc).castAs<DefinedOrUnknownSVal>(); + + // 3. Assume that it is indeed non-null + if (ProgramStateRef NewState = State->assume(StoredVal, true)) { + State = NewState; + } + } + + Context.addTransition(State); +} + std::unique_ptr<PathSensitiveBugReport> NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, const Expr *ArgE, @@ -226,6 +321,6 @@ void ento::registerNonNullParamChecker(CheckerManager &mgr) { mgr.registerChecker<NonNullParamChecker>(); } -bool ento::shouldRegisterNonNullParamChecker(const LangOptions &LO) { +bool ento::shouldRegisterNonNullParamChecker(const CheckerManager &mgr) { return true; } |