1 //===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // This defines NonNullParamChecker, which checks for arguments expected not to 10 // be null due to: 11 // - the corresponding parameters being declared to have nonnull attribute 12 // - the corresponding parameters being references; since the call would form 13 // a reference to a null pointer 14 // 15 //===----------------------------------------------------------------------===// 16 17 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 18 #include "clang/AST/Attr.h" 19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20 #include "clang/StaticAnalyzer/Core/Checker.h" 21 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 22 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 23 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 24 25 using namespace clang; 26 using namespace ento; 27 28 namespace { 29 class NonNullParamChecker 30 : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > { 31 mutable std::unique_ptr<BugType> BTAttrNonNull; 32 mutable std::unique_ptr<BugType> BTNullRefArg; 33 34 public: 35 36 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 37 38 std::unique_ptr<PathSensitiveBugReport> 39 genReportNullAttrNonNull(const ExplodedNode *ErrorN, 40 const Expr *ArgE, 41 unsigned IdxOfArg) const; 42 std::unique_ptr<PathSensitiveBugReport> 43 genReportReferenceToNullPointer(const ExplodedNode *ErrorN, 44 const Expr *ArgE) const; 45 }; 46 } // end anonymous namespace 47 48 /// \return Bitvector marking non-null attributes. 49 static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { 50 const Decl *FD = Call.getDecl(); 51 unsigned NumArgs = Call.getNumArgs(); 52 llvm::SmallBitVector AttrNonNull(NumArgs); 53 for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { 54 if (!NonNull->args_size()) { 55 AttrNonNull.set(0, NumArgs); 56 break; 57 } 58 for (const ParamIdx &Idx : NonNull->args()) { 59 unsigned IdxAST = Idx.getASTIndex(); 60 if (IdxAST >= NumArgs) 61 continue; 62 AttrNonNull.set(IdxAST); 63 } 64 } 65 return AttrNonNull; 66 } 67 68 void NonNullParamChecker::checkPreCall(const CallEvent &Call, 69 CheckerContext &C) const { 70 if (!Call.getDecl()) 71 return; 72 73 llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call); 74 unsigned NumArgs = Call.getNumArgs(); 75 76 ProgramStateRef state = C.getState(); 77 ArrayRef<ParmVarDecl*> parms = Call.parameters(); 78 79 for (unsigned idx = 0; idx < NumArgs; ++idx) { 80 // For vararg functions, a corresponding parameter decl may not exist. 81 bool HasParam = idx < parms.size(); 82 83 // Check if the parameter is a reference. We want to report when reference 84 // to a null pointer is passed as a parameter. 85 bool haveRefTypeParam = 86 HasParam ? parms[idx]->getType()->isReferenceType() : false; 87 bool haveAttrNonNull = AttrNonNull[idx]; 88 89 // Check if the parameter is also marked 'nonnull'. 90 if (!haveAttrNonNull && HasParam) 91 haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); 92 93 if (!haveAttrNonNull && !haveRefTypeParam) 94 continue; 95 96 // If the value is unknown or undefined, we can't perform this check. 97 const Expr *ArgE = Call.getArgExpr(idx); 98 SVal V = Call.getArgSVal(idx); 99 auto DV = V.getAs<DefinedSVal>(); 100 if (!DV) 101 continue; 102 103 assert(!haveRefTypeParam || DV->getAs<Loc>()); 104 105 // Process the case when the argument is not a location. 106 if (haveAttrNonNull && !DV->getAs<Loc>()) { 107 // If the argument is a union type, we want to handle a potential 108 // transparent_union GCC extension. 109 if (!ArgE) 110 continue; 111 112 QualType T = ArgE->getType(); 113 const RecordType *UT = T->getAsUnionType(); 114 if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) 115 continue; 116 117 auto CSV = DV->getAs<nonloc::CompoundVal>(); 118 119 // FIXME: Handle LazyCompoundVals? 120 if (!CSV) 121 continue; 122 123 V = *(CSV->begin()); 124 DV = V.getAs<DefinedSVal>(); 125 assert(++CSV->begin() == CSV->end()); 126 // FIXME: Handle (some_union){ some_other_union_val }, which turns into 127 // a LazyCompoundVal inside a CompoundVal. 128 if (!V.getAs<Loc>()) 129 continue; 130 131 // Retrieve the corresponding expression. 132 if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) 133 if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer())) 134 ArgE = dyn_cast<Expr>(*(IE->begin())); 135 } 136 137 ConstraintManager &CM = C.getConstraintManager(); 138 ProgramStateRef stateNotNull, stateNull; 139 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 140 141 // Generate an error node. Check for a null node in case 142 // we cache out. 143 if (stateNull && !stateNotNull) { 144 if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { 145 146 std::unique_ptr<BugReport> R; 147 if (haveAttrNonNull) 148 R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1); 149 else if (haveRefTypeParam) 150 R = genReportReferenceToNullPointer(errorNode, ArgE); 151 152 // Highlight the range of the argument that was null. 153 R->addRange(Call.getArgSourceRange(idx)); 154 155 // Emit the bug report. 156 C.emitReport(std::move(R)); 157 } 158 159 // Always return. Either we cached out or we just emitted an error. 160 return; 161 } 162 163 if (stateNull) { 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 state = stateNotNull; 175 } 176 177 // If we reach here all of the arguments passed the nonnull check. 178 // If 'state' has been updated generated a new node. 179 C.addTransition(state); 180 } 181 182 std::unique_ptr<PathSensitiveBugReport> 183 NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, 184 const Expr *ArgE, 185 unsigned IdxOfArg) 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 llvm::SmallString<256> SBuf; 194 llvm::raw_svector_ostream OS(SBuf); 195 OS << "Null pointer passed to " 196 << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg) 197 << " parameter expecting 'nonnull'"; 198 199 auto R = 200 std::make_unique<PathSensitiveBugReport>(*BTAttrNonNull, SBuf, ErrorNode); 201 if (ArgE) 202 bugreporter::trackExpressionValue(ErrorNode, ArgE, *R); 203 204 return R; 205 } 206 207 std::unique_ptr<PathSensitiveBugReport> 208 NonNullParamChecker::genReportReferenceToNullPointer( 209 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 210 if (!BTNullRefArg) 211 BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); 212 213 auto R = std::make_unique<PathSensitiveBugReport>( 214 *BTNullRefArg, "Forming reference to null pointer", ErrorNode); 215 if (ArgE) { 216 const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 217 if (!ArgEDeref) 218 ArgEDeref = ArgE; 219 bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R); 220 } 221 return R; 222 223 } 224 225 void ento::registerNonNullParamChecker(CheckerManager &mgr) { 226 mgr.registerChecker<NonNullParamChecker>(); 227 } 228 229 bool ento::shouldRegisterNonNullParamChecker(const LangOptions &LO) { 230 return true; 231 } 232