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 #include "llvm/ADT/StringExtras.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<PathSensitiveBugReport> 40 genReportNullAttrNonNull(const ExplodedNode *ErrorN, 41 const Expr *ArgE, 42 unsigned IdxOfArg) const; 43 std::unique_ptr<PathSensitiveBugReport> 44 genReportReferenceToNullPointer(const ExplodedNode *ErrorN, 45 const Expr *ArgE) const; 46 }; 47 } // end anonymous namespace 48 49 /// \return Bitvector marking non-null attributes. 50 static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { 51 const Decl *FD = Call.getDecl(); 52 unsigned NumArgs = Call.getNumArgs(); 53 llvm::SmallBitVector AttrNonNull(NumArgs); 54 for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { 55 if (!NonNull->args_size()) { 56 AttrNonNull.set(0, NumArgs); 57 break; 58 } 59 for (const ParamIdx &Idx : NonNull->args()) { 60 unsigned IdxAST = Idx.getASTIndex(); 61 if (IdxAST >= NumArgs) 62 continue; 63 AttrNonNull.set(IdxAST); 64 } 65 } 66 return AttrNonNull; 67 } 68 69 void NonNullParamChecker::checkPreCall(const CallEvent &Call, 70 CheckerContext &C) const { 71 if (!Call.getDecl()) 72 return; 73 74 llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call); 75 unsigned NumArgs = Call.getNumArgs(); 76 77 ProgramStateRef state = C.getState(); 78 ArrayRef<ParmVarDecl*> parms = Call.parameters(); 79 80 for (unsigned idx = 0; idx < NumArgs; ++idx) { 81 // For vararg functions, a corresponding parameter decl may not exist. 82 bool HasParam = idx < parms.size(); 83 84 // Check if the parameter is a reference. We want to report when reference 85 // to a null pointer is passed as a parameter. 86 bool haveRefTypeParam = 87 HasParam ? parms[idx]->getType()->isReferenceType() : false; 88 bool haveAttrNonNull = AttrNonNull[idx]; 89 90 // Check if the parameter is also marked 'nonnull'. 91 if (!haveAttrNonNull && HasParam) 92 haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); 93 94 if (!haveAttrNonNull && !haveRefTypeParam) 95 continue; 96 97 // If the value is unknown or undefined, we can't perform this check. 98 const Expr *ArgE = Call.getArgExpr(idx); 99 SVal V = Call.getArgSVal(idx); 100 auto DV = V.getAs<DefinedSVal>(); 101 if (!DV) 102 continue; 103 104 assert(!haveRefTypeParam || DV->getAs<Loc>()); 105 106 // Process the case when the argument is not a location. 107 if (haveAttrNonNull && !DV->getAs<Loc>()) { 108 // If the argument is a union type, we want to handle a potential 109 // transparent_union GCC extension. 110 if (!ArgE) 111 continue; 112 113 QualType T = ArgE->getType(); 114 const RecordType *UT = T->getAsUnionType(); 115 if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) 116 continue; 117 118 auto CSV = DV->getAs<nonloc::CompoundVal>(); 119 120 // FIXME: Handle LazyCompoundVals? 121 if (!CSV) 122 continue; 123 124 V = *(CSV->begin()); 125 DV = V.getAs<DefinedSVal>(); 126 assert(++CSV->begin() == CSV->end()); 127 // FIXME: Handle (some_union){ some_other_union_val }, which turns into 128 // a LazyCompoundVal inside a CompoundVal. 129 if (!V.getAs<Loc>()) 130 continue; 131 132 // Retrieve the corresponding expression. 133 if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) 134 if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer())) 135 ArgE = dyn_cast<Expr>(*(IE->begin())); 136 } 137 138 ConstraintManager &CM = C.getConstraintManager(); 139 ProgramStateRef stateNotNull, stateNull; 140 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 141 142 // Generate an error node. Check for a null node in case 143 // we cache out. 144 if (stateNull && !stateNotNull) { 145 if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { 146 147 std::unique_ptr<BugReport> R; 148 if (haveAttrNonNull) 149 R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1); 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 (stateNull) { 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 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 183 std::unique_ptr<PathSensitiveBugReport> 184 NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, 185 const Expr *ArgE, 186 unsigned IdxOfArg) 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 llvm::SmallString<256> SBuf; 195 llvm::raw_svector_ostream OS(SBuf); 196 OS << "Null pointer passed to " 197 << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg) 198 << " parameter expecting 'nonnull'"; 199 200 auto R = 201 std::make_unique<PathSensitiveBugReport>(*BTAttrNonNull, SBuf, ErrorNode); 202 if (ArgE) 203 bugreporter::trackExpressionValue(ErrorNode, ArgE, *R); 204 205 return R; 206 } 207 208 std::unique_ptr<PathSensitiveBugReport> 209 NonNullParamChecker::genReportReferenceToNullPointer( 210 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 211 if (!BTNullRefArg) 212 BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); 213 214 auto R = std::make_unique<PathSensitiveBugReport>( 215 *BTNullRefArg, "Forming reference to null pointer", ErrorNode); 216 if (ArgE) { 217 const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 218 if (!ArgEDeref) 219 ArgEDeref = ArgE; 220 bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R); 221 } 222 return R; 223 224 } 225 226 void ento::registerNonNullParamChecker(CheckerManager &mgr) { 227 mgr.registerChecker<NonNullParamChecker>(); 228 } 229 230 bool ento::shouldRegisterNonNullParamChecker(const CheckerManager &mgr) { 231 return true; 232 } 233