1 //===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This defines NonNullParamChecker, which checks for arguments expected not to 11 // be null due to: 12 // - the corresponding parameters being declared to have nonnull attribute 13 // - the corresponding parameters being references; since the call would form 14 // a reference to a null pointer 15 // 16 //===----------------------------------------------------------------------===// 17 18 #include "ClangSACheckers.h" 19 #include "clang/AST/Attr.h" 20 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 21 #include "clang/StaticAnalyzer/Core/Checker.h" 22 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 23 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 24 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 25 26 using namespace clang; 27 using namespace ento; 28 29 namespace { 30 class NonNullParamChecker 31 : public Checker< check::PreCall > { 32 mutable OwningPtr<BugType> BTAttrNonNull; 33 mutable OwningPtr<BugType> BTNullRefArg; 34 public: 35 36 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 37 38 BugReport *genReportNullAttrNonNull(const ExplodedNode *ErrorN, 39 const Expr *ArgE) const; 40 BugReport *genReportReferenceToNullPointer(const ExplodedNode *ErrorN, 41 const Expr *ArgE) const; 42 }; 43 } // end anonymous namespace 44 45 void NonNullParamChecker::checkPreCall(const CallEvent &Call, 46 CheckerContext &C) const { 47 const Decl *FD = Call.getDecl(); 48 if (!FD) 49 return; 50 51 const NonNullAttr *Att = FD->getAttr<NonNullAttr>(); 52 53 ProgramStateRef state = C.getState(); 54 55 CallEvent::param_type_iterator TyI = Call.param_type_begin(), 56 TyE = Call.param_type_end(); 57 58 for (unsigned idx = 0, count = Call.getNumArgs(); idx != count; ++idx){ 59 60 // Check if the parameter is a reference. We want to report when reference 61 // to a null pointer is passed as a paramter. 62 bool haveRefTypeParam = false; 63 if (TyI != TyE) { 64 haveRefTypeParam = (*TyI)->isReferenceType(); 65 TyI++; 66 } 67 68 bool haveAttrNonNull = Att && Att->isNonNull(idx); 69 if (!haveAttrNonNull) { 70 // Check if the parameter is also marked 'nonnull'. 71 ArrayRef<ParmVarDecl*> parms = Call.parameters(); 72 if (idx < parms.size()) 73 haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); 74 } 75 76 if (!haveRefTypeParam && !haveAttrNonNull) 77 continue; 78 79 // If the value is unknown or undefined, we can't perform this check. 80 const Expr *ArgE = Call.getArgExpr(idx); 81 SVal V = Call.getArgSVal(idx); 82 Optional<DefinedSVal> DV = V.getAs<DefinedSVal>(); 83 if (!DV) 84 continue; 85 86 // Process the case when the argument is not a location. 87 assert(!haveRefTypeParam || DV->getAs<Loc>()); 88 89 if (haveAttrNonNull && !DV->getAs<Loc>()) { 90 // If the argument is a union type, we want to handle a potential 91 // transparent_union GCC extension. 92 if (!ArgE) 93 continue; 94 95 QualType T = ArgE->getType(); 96 const RecordType *UT = T->getAsUnionType(); 97 if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) 98 continue; 99 100 if (Optional<nonloc::CompoundVal> CSV = 101 DV->getAs<nonloc::CompoundVal>()) { 102 nonloc::CompoundVal::iterator CSV_I = CSV->begin(); 103 assert(CSV_I != CSV->end()); 104 V = *CSV_I; 105 DV = V.getAs<DefinedSVal>(); 106 assert(++CSV_I == CSV->end()); 107 if (!DV) 108 continue; 109 // Retrieve the corresponding expression. 110 if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) 111 if (const InitListExpr *IE = 112 dyn_cast<InitListExpr>(CE->getInitializer())) 113 ArgE = dyn_cast<Expr>(*(IE->begin())); 114 115 } else { 116 // FIXME: Handle LazyCompoundVals? 117 continue; 118 } 119 } 120 121 ConstraintManager &CM = C.getConstraintManager(); 122 ProgramStateRef stateNotNull, stateNull; 123 llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 124 125 if (stateNull && !stateNotNull) { 126 // Generate an error node. Check for a null node in case 127 // we cache out. 128 if (ExplodedNode *errorNode = C.generateSink(stateNull)) { 129 130 BugReport *R = 0; 131 if (haveAttrNonNull) 132 R = genReportNullAttrNonNull(errorNode, ArgE); 133 else if (haveRefTypeParam) 134 R = genReportReferenceToNullPointer(errorNode, ArgE); 135 136 // Highlight the range of the argument that was null. 137 R->addRange(Call.getArgSourceRange(idx)); 138 139 // Emit the bug report. 140 C.emitReport(R); 141 } 142 143 // Always return. Either we cached out or we just emitted an error. 144 return; 145 } 146 147 // If a pointer value passed the check we should assume that it is 148 // indeed not null from this point forward. 149 assert(stateNotNull); 150 state = stateNotNull; 151 } 152 153 // If we reach here all of the arguments passed the nonnull check. 154 // If 'state' has been updated generated a new node. 155 C.addTransition(state); 156 } 157 158 BugReport *NonNullParamChecker::genReportNullAttrNonNull( 159 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 160 // Lazily allocate the BugType object if it hasn't already been 161 // created. Ownership is transferred to the BugReporter object once 162 // the BugReport is passed to 'EmitWarning'. 163 if (!BTAttrNonNull) 164 BTAttrNonNull.reset(new BugType( 165 "Argument with 'nonnull' attribute passed null", 166 "API")); 167 168 BugReport *R = new BugReport(*BTAttrNonNull, 169 "Null pointer passed as an argument to a 'nonnull' parameter", 170 ErrorNode); 171 if (ArgE) 172 bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); 173 174 return R; 175 } 176 177 BugReport *NonNullParamChecker::genReportReferenceToNullPointer( 178 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 179 if (!BTNullRefArg) 180 BTNullRefArg.reset(new BuiltinBug("Dereference of null pointer")); 181 182 BugReport *R = new BugReport(*BTNullRefArg, 183 "Forming reference to null pointer", 184 ErrorNode); 185 if (ArgE) { 186 const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 187 if (ArgEDeref == 0) 188 ArgEDeref = ArgE; 189 bugreporter::trackNullOrUndefValue(ErrorNode, 190 ArgEDeref, 191 *R); 192 } 193 return R; 194 195 } 196 197 void ento::registerNonNullParamChecker(CheckerManager &mgr) { 198 mgr.registerChecker<NonNullParamChecker>(); 199 } 200