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 "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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, EventDispatcher<ImplicitNullDerefEvent> > { 32 mutable std::unique_ptr<BugType> BTAttrNonNull; 33 mutable std::unique_ptr<BugType> BTNullRefArg; 34 35 public: 36 37 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 38 39 std::unique_ptr<BugReport> 40 genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const; 41 std::unique_ptr<BugReport> 42 genReportReferenceToNullPointer(const ExplodedNode *ErrorN, 43 const Expr *ArgE) const; 44 }; 45 } // end anonymous namespace 46 47 /// \return Bitvector marking non-null attributes. 48 static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { 49 const Decl *FD = Call.getDecl(); 50 unsigned NumArgs = Call.getNumArgs(); 51 llvm::SmallBitVector AttrNonNull(NumArgs); 52 for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { 53 if (!NonNull->args_size()) { 54 AttrNonNull.set(0, NumArgs); 55 break; 56 } 57 for (const ParamIdx &Idx : NonNull->args()) { 58 unsigned IdxAST = Idx.getASTIndex(); 59 if (IdxAST >= NumArgs) 60 continue; 61 AttrNonNull.set(IdxAST); 62 } 63 } 64 return AttrNonNull; 65 } 66 67 void NonNullParamChecker::checkPreCall(const CallEvent &Call, 68 CheckerContext &C) const { 69 if (!Call.getDecl()) 70 return; 71 72 llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call); 73 unsigned NumArgs = Call.getNumArgs(); 74 75 ProgramStateRef state = C.getState(); 76 ArrayRef<ParmVarDecl*> parms = Call.parameters(); 77 78 for (unsigned idx = 0; idx < NumArgs; ++idx) { 79 // For vararg functions, a corresponding parameter decl may not exist. 80 bool HasParam = idx < parms.size(); 81 82 // Check if the parameter is a reference. We want to report when reference 83 // to a null pointer is passed as a parameter. 84 bool haveRefTypeParam = 85 HasParam ? parms[idx]->getType()->isReferenceType() : false; 86 bool haveAttrNonNull = AttrNonNull[idx]; 87 88 // Check if the parameter is also marked 'nonnull'. 89 if (!haveAttrNonNull && HasParam) 90 haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); 91 92 if (!haveAttrNonNull && !haveRefTypeParam) 93 continue; 94 95 // If the value is unknown or undefined, we can't perform this check. 96 const Expr *ArgE = Call.getArgExpr(idx); 97 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 181 std::unique_ptr<BugReport> 182 NonNullParamChecker::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::trackExpressionValue(ErrorNode, ArgE, *R); 196 197 return R; 198 } 199 200 std::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::trackExpressionValue(ErrorNode, ArgEDeref, *R); 212 } 213 return R; 214 215 } 216 217 void ento::registerNonNullParamChecker(CheckerManager &mgr) { 218 mgr.registerChecker<NonNullParamChecker>(); 219 } 220