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 70 if (!haveRefTypeParam && !haveAttrNonNull) 71 continue; 72 73 // If the value is unknown or undefined, we can't perform this check. 74 const Expr *ArgE = Call.getArgExpr(idx); 75 SVal V = Call.getArgSVal(idx); 76 Optional<DefinedSVal> DV = V.getAs<DefinedSVal>(); 77 if (!DV) 78 continue; 79 80 // Process the case when the argument is not a location. 81 assert(!haveRefTypeParam || DV->getAs<Loc>()); 82 83 if (haveAttrNonNull && !DV->getAs<Loc>()) { 84 // If the argument is a union type, we want to handle a potential 85 // transparent_union GCC extension. 86 if (!ArgE) 87 continue; 88 89 QualType T = ArgE->getType(); 90 const RecordType *UT = T->getAsUnionType(); 91 if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) 92 continue; 93 94 if (Optional<nonloc::CompoundVal> CSV = 95 DV->getAs<nonloc::CompoundVal>()) { 96 nonloc::CompoundVal::iterator CSV_I = CSV->begin(); 97 assert(CSV_I != CSV->end()); 98 V = *CSV_I; 99 DV = V.getAs<DefinedSVal>(); 100 assert(++CSV_I == CSV->end()); 101 if (!DV) 102 continue; 103 // Retrieve the corresponding expression. 104 if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) 105 if (const InitListExpr *IE = 106 dyn_cast<InitListExpr>(CE->getInitializer())) 107 ArgE = dyn_cast<Expr>(*(IE->begin())); 108 109 } else { 110 // FIXME: Handle LazyCompoundVals? 111 continue; 112 } 113 } 114 115 ConstraintManager &CM = C.getConstraintManager(); 116 ProgramStateRef stateNotNull, stateNull; 117 llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 118 119 if (stateNull && !stateNotNull) { 120 // Generate an error node. Check for a null node in case 121 // we cache out. 122 if (ExplodedNode *errorNode = C.generateSink(stateNull)) { 123 124 BugReport *R = 0; 125 if (haveAttrNonNull) 126 R = genReportNullAttrNonNull(errorNode, ArgE); 127 else if (haveRefTypeParam) 128 R = genReportReferenceToNullPointer(errorNode, ArgE); 129 130 // Highlight the range of the argument that was null. 131 R->addRange(Call.getArgSourceRange(idx)); 132 133 // Emit the bug report. 134 C.emitReport(R); 135 } 136 137 // Always return. Either we cached out or we just emitted an error. 138 return; 139 } 140 141 // If a pointer value passed the check we should assume that it is 142 // indeed not null from this point forward. 143 assert(stateNotNull); 144 state = stateNotNull; 145 } 146 147 // If we reach here all of the arguments passed the nonnull check. 148 // If 'state' has been updated generated a new node. 149 C.addTransition(state); 150 } 151 152 BugReport *NonNullParamChecker::genReportNullAttrNonNull( 153 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 154 // Lazily allocate the BugType object if it hasn't already been 155 // created. Ownership is transferred to the BugReporter object once 156 // the BugReport is passed to 'EmitWarning'. 157 if (!BTAttrNonNull) 158 BTAttrNonNull.reset(new BugType( 159 "Argument with 'nonnull' attribute passed null", 160 "API")); 161 162 BugReport *R = new BugReport(*BTAttrNonNull, 163 "Null pointer passed as an argument to a 'nonnull' parameter", 164 ErrorNode); 165 if (ArgE) 166 bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); 167 168 return R; 169 } 170 171 BugReport *NonNullParamChecker::genReportReferenceToNullPointer( 172 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 173 if (!BTNullRefArg) 174 BTNullRefArg.reset(new BuiltinBug("Dereference of null pointer")); 175 176 BugReport *R = new BugReport(*BTNullRefArg, 177 "Forming reference to null pointer", 178 ErrorNode); 179 if (ArgE) { 180 const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 181 if (ArgEDeref == 0) 182 ArgEDeref = ArgE; 183 bugreporter::trackNullOrUndefValue(ErrorNode, 184 ArgEDeref, 185 *R); 186 } 187 return R; 188 189 } 190 191 void ento::registerNonNullParamChecker(CheckerManager &mgr) { 192 mgr.registerChecker<NonNullParamChecker>(); 193 } 194