1249261Sdim//===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===// 2249261Sdim// 3249261Sdim// The LLVM Compiler Infrastructure 4249261Sdim// 5249261Sdim// This file is distributed under the University of Illinois Open Source 6249261Sdim// License. See LICENSE.TXT for details. 7249261Sdim// 8249261Sdim//===----------------------------------------------------------------------===// 9249261Sdim// 10249261Sdim// This defines NonNullParamChecker, which checks for arguments expected not to 11249261Sdim// be null due to: 12249261Sdim// - the corresponding parameters being declared to have nonnull attribute 13249261Sdim// - the corresponding parameters being references; since the call would form 14249261Sdim// a reference to a null pointer 15249261Sdim// 16249261Sdim//===----------------------------------------------------------------------===// 17249261Sdim 18249261Sdim#include "ClangSACheckers.h" 19249261Sdim#include "clang/AST/Attr.h" 20249261Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 21249261Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 22249261Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 23249261Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 24249261Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 25249261Sdim 26249261Sdimusing namespace clang; 27249261Sdimusing namespace ento; 28249261Sdim 29249261Sdimnamespace { 30249261Sdimclass NonNullParamChecker 31296417Sdim : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > { 32276479Sdim mutable std::unique_ptr<BugType> BTAttrNonNull; 33276479Sdim mutable std::unique_ptr<BugType> BTNullRefArg; 34276479Sdim 35249261Sdimpublic: 36249261Sdim 37249261Sdim void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 38249261Sdim 39288943Sdim std::unique_ptr<BugReport> 40288943Sdim genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const; 41288943Sdim std::unique_ptr<BugReport> 42288943Sdim genReportReferenceToNullPointer(const ExplodedNode *ErrorN, 43288943Sdim const Expr *ArgE) const; 44249261Sdim}; 45249261Sdim} // end anonymous namespace 46249261Sdim 47249261Sdimvoid NonNullParamChecker::checkPreCall(const CallEvent &Call, 48276479Sdim CheckerContext &C) const { 49249261Sdim const Decl *FD = Call.getDecl(); 50249261Sdim if (!FD) 51249261Sdim return; 52249261Sdim 53280031Sdim // Merge all non-null attributes 54280031Sdim unsigned NumArgs = Call.getNumArgs(); 55280031Sdim llvm::SmallBitVector AttrNonNull(NumArgs); 56280031Sdim for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { 57280031Sdim if (!NonNull->args_size()) { 58280031Sdim AttrNonNull.set(0, NumArgs); 59280031Sdim break; 60280031Sdim } 61280031Sdim for (unsigned Val : NonNull->args()) { 62280031Sdim if (Val >= NumArgs) 63280031Sdim continue; 64280031Sdim AttrNonNull.set(Val); 65280031Sdim } 66280031Sdim } 67249261Sdim 68249261Sdim ProgramStateRef state = C.getState(); 69249261Sdim 70249261Sdim CallEvent::param_type_iterator TyI = Call.param_type_begin(), 71249261Sdim TyE = Call.param_type_end(); 72249261Sdim 73280031Sdim for (unsigned idx = 0; idx < NumArgs; ++idx) { 74249261Sdim 75249261Sdim // Check if the parameter is a reference. We want to report when reference 76249261Sdim // to a null pointer is passed as a paramter. 77249261Sdim bool haveRefTypeParam = false; 78249261Sdim if (TyI != TyE) { 79249261Sdim haveRefTypeParam = (*TyI)->isReferenceType(); 80249261Sdim TyI++; 81249261Sdim } 82249261Sdim 83280031Sdim bool haveAttrNonNull = AttrNonNull[idx]; 84276479Sdim if (!haveAttrNonNull) { 85276479Sdim // Check if the parameter is also marked 'nonnull'. 86276479Sdim ArrayRef<ParmVarDecl*> parms = Call.parameters(); 87276479Sdim if (idx < parms.size()) 88276479Sdim haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); 89276479Sdim } 90249261Sdim 91249261Sdim if (!haveRefTypeParam && !haveAttrNonNull) 92249261Sdim continue; 93249261Sdim 94249261Sdim // If the value is unknown or undefined, we can't perform this check. 95249261Sdim const Expr *ArgE = Call.getArgExpr(idx); 96249261Sdim SVal V = Call.getArgSVal(idx); 97249261Sdim Optional<DefinedSVal> DV = V.getAs<DefinedSVal>(); 98249261Sdim if (!DV) 99249261Sdim continue; 100249261Sdim 101249261Sdim // Process the case when the argument is not a location. 102249261Sdim assert(!haveRefTypeParam || DV->getAs<Loc>()); 103249261Sdim 104249261Sdim if (haveAttrNonNull && !DV->getAs<Loc>()) { 105249261Sdim // If the argument is a union type, we want to handle a potential 106249261Sdim // transparent_union GCC extension. 107249261Sdim if (!ArgE) 108249261Sdim continue; 109249261Sdim 110249261Sdim QualType T = ArgE->getType(); 111249261Sdim const RecordType *UT = T->getAsUnionType(); 112249261Sdim if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) 113249261Sdim continue; 114249261Sdim 115249261Sdim if (Optional<nonloc::CompoundVal> CSV = 116249261Sdim DV->getAs<nonloc::CompoundVal>()) { 117249261Sdim nonloc::CompoundVal::iterator CSV_I = CSV->begin(); 118249261Sdim assert(CSV_I != CSV->end()); 119249261Sdim V = *CSV_I; 120249261Sdim DV = V.getAs<DefinedSVal>(); 121249261Sdim assert(++CSV_I == CSV->end()); 122276479Sdim // FIXME: Handle (some_union){ some_other_union_val }, which turns into 123276479Sdim // a LazyCompoundVal inside a CompoundVal. 124276479Sdim if (!V.getAs<Loc>()) 125249261Sdim continue; 126249261Sdim // Retrieve the corresponding expression. 127249261Sdim if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) 128249261Sdim if (const InitListExpr *IE = 129249261Sdim dyn_cast<InitListExpr>(CE->getInitializer())) 130249261Sdim ArgE = dyn_cast<Expr>(*(IE->begin())); 131249261Sdim 132249261Sdim } else { 133249261Sdim // FIXME: Handle LazyCompoundVals? 134249261Sdim continue; 135249261Sdim } 136249261Sdim } 137249261Sdim 138249261Sdim ConstraintManager &CM = C.getConstraintManager(); 139249261Sdim ProgramStateRef stateNotNull, stateNull; 140276479Sdim std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 141249261Sdim 142296417Sdim if (stateNull) { 143296417Sdim if (!stateNotNull) { 144296417Sdim // Generate an error node. Check for a null node in case 145296417Sdim // we cache out. 146296417Sdim if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { 147249261Sdim 148296417Sdim std::unique_ptr<BugReport> R; 149296417Sdim if (haveAttrNonNull) 150296417Sdim R = genReportNullAttrNonNull(errorNode, ArgE); 151296417Sdim else if (haveRefTypeParam) 152296417Sdim R = genReportReferenceToNullPointer(errorNode, ArgE); 153249261Sdim 154296417Sdim // Highlight the range of the argument that was null. 155296417Sdim R->addRange(Call.getArgSourceRange(idx)); 156249261Sdim 157296417Sdim // Emit the bug report. 158296417Sdim C.emitReport(std::move(R)); 159296417Sdim } 160296417Sdim 161296417Sdim // Always return. Either we cached out or we just emitted an error. 162296417Sdim return; 163249261Sdim } 164296417Sdim if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { 165296417Sdim ImplicitNullDerefEvent event = { 166296417Sdim V, false, N, &C.getBugReporter(), 167296417Sdim /*IsDirectDereference=*/haveRefTypeParam}; 168296417Sdim dispatchEvent(event); 169296417Sdim } 170249261Sdim } 171249261Sdim 172249261Sdim // If a pointer value passed the check we should assume that it is 173249261Sdim // indeed not null from this point forward. 174249261Sdim assert(stateNotNull); 175249261Sdim state = stateNotNull; 176249261Sdim } 177249261Sdim 178249261Sdim // If we reach here all of the arguments passed the nonnull check. 179249261Sdim // If 'state' has been updated generated a new node. 180249261Sdim C.addTransition(state); 181249261Sdim} 182249261Sdim 183288943Sdimstd::unique_ptr<BugReport> 184288943SdimNonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, 185288943Sdim const Expr *ArgE) const { 186249261Sdim // Lazily allocate the BugType object if it hasn't already been 187249261Sdim // created. Ownership is transferred to the BugReporter object once 188249261Sdim // the BugReport is passed to 'EmitWarning'. 189249261Sdim if (!BTAttrNonNull) 190249261Sdim BTAttrNonNull.reset(new BugType( 191276479Sdim this, "Argument with 'nonnull' attribute passed null", "API")); 192249261Sdim 193288943Sdim auto R = llvm::make_unique<BugReport>( 194288943Sdim *BTAttrNonNull, 195288943Sdim "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode); 196249261Sdim if (ArgE) 197249261Sdim bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); 198249261Sdim 199249261Sdim return R; 200249261Sdim} 201249261Sdim 202288943Sdimstd::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer( 203288943Sdim const ExplodedNode *ErrorNode, const Expr *ArgE) const { 204249261Sdim if (!BTNullRefArg) 205276479Sdim BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); 206249261Sdim 207288943Sdim auto R = llvm::make_unique<BugReport>( 208288943Sdim *BTNullRefArg, "Forming reference to null pointer", ErrorNode); 209249261Sdim if (ArgE) { 210249261Sdim const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 211276479Sdim if (!ArgEDeref) 212249261Sdim ArgEDeref = ArgE; 213249261Sdim bugreporter::trackNullOrUndefValue(ErrorNode, 214249261Sdim ArgEDeref, 215249261Sdim *R); 216249261Sdim } 217249261Sdim return R; 218249261Sdim 219249261Sdim} 220249261Sdim 221249261Sdimvoid ento::registerNonNullParamChecker(CheckerManager &mgr) { 222249261Sdim mgr.registerChecker<NonNullParamChecker>(); 223249261Sdim} 224