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, EventDispatcher<ImplicitNullDerefEvent> > { 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) { 143 if (!stateNotNull) { 144 // Generate an error node. Check for a null node in case 145 // we cache out. 146 if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { 147 148 std::unique_ptr<BugReport> R; 149 if (haveAttrNonNull) 150 R = genReportNullAttrNonNull(errorNode, ArgE); 151 else if (haveRefTypeParam) 152 R = genReportReferenceToNullPointer(errorNode, ArgE); 153 154 // Highlight the range of the argument that was null. 155 R->addRange(Call.getArgSourceRange(idx)); 156 157 // Emit the bug report. 158 C.emitReport(std::move(R)); 159 } 160 161 // Always return. Either we cached out or we just emitted an error. 162 return; 163 } 164 if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { 165 ImplicitNullDerefEvent event = { 166 V, false, N, &C.getBugReporter(), 167 /*IsDirectDereference=*/haveRefTypeParam}; 168 dispatchEvent(event); 169 } 170 } 171 172 // If a pointer value passed the check we should assume that it is 173 // indeed not null from this point forward. 174 assert(stateNotNull); 175 state = stateNotNull; 176 } 177 178 // If we reach here all of the arguments passed the nonnull check. 179 // If 'state' has been updated generated a new node. 180 C.addTransition(state); 181} 182 183std::unique_ptr<BugReport> 184NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, 185 const Expr *ArgE) const { 186 // Lazily allocate the BugType object if it hasn't already been 187 // created. Ownership is transferred to the BugReporter object once 188 // the BugReport is passed to 'EmitWarning'. 189 if (!BTAttrNonNull) 190 BTAttrNonNull.reset(new BugType( 191 this, "Argument with 'nonnull' attribute passed null", "API")); 192 193 auto R = llvm::make_unique<BugReport>( 194 *BTAttrNonNull, 195 "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode); 196 if (ArgE) 197 bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); 198 199 return R; 200} 201 202std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer( 203 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 204 if (!BTNullRefArg) 205 BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); 206 207 auto R = llvm::make_unique<BugReport>( 208 *BTNullRefArg, "Forming reference to null pointer", ErrorNode); 209 if (ArgE) { 210 const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 211 if (!ArgEDeref) 212 ArgEDeref = ArgE; 213 bugreporter::trackNullOrUndefValue(ErrorNode, 214 ArgEDeref, 215 *R); 216 } 217 return R; 218 219} 220 221void ento::registerNonNullParamChecker(CheckerManager &mgr) { 222 mgr.registerChecker<NonNullParamChecker>(); 223} 224