NonNullParamChecker.cpp revision 341825
1286171Smarkj//===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===// 2210753Srpaulo// 3210753Srpaulo// The LLVM Compiler Infrastructure 4210753Srpaulo// 5210753Srpaulo// This file is distributed under the University of Illinois Open Source 6210753Srpaulo// License. See LICENSE.TXT for details. 7210753Srpaulo// 8210753Srpaulo//===----------------------------------------------------------------------===// 9210753Srpaulo// 10210753Srpaulo// This defines NonNullParamChecker, which checks for arguments expected not to 11210753Srpaulo// be null due to: 12210753Srpaulo// - the corresponding parameters being declared to have nonnull attribute 13210753Srpaulo// - the corresponding parameters being references; since the call would form 14210753Srpaulo// a reference to a null pointer 15210753Srpaulo// 16210753Srpaulo//===----------------------------------------------------------------------===// 17210753Srpaulo 18210753Srpaulo#include "ClangSACheckers.h" 19210753Srpaulo#include "clang/AST/Attr.h" 20210753Srpaulo#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 21210753Srpaulo#include "clang/StaticAnalyzer/Core/Checker.h" 22210753Srpaulo#include "clang/StaticAnalyzer/Core/CheckerManager.h" 23210753Srpaulo#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 24210753Srpaulo#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 25210753Srpaulo 26210753Srpaulousing namespace clang; 27210753Srpaulousing namespace ento; 28210753Srpaulo 29210753Srpaulonamespace { 30210753Srpauloclass NonNullParamChecker 31210753Srpaulo : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > { 32210753Srpaulo mutable std::unique_ptr<BugType> BTAttrNonNull; 33210753Srpaulo mutable std::unique_ptr<BugType> BTNullRefArg; 34210753Srpaulo 35210753Srpaulopublic: 36210753Srpaulo 37210753Srpaulo void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 38210753Srpaulo 39210753Srpaulo std::unique_ptr<BugReport> 40210753Srpaulo genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const; 41210753Srpaulo std::unique_ptr<BugReport> 42210753Srpaulo genReportReferenceToNullPointer(const ExplodedNode *ErrorN, 43210753Srpaulo const Expr *ArgE) const; 44210753Srpaulo}; 45210753Srpaulo} // end anonymous namespace 46210753Srpaulo 47210753Srpaulo/// \return Bitvector marking non-null attributes. 48210753Srpaulostatic llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { 49210753Srpaulo const Decl *FD = Call.getDecl(); 50210753Srpaulo unsigned NumArgs = Call.getNumArgs(); 51250685Smarkj llvm::SmallBitVector AttrNonNull(NumArgs); 52210753Srpaulo for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { 53250685Smarkj if (!NonNull->args_size()) { 54210753Srpaulo AttrNonNull.set(0, NumArgs); 55211545Srpaulo break; 56210753Srpaulo } 57210753Srpaulo for (const ParamIdx &Idx : NonNull->args()) { 58210753Srpaulo unsigned IdxAST = Idx.getASTIndex(); 59210753Srpaulo if (IdxAST >= NumArgs) 60210753Srpaulo continue; 61210753Srpaulo AttrNonNull.set(IdxAST); 62210753Srpaulo } 63210753Srpaulo } 64250685Smarkj return AttrNonNull; 65250685Smarkj} 66250685Smarkj 67250685Smarkjvoid NonNullParamChecker::checkPreCall(const CallEvent &Call, 68210753Srpaulo CheckerContext &C) const { 69250685Smarkj if (!Call.getDecl()) 70210753Srpaulo return; 71210753Srpaulo 72250685Smarkj llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call); 73210753Srpaulo unsigned NumArgs = Call.getNumArgs(); 74250685Smarkj 75210753Srpaulo ProgramStateRef state = C.getState(); 76210753Srpaulo ArrayRef<ParmVarDecl*> parms = Call.parameters(); 77210753Srpaulo 78210753Srpaulo for (unsigned idx = 0; idx < NumArgs; ++idx) { 79210753Srpaulo // For vararg functions, a corresponding parameter decl may not exist. 80210753Srpaulo bool HasParam = idx < parms.size(); 81250685Smarkj 82210753Srpaulo // Check if the parameter is a reference. We want to report when reference 83250685Smarkj // to a null pointer is passed as a parameter. 84250685Smarkj bool haveRefTypeParam = 85250685Smarkj HasParam ? parms[idx]->getType()->isReferenceType() : false; 86250685Smarkj bool haveAttrNonNull = AttrNonNull[idx]; 87250685Smarkj 88250685Smarkj // Check if the parameter is also marked 'nonnull'. 89250685Smarkj if (!haveAttrNonNull && HasParam) 90250685Smarkj haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); 91250685Smarkj 92210753Srpaulo if (!haveAttrNonNull && !haveRefTypeParam) 93210753Srpaulo continue; 94210753Srpaulo 95210753Srpaulo // If the value is unknown or undefined, we can't perform this check. 96210753Srpaulo const Expr *ArgE = Call.getArgExpr(idx); 97210753Srpaulo SVal V = Call.getArgSVal(idx); 98 auto DV = V.getAs<DefinedSVal>(); 99 if (!DV) 100 continue; 101 102 assert(!haveRefTypeParam || DV->getAs<Loc>()); 103 104 // Process the case when the argument is not a location. 105 if (haveAttrNonNull && !DV->getAs<Loc>()) { 106 // If the argument is a union type, we want to handle a potential 107 // transparent_union GCC extension. 108 if (!ArgE) 109 continue; 110 111 QualType T = ArgE->getType(); 112 const RecordType *UT = T->getAsUnionType(); 113 if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) 114 continue; 115 116 auto CSV = DV->getAs<nonloc::CompoundVal>(); 117 118 // FIXME: Handle LazyCompoundVals? 119 if (!CSV) 120 continue; 121 122 V = *(CSV->begin()); 123 DV = V.getAs<DefinedSVal>(); 124 assert(++CSV->begin() == CSV->end()); 125 // FIXME: Handle (some_union){ some_other_union_val }, which turns into 126 // a LazyCompoundVal inside a CompoundVal. 127 if (!V.getAs<Loc>()) 128 continue; 129 130 // Retrieve the corresponding expression. 131 if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) 132 if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer())) 133 ArgE = dyn_cast<Expr>(*(IE->begin())); 134 } 135 136 ConstraintManager &CM = C.getConstraintManager(); 137 ProgramStateRef stateNotNull, stateNull; 138 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 139 140 // Generate an error node. Check for a null node in case 141 // we cache out. 142 if (stateNull && !stateNotNull) { 143 if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { 144 145 std::unique_ptr<BugReport> R; 146 if (haveAttrNonNull) 147 R = genReportNullAttrNonNull(errorNode, ArgE); 148 else if (haveRefTypeParam) 149 R = genReportReferenceToNullPointer(errorNode, ArgE); 150 151 // Highlight the range of the argument that was null. 152 R->addRange(Call.getArgSourceRange(idx)); 153 154 // Emit the bug report. 155 C.emitReport(std::move(R)); 156 } 157 158 // Always return. Either we cached out or we just emitted an error. 159 return; 160 } 161 162 if (stateNull) { 163 if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { 164 ImplicitNullDerefEvent event = { 165 V, false, N, &C.getBugReporter(), 166 /*IsDirectDereference=*/haveRefTypeParam}; 167 dispatchEvent(event); 168 } 169 } 170 171 // If a pointer value passed the check we should assume that it is 172 // indeed not null from this point forward. 173 state = stateNotNull; 174 } 175 176 // If we reach here all of the arguments passed the nonnull check. 177 // If 'state' has been updated generated a new node. 178 C.addTransition(state); 179} 180 181std::unique_ptr<BugReport> 182NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, 183 const Expr *ArgE) const { 184 // Lazily allocate the BugType object if it hasn't already been 185 // created. Ownership is transferred to the BugReporter object once 186 // the BugReport is passed to 'EmitWarning'. 187 if (!BTAttrNonNull) 188 BTAttrNonNull.reset(new BugType( 189 this, "Argument with 'nonnull' attribute passed null", "API")); 190 191 auto R = llvm::make_unique<BugReport>( 192 *BTAttrNonNull, 193 "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode); 194 if (ArgE) 195 bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); 196 197 return R; 198} 199 200std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer( 201 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 202 if (!BTNullRefArg) 203 BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); 204 205 auto R = llvm::make_unique<BugReport>( 206 *BTNullRefArg, "Forming reference to null pointer", ErrorNode); 207 if (ArgE) { 208 const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 209 if (!ArgEDeref) 210 ArgEDeref = ArgE; 211 bugreporter::trackNullOrUndefValue(ErrorNode, 212 ArgEDeref, 213 *R); 214 } 215 return R; 216 217} 218 219void ento::registerNonNullParamChecker(CheckerManager &mgr) { 220 mgr.registerChecker<NonNullParamChecker>(); 221} 222