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 // FIXME: Handle (some_union){ some_other_union_val }, which turns into 108 // a LazyCompoundVal inside a CompoundVal. 109 if (!V.getAs<Loc>()) 110 continue; 111 // Retrieve the corresponding expression. 112 if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) 113 if (const InitListExpr *IE = 114 dyn_cast<InitListExpr>(CE->getInitializer())) 115 ArgE = dyn_cast<Expr>(*(IE->begin())); 116 117 } else { 118 // FIXME: Handle LazyCompoundVals? 119 continue; 120 } 121 } 122 123 ConstraintManager &CM = C.getConstraintManager(); 124 ProgramStateRef stateNotNull, stateNull; 125 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 126 127 if (stateNull && !stateNotNull) { 128 // Generate an error node. Check for a null node in case 129 // we cache out. 130 if (ExplodedNode *errorNode = C.generateSink(stateNull)) { 131 132 BugReport *R = 0; 133 if (haveAttrNonNull) 134 R = genReportNullAttrNonNull(errorNode, ArgE); 135 else if (haveRefTypeParam) 136 R = genReportReferenceToNullPointer(errorNode, ArgE); 137 138 // Highlight the range of the argument that was null. 139 R->addRange(Call.getArgSourceRange(idx)); 140 141 // Emit the bug report. 142 C.emitReport(R); 143 } 144 145 // Always return. Either we cached out or we just emitted an error. 146 return; 147 } 148 149 // If a pointer value passed the check we should assume that it is 150 // indeed not null from this point forward. 151 assert(stateNotNull); 152 state = stateNotNull; 153 } 154 155 // If we reach here all of the arguments passed the nonnull check. 156 // If 'state' has been updated generated a new node. 157 C.addTransition(state); 158 } 159 160 BugReport *NonNullParamChecker::genReportNullAttrNonNull( 161 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 162 // Lazily allocate the BugType object if it hasn't already been 163 // created. Ownership is transferred to the BugReporter object once 164 // the BugReport is passed to 'EmitWarning'. 165 if (!BTAttrNonNull) 166 BTAttrNonNull.reset(new BugType( 167 this, "Argument with 'nonnull' attribute passed null", "API")); 168 169 BugReport *R = new BugReport(*BTAttrNonNull, 170 "Null pointer passed as an argument to a 'nonnull' parameter", 171 ErrorNode); 172 if (ArgE) 173 bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); 174 175 return R; 176 } 177 178 BugReport *NonNullParamChecker::genReportReferenceToNullPointer( 179 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 180 if (!BTNullRefArg) 181 BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); 182 183 BugReport *R = new BugReport(*BTNullRefArg, 184 "Forming reference to null pointer", 185 ErrorNode); 186 if (ArgE) { 187 const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 188 if (ArgEDeref == 0) 189 ArgEDeref = ArgE; 190 bugreporter::trackNullOrUndefValue(ErrorNode, 191 ArgEDeref, 192 *R); 193 } 194 return R; 195 196 } 197 198 void ento::registerNonNullParamChecker(CheckerManager &mgr) { 199 mgr.registerChecker<NonNullParamChecker>(); 200 } 201