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