NonNullParamChecker.cpp revision 360660
133965Sjdp//===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===// 2218822Sdim// 3218822Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 433965Sjdp// See https://llvm.org/LICENSE.txt for license information. 533965Sjdp// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6130561Sobrien// 733965Sjdp//===----------------------------------------------------------------------===// 8130561Sobrien// 9130561Sobrien// This defines NonNullParamChecker, which checks for arguments expected not to 10130561Sobrien// be null due to: 11130561Sobrien// - the corresponding parameters being declared to have nonnull attribute 1233965Sjdp// - the corresponding parameters being references; since the call would form 13130561Sobrien// a reference to a null pointer 14130561Sobrien// 15130561Sobrien//===----------------------------------------------------------------------===// 16130561Sobrien 1733965Sjdp#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 18130561Sobrien#include "clang/AST/Attr.h" 19130561Sobrien#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20218822Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 2133965Sjdp#include "clang/StaticAnalyzer/Core/CheckerManager.h" 2233965Sjdp#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 2333965Sjdp#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 2433965Sjdp 2533965Sjdpusing namespace clang; 2633965Sjdpusing namespace ento; 2733965Sjdp 2833965Sjdpnamespace { 2933965Sjdpclass NonNullParamChecker 3033965Sjdp : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > { 3133965Sjdp mutable std::unique_ptr<BugType> BTAttrNonNull; 3233965Sjdp mutable std::unique_ptr<BugType> BTNullRefArg; 3333965Sjdp 3433965Sjdppublic: 35218822Sdim 3633965Sjdp void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 3789857Sobrien 3833965Sjdp std::unique_ptr<BugReport> 3933965Sjdp genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const; 4033965Sjdp std::unique_ptr<BugReport> 4133965Sjdp genReportReferenceToNullPointer(const ExplodedNode *ErrorN, 4233965Sjdp const Expr *ArgE) const; 4333965Sjdp}; 44130561Sobrien} // end anonymous namespace 45130561Sobrien 46130561Sobrien/// \return Bitvector marking non-null attributes. 47130561Sobrienstatic llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { 4889857Sobrien const Decl *FD = Call.getDecl(); 4933965Sjdp unsigned NumArgs = Call.getNumArgs(); 5033965Sjdp llvm::SmallBitVector AttrNonNull(NumArgs); 51130561Sobrien for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { 52218822Sdim if (!NonNull->args_size()) { 5333965Sjdp AttrNonNull.set(0, NumArgs); 54130561Sobrien break; 5533965Sjdp } 5633965Sjdp for (const ParamIdx &Idx : NonNull->args()) { 5733965Sjdp unsigned IdxAST = Idx.getASTIndex(); 5833965Sjdp if (IdxAST >= NumArgs) 5933965Sjdp continue; 6033965Sjdp AttrNonNull.set(IdxAST); 6133965Sjdp } 62218822Sdim } 6333965Sjdp return AttrNonNull; 6433965Sjdp} 6533965Sjdp 66218822Sdimvoid NonNullParamChecker::checkPreCall(const CallEvent &Call, 6733965Sjdp CheckerContext &C) const { 6833965Sjdp if (!Call.getDecl()) 6933965Sjdp return; 7033965Sjdp 7133965Sjdp llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call); 7233965Sjdp unsigned NumArgs = Call.getNumArgs(); 7333965Sjdp 7433965Sjdp ProgramStateRef state = C.getState(); 7533965Sjdp ArrayRef<ParmVarDecl*> parms = Call.parameters(); 7633965Sjdp 7733965Sjdp for (unsigned idx = 0; idx < NumArgs; ++idx) { 7833965Sjdp // For vararg functions, a corresponding parameter decl may not exist. 7933965Sjdp bool HasParam = idx < parms.size(); 8033965Sjdp 8133965Sjdp // Check if the parameter is a reference. We want to report when reference 8233965Sjdp // to a null pointer is passed as a parameter. 8333965Sjdp bool haveRefTypeParam = 84218822Sdim HasParam ? parms[idx]->getType()->isReferenceType() : false; 85218822Sdim bool haveAttrNonNull = AttrNonNull[idx]; 8633965Sjdp 8733965Sjdp // Check if the parameter is also marked 'nonnull'. 8833965Sjdp if (!haveAttrNonNull && HasParam) 89218822Sdim haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); 9033965Sjdp 9133965Sjdp if (!haveAttrNonNull && !haveRefTypeParam) 92218822Sdim continue; 9333965Sjdp 9489857Sobrien // If the value is unknown or undefined, we can't perform this check. 9589857Sobrien const Expr *ArgE = Call.getArgExpr(idx); 9689857Sobrien SVal V = Call.getArgSVal(idx); 9789857Sobrien auto DV = V.getAs<DefinedSVal>(); 98130561Sobrien if (!DV) 99130561Sobrien continue; 10089857Sobrien 10189857Sobrien assert(!haveRefTypeParam || DV->getAs<Loc>()); 10233965Sjdp 10333965Sjdp // Process the case when the argument is not a location. 10433965Sjdp if (haveAttrNonNull && !DV->getAs<Loc>()) { 105218822Sdim // If the argument is a union type, we want to handle a potential 106218822Sdim // transparent_union GCC extension. 107218822Sdim if (!ArgE) 10833965Sjdp continue; 10933965Sjdp 11033965Sjdp QualType T = ArgE->getType(); 111130561Sobrien const RecordType *UT = T->getAsUnionType(); 112218822Sdim if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) 113218822Sdim continue; 114218822Sdim 115218822Sdim auto CSV = DV->getAs<nonloc::CompoundVal>(); 116218822Sdim 11733965Sjdp // FIXME: Handle LazyCompoundVals? 11833965Sjdp if (!CSV) 11989857Sobrien continue; 120130561Sobrien 121130561Sobrien V = *(CSV->begin()); 12233965Sjdp DV = V.getAs<DefinedSVal>(); 12333965Sjdp assert(++CSV->begin() == CSV->end()); 12433965Sjdp // FIXME: Handle (some_union){ some_other_union_val }, which turns into 12533965Sjdp // a LazyCompoundVal inside a CompoundVal. 12633965Sjdp if (!V.getAs<Loc>()) 127218822Sdim continue; 12833965Sjdp 12933965Sjdp // Retrieve the corresponding expression. 13033965Sjdp if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) 13133965Sjdp if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer())) 13233965Sjdp ArgE = dyn_cast<Expr>(*(IE->begin())); 13333965Sjdp } 13433965Sjdp 135218822Sdim ConstraintManager &CM = C.getConstraintManager(); 13633965Sjdp ProgramStateRef stateNotNull, stateNull; 13789857Sobrien std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 13833965Sjdp 13933965Sjdp // Generate an error node. Check for a null node in case 14033965Sjdp // we cache out. 14133965Sjdp if (stateNull && !stateNotNull) { 14233965Sjdp if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { 14333965Sjdp 14433965Sjdp std::unique_ptr<BugReport> R; 145218822Sdim if (haveAttrNonNull) 14633965Sjdp R = genReportNullAttrNonNull(errorNode, ArgE); 14733965Sjdp else if (haveRefTypeParam) 14833965Sjdp R = genReportReferenceToNullPointer(errorNode, ArgE); 14933965Sjdp 15033965Sjdp // Highlight the range of the argument that was null. 15133965Sjdp R->addRange(Call.getArgSourceRange(idx)); 15233965Sjdp 15389857Sobrien // Emit the bug report. 15433965Sjdp C.emitReport(std::move(R)); 15533965Sjdp } 15633965Sjdp 15733965Sjdp // Always return. Either we cached out or we just emitted an error. 15833965Sjdp return; 15933965Sjdp } 16033965Sjdp 16133965Sjdp if (stateNull) { 162218822Sdim if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { 16333965Sjdp ImplicitNullDerefEvent event = { 16433965Sjdp V, false, N, &C.getBugReporter(), 16533965Sjdp /*IsDirectDereference=*/haveRefTypeParam}; 16633965Sjdp dispatchEvent(event); 16789857Sobrien } 16833965Sjdp } 169218822Sdim 17033965Sjdp // If a pointer value passed the check we should assume that it is 171130561Sobrien // indeed not null from this point forward. 17233965Sjdp state = stateNotNull; 17333965Sjdp } 17433965Sjdp 17533965Sjdp // If we reach here all of the arguments passed the nonnull check. 17633965Sjdp // If 'state' has been updated generated a new node. 17733965Sjdp C.addTransition(state); 17833965Sjdp} 17933965Sjdp 18033965Sjdpstd::unique_ptr<BugReport> 18133965SjdpNonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, 18233965Sjdp const Expr *ArgE) const { 18333965Sjdp // Lazily allocate the BugType object if it hasn't already been 184218822Sdim // created. Ownership is transferred to the BugReporter object once 18533965Sjdp // the BugReport is passed to 'EmitWarning'. 18633965Sjdp if (!BTAttrNonNull) 18733965Sjdp BTAttrNonNull.reset(new BugType( 18833965Sjdp this, "Argument with 'nonnull' attribute passed null", "API")); 18933965Sjdp 19033965Sjdp auto R = llvm::make_unique<BugReport>( 19133965Sjdp *BTAttrNonNull, 192218822Sdim "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode); 19333965Sjdp if (ArgE) 19433965Sjdp bugreporter::trackExpressionValue(ErrorNode, ArgE, *R); 19533965Sjdp 19633965Sjdp return R; 19733965Sjdp} 19833965Sjdp 19933965Sjdpstd::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer( 20033965Sjdp const ExplodedNode *ErrorNode, const Expr *ArgE) const { 20133965Sjdp if (!BTNullRefArg) 20233965Sjdp BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); 20333965Sjdp 204218822Sdim auto R = llvm::make_unique<BugReport>( 205218822Sdim *BTNullRefArg, "Forming reference to null pointer", ErrorNode); 20633965Sjdp if (ArgE) { 20733965Sjdp const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 20833965Sjdp if (!ArgEDeref) 20933965Sjdp ArgEDeref = ArgE; 210218822Sdim bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R); 211218822Sdim } 212218822Sdim return R; 21333965Sjdp 21433965Sjdp} 21533965Sjdp 21633965Sjdpvoid ento::registerNonNullParamChecker(CheckerManager &mgr) { 217218822Sdim mgr.registerChecker<NonNullParamChecker>(); 218218822Sdim} 219218822Sdim 220218822Sdimbool ento::shouldRegisterNonNullParamChecker(const LangOptions &LO) { 221218822Sdim return true; 222218822Sdim} 223218822Sdim