clang API Documentation
00001 //===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This defines NonNullParamChecker, which checks for arguments expected not to 00011 // be null due to: 00012 // - the corresponding parameters being declared to have nonnull attribute 00013 // - the corresponding parameters being references; since the call would form 00014 // a reference to a null pointer 00015 // 00016 //===----------------------------------------------------------------------===// 00017 00018 #include "ClangSACheckers.h" 00019 #include "clang/AST/Attr.h" 00020 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 00021 #include "clang/StaticAnalyzer/Core/Checker.h" 00022 #include "clang/StaticAnalyzer/Core/CheckerManager.h" 00023 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 00024 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 00025 00026 using namespace clang; 00027 using namespace ento; 00028 00029 namespace { 00030 class NonNullParamChecker 00031 : public Checker< check::PreCall > { 00032 mutable std::unique_ptr<BugType> BTAttrNonNull; 00033 mutable std::unique_ptr<BugType> BTNullRefArg; 00034 00035 public: 00036 00037 void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 00038 00039 BugReport *genReportNullAttrNonNull(const ExplodedNode *ErrorN, 00040 const Expr *ArgE) const; 00041 BugReport *genReportReferenceToNullPointer(const ExplodedNode *ErrorN, 00042 const Expr *ArgE) const; 00043 }; 00044 } // end anonymous namespace 00045 00046 void NonNullParamChecker::checkPreCall(const CallEvent &Call, 00047 CheckerContext &C) const { 00048 const Decl *FD = Call.getDecl(); 00049 if (!FD) 00050 return; 00051 00052 // Merge all non-null attributes 00053 unsigned NumArgs = Call.getNumArgs(); 00054 llvm::SmallBitVector AttrNonNull(NumArgs); 00055 for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { 00056 if (!NonNull->args_size()) { 00057 AttrNonNull.set(0, NumArgs); 00058 break; 00059 } 00060 for (unsigned Val : NonNull->args()) { 00061 if (Val >= NumArgs) 00062 continue; 00063 AttrNonNull.set(Val); 00064 } 00065 } 00066 00067 ProgramStateRef state = C.getState(); 00068 00069 CallEvent::param_type_iterator TyI = Call.param_type_begin(), 00070 TyE = Call.param_type_end(); 00071 00072 for (unsigned idx = 0; idx < NumArgs; ++idx) { 00073 00074 // Check if the parameter is a reference. We want to report when reference 00075 // to a null pointer is passed as a paramter. 00076 bool haveRefTypeParam = false; 00077 if (TyI != TyE) { 00078 haveRefTypeParam = (*TyI)->isReferenceType(); 00079 TyI++; 00080 } 00081 00082 bool haveAttrNonNull = AttrNonNull[idx]; 00083 if (!haveAttrNonNull) { 00084 // Check if the parameter is also marked 'nonnull'. 00085 ArrayRef<ParmVarDecl*> parms = Call.parameters(); 00086 if (idx < parms.size()) 00087 haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); 00088 } 00089 00090 if (!haveRefTypeParam && !haveAttrNonNull) 00091 continue; 00092 00093 // If the value is unknown or undefined, we can't perform this check. 00094 const Expr *ArgE = Call.getArgExpr(idx); 00095 SVal V = Call.getArgSVal(idx); 00096 Optional<DefinedSVal> DV = V.getAs<DefinedSVal>(); 00097 if (!DV) 00098 continue; 00099 00100 // Process the case when the argument is not a location. 00101 assert(!haveRefTypeParam || DV->getAs<Loc>()); 00102 00103 if (haveAttrNonNull && !DV->getAs<Loc>()) { 00104 // If the argument is a union type, we want to handle a potential 00105 // transparent_union GCC extension. 00106 if (!ArgE) 00107 continue; 00108 00109 QualType T = ArgE->getType(); 00110 const RecordType *UT = T->getAsUnionType(); 00111 if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) 00112 continue; 00113 00114 if (Optional<nonloc::CompoundVal> CSV = 00115 DV->getAs<nonloc::CompoundVal>()) { 00116 nonloc::CompoundVal::iterator CSV_I = CSV->begin(); 00117 assert(CSV_I != CSV->end()); 00118 V = *CSV_I; 00119 DV = V.getAs<DefinedSVal>(); 00120 assert(++CSV_I == CSV->end()); 00121 // FIXME: Handle (some_union){ some_other_union_val }, which turns into 00122 // a LazyCompoundVal inside a CompoundVal. 00123 if (!V.getAs<Loc>()) 00124 continue; 00125 // Retrieve the corresponding expression. 00126 if (const CompoundLiteralExpr *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) 00127 if (const InitListExpr *IE = 00128 dyn_cast<InitListExpr>(CE->getInitializer())) 00129 ArgE = dyn_cast<Expr>(*(IE->begin())); 00130 00131 } else { 00132 // FIXME: Handle LazyCompoundVals? 00133 continue; 00134 } 00135 } 00136 00137 ConstraintManager &CM = C.getConstraintManager(); 00138 ProgramStateRef stateNotNull, stateNull; 00139 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); 00140 00141 if (stateNull && !stateNotNull) { 00142 // Generate an error node. Check for a null node in case 00143 // we cache out. 00144 if (ExplodedNode *errorNode = C.generateSink(stateNull)) { 00145 00146 BugReport *R = nullptr; 00147 if (haveAttrNonNull) 00148 R = genReportNullAttrNonNull(errorNode, ArgE); 00149 else if (haveRefTypeParam) 00150 R = genReportReferenceToNullPointer(errorNode, ArgE); 00151 00152 // Highlight the range of the argument that was null. 00153 R->addRange(Call.getArgSourceRange(idx)); 00154 00155 // Emit the bug report. 00156 C.emitReport(R); 00157 } 00158 00159 // Always return. Either we cached out or we just emitted an error. 00160 return; 00161 } 00162 00163 // If a pointer value passed the check we should assume that it is 00164 // indeed not null from this point forward. 00165 assert(stateNotNull); 00166 state = stateNotNull; 00167 } 00168 00169 // If we reach here all of the arguments passed the nonnull check. 00170 // If 'state' has been updated generated a new node. 00171 C.addTransition(state); 00172 } 00173 00174 BugReport *NonNullParamChecker::genReportNullAttrNonNull( 00175 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 00176 // Lazily allocate the BugType object if it hasn't already been 00177 // created. Ownership is transferred to the BugReporter object once 00178 // the BugReport is passed to 'EmitWarning'. 00179 if (!BTAttrNonNull) 00180 BTAttrNonNull.reset(new BugType( 00181 this, "Argument with 'nonnull' attribute passed null", "API")); 00182 00183 BugReport *R = new BugReport(*BTAttrNonNull, 00184 "Null pointer passed as an argument to a 'nonnull' parameter", 00185 ErrorNode); 00186 if (ArgE) 00187 bugreporter::trackNullOrUndefValue(ErrorNode, ArgE, *R); 00188 00189 return R; 00190 } 00191 00192 BugReport *NonNullParamChecker::genReportReferenceToNullPointer( 00193 const ExplodedNode *ErrorNode, const Expr *ArgE) const { 00194 if (!BTNullRefArg) 00195 BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); 00196 00197 BugReport *R = new BugReport(*BTNullRefArg, 00198 "Forming reference to null pointer", 00199 ErrorNode); 00200 if (ArgE) { 00201 const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); 00202 if (!ArgEDeref) 00203 ArgEDeref = ArgE; 00204 bugreporter::trackNullOrUndefValue(ErrorNode, 00205 ArgEDeref, 00206 *R); 00207 } 00208 return R; 00209 00210 } 00211 00212 void ento::registerNonNullParamChecker(CheckerManager &mgr) { 00213 mgr.registerChecker<NonNullParamChecker>(); 00214 }