NonNullParamChecker.cpp revision 288943
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 26using namespace clang; 27using namespace ento; 28 29namespace { 30class NonNullParamChecker 31 : public Checker< check::PreCall > { 32 mutable std::unique_ptr<BugType> BTAttrNonNull; 33 mutable std::unique_ptr<BugType> BTNullRefArg; 34 35public: 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 47void 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 (unsigned Val : NonNull->args()) { 62 if (Val >= NumArgs) 63 continue; 64 AttrNonNull.set(Val); 65 } 66 } 67 68 ProgramStateRef state = C.getState(); 69 70 CallEvent::param_type_iterator TyI = Call.param_type_begin(), 71 TyE = Call.param_type_end(); 72 73 for (unsigned idx = 0; idx < NumArgs; ++idx) { 74 75 // Check if the parameter is a reference. We want to report when reference 76 // to a null pointer is passed as a paramter. 77 bool haveRefTypeParam = false; 78 if (TyI != TyE) { 79 haveRefTypeParam = (*TyI)->isReferenceType(); 80 TyI++; 81 } 82 83 bool haveAttrNonNull = AttrNonNull[idx]; 84 if (!haveAttrNonNull) { 85 // Check if the parameter is also marked 'nonnull'. 86 ArrayRef<ParmVarDecl*> parms = Call.parameters(); 87 if (idx < parms.size()) 88 haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); 89 } 90 91 if (!haveRefTypeParam && !haveAttrNonNull) 92 continue; 93 94 // If the value is unknown or undefined, we can't perform this check. 95 const Expr *ArgE = Call.getArgExpr(idx); 96 SVal V = Call.getArgSVal(idx); 97 Optional<DefinedSVal> DV = V.getAs<DefinedSVal>(); 98 if (!DV) 99 continue; 100 101 // Process the case when the argument is not a location. 102 assert(!haveRefTypeParam || DV->getAs<Loc>()); 103 104 if (haveAttrNonNull && !DV->getAs<Loc>()) { 105 // If the argument is a union type, we want to handle a potential 106 // transparent_union GCC extension. 107 if (!ArgE) 108 continue; 109 110 QualType T = ArgE->getType(); 111 const RecordType *UT = T->getAsUnionType(); 112 if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) 113 continue; 114 115 if (Optional<nonloc::CompoundVal> CSV = 116 DV->getAs<nonloc::CompoundVal>()) { 117 nonloc::CompoundVal::iterator CSV_I = CSV->begin(); 118 assert(CSV_I != CSV->end()); 119 V = *CSV_I; 120 DV = V.getAs<DefinedSVal>(); 121 assert(++CSV_I == CSV->end()); 122 // FIXME: Handle (some_union){ some_other_union_val }, which turns into 123 // a LazyCompoundVal inside a CompoundVal. 124 if (!V.getAs<Loc>()) 125 continue; 126 // Retrieve the corresponding expression. 127 if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) 128 if (const InitListExpr *IE = 129 dyn_cast<InitListExpr>(CE->getInitializer())) 130 ArgE = dyn_cast<Expr>(*(IE->begin())); 131 132 } else { 133 // FIXME: Handle LazyCompoundVals? 134 continue; 135 } 136 } 137 138 ConstraintManager &CM = C.getConstraintManager(); 139 ProgramStateRef stateNotNull, stateNull; 140 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 141 142 if (stateNull && !stateNotNull) { 143 // Generate an error node. Check for a null node in case 144 // we cache out. 145 if (ExplodedNode *errorNode = C.generateSink(stateNull)) { 146 147 std::unique_ptr<BugReport> R; 148 if (haveAttrNonNull) 149 R = genReportNullAttrNonNull(errorNode, ArgE); 150 else if (haveRefTypeParam) 151 R = genReportReferenceToNullPointer(errorNode, ArgE); 152 153 // Highlight the range of the argument that was null. 154 R->addRange(Call.getArgSourceRange(idx)); 155 156 // Emit the bug report. 157 C.emitReport(std::move(R)); 158 } 159 160 // Always return. Either we cached out or we just emitted an error. 161 return; 162 } 163 164 // If a pointer value passed the check we should assume that it is 165 // indeed not null from this point forward. 166 assert(stateNotNull); 167 state = stateNotNull; 168 } 169 170 // If we reach here all of the arguments passed the nonnull check. 171 // If 'state' has been updated generated a new node. 172 C.addTransition(state); 173} 174 175std::unique_ptr<BugReport> 176NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, 177 const Expr *ArgE) const { 178 // Lazily allocate the BugType object if it hasn't already been 179 // created. Ownership is transferred to the BugReporter object once 180 // the BugReport is passed to 'EmitWarning'. 181 if (!BTAttrNonNull) 182 BTAttrNonNull.reset(new BugType( 183 this, "Argument with 'nonnull' attribute passed null", "API")); 184 185 auto R = llvm::make_unique<BugReport>( 186 *BTAttrNonNull, 187 "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode); 188 if (ArgE) 189 bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); 190 191 return R; 192} 193 194std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer( 195 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 196 if (!BTNullRefArg) 197 BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); 198 199 auto R = llvm::make_unique<BugReport>( 200 *BTNullRefArg, "Forming reference to null pointer", ErrorNode); 201 if (ArgE) { 202 const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 203 if (!ArgEDeref) 204 ArgEDeref = ArgE; 205 bugreporter::trackNullOrUndefValue(ErrorNode, 206 ArgEDeref, 207 *R); 208 } 209 return R; 210 211} 212 213void ento::registerNonNullParamChecker(CheckerManager &mgr) { 214 mgr.registerChecker<NonNullParamChecker>(); 215} 216