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, 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 void NonNullParamChecker::checkPreCall(const CallEvent &Call, 48 CheckerContext &C) const { 49 const Decl *FD = Call.getDecl(); 50 if (!FD) 51 return; 52 53 // Merge all non-null attributes 54 unsigned NumArgs = Call.getNumArgs(); 55 llvm::SmallBitVector AttrNonNull(NumArgs); 56 for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { 57 if (!NonNull->args_size()) { 58 AttrNonNull.set(0, NumArgs); 59 break; 60 } 61 for (const ParamIdx &Idx : NonNull->args()) { 62 unsigned IdxAST = Idx.getASTIndex(); 63 if (IdxAST >= NumArgs) 64 continue; 65 AttrNonNull.set(IdxAST); 66 } 67 } 68 69 ProgramStateRef state = C.getState(); 70 71 CallEvent::param_type_iterator TyI = Call.param_type_begin(), 72 TyE = Call.param_type_end(); 73 74 for (unsigned idx = 0; idx < NumArgs; ++idx) { 75 76 // Check if the parameter is a reference. We want to report when reference 77 // to a null pointer is passed as a parameter. 78 bool haveRefTypeParam = false; 79 if (TyI != TyE) { 80 haveRefTypeParam = (*TyI)->isReferenceType(); 81 TyI++; 82 } 83 84 bool haveAttrNonNull = AttrNonNull[idx]; 85 if (!haveAttrNonNull) { 86 // Check if the parameter is also marked 'nonnull'. 87 ArrayRef<ParmVarDecl*> parms = Call.parameters(); 88 if (idx < parms.size()) 89 haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); 90 } 91 92 if (!haveRefTypeParam && !haveAttrNonNull) 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 Optional<DefinedSVal> DV = V.getAs<DefinedSVal>(); 99 if (!DV) 100 continue; 101 102 // Process the case when the argument is not a location. 103 assert(!haveRefTypeParam || DV->getAs<Loc>()); 104 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 if (Optional<nonloc::CompoundVal> CSV = 117 DV->getAs<nonloc::CompoundVal>()) { 118 nonloc::CompoundVal::iterator CSV_I = CSV->begin(); 119 assert(CSV_I != CSV->end()); 120 V = *CSV_I; 121 DV = V.getAs<DefinedSVal>(); 122 assert(++CSV_I == CSV->end()); 123 // FIXME: Handle (some_union){ some_other_union_val }, which turns into 124 // a LazyCompoundVal inside a CompoundVal. 125 if (!V.getAs<Loc>()) 126 continue; 127 // Retrieve the corresponding expression. 128 if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) 129 if (const InitListExpr *IE = 130 dyn_cast<InitListExpr>(CE->getInitializer())) 131 ArgE = dyn_cast<Expr>(*(IE->begin())); 132 133 } else { 134 // FIXME: Handle LazyCompoundVals? 135 continue; 136 } 137 } 138 139 ConstraintManager &CM = C.getConstraintManager(); 140 ProgramStateRef stateNotNull, stateNull; 141 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 142 143 if (stateNull) { 144 if (!stateNotNull) { 145 // Generate an error node. Check for a null node in case 146 // we cache out. 147 if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { 148 149 std::unique_ptr<BugReport> R; 150 if (haveAttrNonNull) 151 R = genReportNullAttrNonNull(errorNode, ArgE); 152 else if (haveRefTypeParam) 153 R = genReportReferenceToNullPointer(errorNode, ArgE); 154 155 // Highlight the range of the argument that was null. 156 R->addRange(Call.getArgSourceRange(idx)); 157 158 // Emit the bug report. 159 C.emitReport(std::move(R)); 160 } 161 162 // Always return. Either we cached out or we just emitted an error. 163 return; 164 } 165 if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { 166 ImplicitNullDerefEvent event = { 167 V, false, N, &C.getBugReporter(), 168 /*IsDirectDereference=*/haveRefTypeParam}; 169 dispatchEvent(event); 170 } 171 } 172 173 // If a pointer value passed the check we should assume that it is 174 // indeed not null from this point forward. 175 assert(stateNotNull); 176 state = stateNotNull; 177 } 178 179 // If we reach here all of the arguments passed the nonnull check. 180 // If 'state' has been updated generated a new node. 181 C.addTransition(state); 182 } 183 184 std::unique_ptr<BugReport> 185 NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, 186 const Expr *ArgE) const { 187 // Lazily allocate the BugType object if it hasn't already been 188 // created. Ownership is transferred to the BugReporter object once 189 // the BugReport is passed to 'EmitWarning'. 190 if (!BTAttrNonNull) 191 BTAttrNonNull.reset(new BugType( 192 this, "Argument with 'nonnull' attribute passed null", "API")); 193 194 auto R = llvm::make_unique<BugReport>( 195 *BTAttrNonNull, 196 "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode); 197 if (ArgE) 198 bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); 199 200 return R; 201 } 202 203 std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer( 204 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 205 if (!BTNullRefArg) 206 BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); 207 208 auto R = llvm::make_unique<BugReport>( 209 *BTNullRefArg, "Forming reference to null pointer", ErrorNode); 210 if (ArgE) { 211 const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 212 if (!ArgEDeref) 213 ArgEDeref = ArgE; 214 bugreporter::trackNullOrUndefValue(ErrorNode, 215 ArgEDeref, 216 *R); 217 } 218 return R; 219 220 } 221 222 void ento::registerNonNullParamChecker(CheckerManager &mgr) { 223 mgr.registerChecker<NonNullParamChecker>(); 224 } 225