318 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This defines NonNullParamChecker, which checks for arguments expected not to10// be null due to:11// - the corresponding parameters being declared to have nonnull attribute12// - the corresponding parameters being references; since the call would form13// a reference to a null pointer14//15//===----------------------------------------------------------------------===//16 17#include "clang/AST/Attr.h"18#include "clang/Analysis/AnyCall.h"19#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.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#include "llvm/ADT/StringExtras.h"26 27using namespace clang;28using namespace ento;29 30namespace {31class NonNullParamChecker32 : public Checker<check::PreCall, check::BeginFunction,33 EventDispatcher<ImplicitNullDerefEvent>> {34 const BugType BTAttrNonNull{35 this, "Argument with 'nonnull' attribute passed null", "API"};36 const BugType BTNullRefArg{this, "Dereference of null pointer"};37 38public:39 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;40 void checkBeginFunction(CheckerContext &C) const;41 42 std::unique_ptr<PathSensitiveBugReport>43 genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE,44 unsigned IdxOfArg) const;45 std::unique_ptr<PathSensitiveBugReport>46 genReportReferenceToNullPointer(const ExplodedNode *ErrorN,47 const Expr *ArgE) const;48};49 50template <class CallType>51void setBitsAccordingToFunctionAttributes(const CallType &Call,52 llvm::SmallBitVector &AttrNonNull) {53 const Decl *FD = Call.getDecl();54 55 for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {56 if (!NonNull->args_size()) {57 // Lack of attribute parameters means that all of the parameters are58 // implicitly marked as non-null.59 AttrNonNull.set();60 break;61 }62 63 for (const ParamIdx &Idx : NonNull->args()) {64 // 'nonnull' attribute's parameters are 1-based and should be adjusted to65 // match actual AST parameter/argument indices.66 unsigned IdxAST = Idx.getASTIndex();67 if (IdxAST >= AttrNonNull.size())68 continue;69 AttrNonNull.set(IdxAST);70 }71 }72}73 74template <class CallType>75void setBitsAccordingToParameterAttributes(const CallType &Call,76 llvm::SmallBitVector &AttrNonNull) {77 for (const ParmVarDecl *Parameter : Call.parameters()) {78 unsigned ParameterIndex = Parameter->getFunctionScopeIndex();79 if (ParameterIndex == AttrNonNull.size())80 break;81 82 if (Parameter->hasAttr<NonNullAttr>())83 AttrNonNull.set(ParameterIndex);84 }85}86 87template <class CallType>88llvm::SmallBitVector getNonNullAttrsImpl(const CallType &Call,89 unsigned ExpectedSize) {90 llvm::SmallBitVector AttrNonNull(ExpectedSize);91 92 setBitsAccordingToFunctionAttributes(Call, AttrNonNull);93 setBitsAccordingToParameterAttributes(Call, AttrNonNull);94 95 return AttrNonNull;96}97 98/// \return Bitvector marking non-null attributes.99llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {100 return getNonNullAttrsImpl(Call, Call.getNumArgs());101}102 103/// \return Bitvector marking non-null attributes.104llvm::SmallBitVector getNonNullAttrs(const AnyCall &Call) {105 return getNonNullAttrsImpl(Call, Call.param_size());106}107} // end anonymous namespace108 109void NonNullParamChecker::checkPreCall(const CallEvent &Call,110 CheckerContext &C) const {111 if (!Call.getDecl())112 return;113 114 llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call);115 unsigned NumArgs = Call.getNumArgs();116 117 ProgramStateRef state = C.getState();118 ArrayRef<ParmVarDecl *> parms = Call.parameters();119 120 for (unsigned idx = 0; idx < NumArgs; ++idx) {121 // For vararg functions, a corresponding parameter decl may not exist.122 bool HasParam = idx < parms.size();123 124 // Check if the parameter is a reference. We want to report when reference125 // to a null pointer is passed as a parameter.126 bool HasRefTypeParam =127 HasParam ? parms[idx]->getType()->isReferenceType() : false;128 bool ExpectedToBeNonNull = AttrNonNull.test(idx);129 130 if (!ExpectedToBeNonNull && !HasRefTypeParam)131 continue;132 133 // If the value is unknown or undefined, we can't perform this check.134 const Expr *ArgE = Call.getArgExpr(idx);135 SVal V = Call.getArgSVal(idx);136 auto DV = V.getAs<DefinedSVal>();137 if (!DV)138 continue;139 140 assert(!HasRefTypeParam || isa<Loc>(*DV));141 142 // Process the case when the argument is not a location.143 if (ExpectedToBeNonNull && !isa<Loc>(*DV)) {144 // If the argument is a union type, we want to handle a potential145 // transparent_union GCC extension.146 if (!ArgE)147 continue;148 149 QualType T = ArgE->getType();150 const RecordType *UT = T->getAsUnionType();151 if (!UT ||152 !UT->getDecl()->getMostRecentDecl()->hasAttr<TransparentUnionAttr>())153 continue;154 155 auto CSV = DV->getAs<nonloc::CompoundVal>();156 157 // FIXME: Handle LazyCompoundVals?158 if (!CSV)159 continue;160 161 V = *(CSV->begin());162 DV = V.getAs<DefinedSVal>();163 assert(++CSV->begin() == CSV->end());164 // FIXME: Handle (some_union){ some_other_union_val }, which turns into165 // a LazyCompoundVal inside a CompoundVal.166 if (!isa<Loc>(V))167 continue;168 169 // Retrieve the corresponding expression.170 if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE))171 if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer()))172 ArgE = dyn_cast<Expr>(*(IE->begin()));173 }174 175 ConstraintManager &CM = C.getConstraintManager();176 ProgramStateRef stateNotNull, stateNull;177 std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);178 179 // Generate an error node. Check for a null node in case180 // we cache out.181 if (stateNull && !stateNotNull) {182 if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {183 184 std::unique_ptr<BugReport> R;185 if (ExpectedToBeNonNull)186 R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1);187 else if (HasRefTypeParam)188 R = genReportReferenceToNullPointer(errorNode, ArgE);189 190 // Highlight the range of the argument that was null.191 R->addRange(Call.getArgSourceRange(idx));192 193 // Emit the bug report.194 C.emitReport(std::move(R));195 }196 197 // Always return. Either we cached out or we just emitted an error.198 return;199 }200 201 if (stateNull) {202 if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {203 ImplicitNullDerefEvent event = {204 V, false, N, &C.getBugReporter(),205 /*IsDirectDereference=*/HasRefTypeParam};206 dispatchEvent(event);207 }208 }209 210 // If a pointer value passed the check we should assume that it is211 // indeed not null from this point forward.212 state = stateNotNull;213 }214 215 // If we reach here all of the arguments passed the nonnull check.216 // If 'state' has been updated generated a new node.217 C.addTransition(state);218}219 220/// We want to trust developer annotations and consider all 'nonnull' parameters221/// as non-null indeed. Each marked parameter will get a corresponding222/// constraint.223///224/// This approach will not only help us to get rid of some false positives, but225/// remove duplicates and shorten warning traces as well.226///227/// \code228/// void foo(int *x) [[gnu::nonnull]] {229/// // . . .230/// *x = 42; // we don't want to consider this as an error...231/// // . . .232/// }233///234/// foo(nullptr); // ...and report here instead235/// \endcode236void NonNullParamChecker::checkBeginFunction(CheckerContext &Context) const {237 // Planned assumption makes sense only for top-level functions.238 // Inlined functions will get similar constraints as part of 'checkPreCall'.239 if (!Context.inTopFrame())240 return;241 242 const LocationContext *LocContext = Context.getLocationContext();243 244 const Decl *FD = LocContext->getDecl();245 // AnyCall helps us here to avoid checking for FunctionDecl and ObjCMethodDecl246 // separately and aggregates interfaces of these classes.247 auto AbstractCall = AnyCall::forDecl(FD);248 if (!AbstractCall)249 return;250 251 ProgramStateRef State = Context.getState();252 llvm::SmallBitVector ParameterNonNullMarks = getNonNullAttrs(*AbstractCall);253 254 for (const ParmVarDecl *Parameter : AbstractCall->parameters()) {255 // 1. Check parameter if it is annotated as non-null256 if (!ParameterNonNullMarks.test(Parameter->getFunctionScopeIndex()))257 continue;258 259 // 2. Check that parameter is a pointer.260 // Nonnull attribute can be applied to non-pointers (by default261 // __attribute__(nonnull) implies "all parameters").262 if (!Parameter->getType()->isPointerType())263 continue;264 265 Loc ParameterLoc = State->getLValue(Parameter, LocContext);266 // We never consider top-level function parameters undefined.267 auto StoredVal =268 State->getSVal(ParameterLoc).castAs<DefinedOrUnknownSVal>();269 270 // 3. Assume that it is indeed non-null271 if (ProgramStateRef NewState = State->assume(StoredVal, true)) {272 State = NewState;273 }274 }275 276 Context.addTransition(State);277}278 279std::unique_ptr<PathSensitiveBugReport>280NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,281 const Expr *ArgE,282 unsigned IdxOfArg) const {283 llvm::SmallString<256> SBuf;284 llvm::raw_svector_ostream OS(SBuf);285 OS << "Null pointer passed to "286 << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg)287 << " parameter expecting 'nonnull'";288 289 auto R =290 std::make_unique<PathSensitiveBugReport>(BTAttrNonNull, SBuf, ErrorNode);291 if (ArgE)292 bugreporter::trackExpressionValue(ErrorNode, ArgE, *R);293 294 return R;295}296 297std::unique_ptr<PathSensitiveBugReport>298NonNullParamChecker::genReportReferenceToNullPointer(299 const ExplodedNode *ErrorNode, const Expr *ArgE) const {300 auto R = std::make_unique<PathSensitiveBugReport>(301 BTNullRefArg, "Forming reference to null pointer", ErrorNode);302 if (ArgE) {303 const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);304 if (!ArgEDeref)305 ArgEDeref = ArgE;306 bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R);307 }308 return R;309}310 311void ento::registerNonNullParamChecker(CheckerManager &mgr) {312 mgr.registerChecker<NonNullParamChecker>();313}314 315bool ento::shouldRegisterNonNullParamChecker(const CheckerManager &mgr) {316 return true;317}318