clang  14.0.0git
NonNullParamChecker.cpp
Go to the documentation of this file.
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/AST/Attr.h"
18 #include "clang/Analysis/AnyCall.h"
25 #include "llvm/ADT/StringExtras.h"
26 
27 using namespace clang;
28 using namespace ento;
29 
30 namespace {
31 class NonNullParamChecker
32  : public Checker<check::PreCall, check::BeginFunction,
33  EventDispatcher<ImplicitNullDerefEvent>> {
34  mutable std::unique_ptr<BugType> BTAttrNonNull;
35  mutable std::unique_ptr<BugType> BTNullRefArg;
36 
37 public:
38  void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
39  void checkBeginFunction(CheckerContext &C) const;
40 
41  std::unique_ptr<PathSensitiveBugReport>
42  genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE,
43  unsigned IdxOfArg) const;
44  std::unique_ptr<PathSensitiveBugReport>
45  genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
46  const Expr *ArgE) const;
47 };
48 
49 template <class CallType>
50 void setBitsAccordingToFunctionAttributes(const CallType &Call,
51  llvm::SmallBitVector &AttrNonNull) {
52  const Decl *FD = Call.getDecl();
53 
54  for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
55  if (!NonNull->args_size()) {
56  // Lack of attribute parameters means that all of the parameters are
57  // implicitly marked as non-null.
58  AttrNonNull.set();
59  break;
60  }
61 
62  for (const ParamIdx &Idx : NonNull->args()) {
63  // 'nonnull' attribute's parameters are 1-based and should be adjusted to
64  // match actual AST parameter/argument indices.
65  unsigned IdxAST = Idx.getASTIndex();
66  if (IdxAST >= AttrNonNull.size())
67  continue;
68  AttrNonNull.set(IdxAST);
69  }
70  }
71 }
72 
73 template <class CallType>
74 void setBitsAccordingToParameterAttributes(const CallType &Call,
75  llvm::SmallBitVector &AttrNonNull) {
76  for (const ParmVarDecl *Parameter : Call.parameters()) {
77  unsigned ParameterIndex = Parameter->getFunctionScopeIndex();
78  if (ParameterIndex == AttrNonNull.size())
79  break;
80 
81  if (Parameter->hasAttr<NonNullAttr>())
82  AttrNonNull.set(ParameterIndex);
83  }
84 }
85 
86 template <class CallType>
87 llvm::SmallBitVector getNonNullAttrsImpl(const CallType &Call,
88  unsigned ExpectedSize) {
89  llvm::SmallBitVector AttrNonNull(ExpectedSize);
90 
91  setBitsAccordingToFunctionAttributes(Call, AttrNonNull);
92  setBitsAccordingToParameterAttributes(Call, AttrNonNull);
93 
94  return AttrNonNull;
95 }
96 
97 /// \return Bitvector marking non-null attributes.
98 llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
99  return getNonNullAttrsImpl(Call, Call.getNumArgs());
100 }
101 
102 /// \return Bitvector marking non-null attributes.
103 llvm::SmallBitVector getNonNullAttrs(const AnyCall &Call) {
104  return getNonNullAttrsImpl(Call, Call.param_size());
105 }
106 } // end anonymous namespace
107 
108 void NonNullParamChecker::checkPreCall(const CallEvent &Call,
109  CheckerContext &C) const {
110  if (!Call.getDecl())
111  return;
112 
113  llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call);
114  unsigned NumArgs = Call.getNumArgs();
115 
116  ProgramStateRef state = C.getState();
117  ArrayRef<ParmVarDecl *> parms = Call.parameters();
118 
119  for (unsigned idx = 0; idx < NumArgs; ++idx) {
120  // For vararg functions, a corresponding parameter decl may not exist.
121  bool HasParam = idx < parms.size();
122 
123  // Check if the parameter is a reference. We want to report when reference
124  // to a null pointer is passed as a parameter.
125  bool HasRefTypeParam =
126  HasParam ? parms[idx]->getType()->isReferenceType() : false;
127  bool ExpectedToBeNonNull = AttrNonNull.test(idx);
128 
129  if (!ExpectedToBeNonNull && !HasRefTypeParam)
130  continue;
131 
132  // If the value is unknown or undefined, we can't perform this check.
133  const Expr *ArgE = Call.getArgExpr(idx);
134  SVal V = Call.getArgSVal(idx);
135  auto DV = V.getAs<DefinedSVal>();
136  if (!DV)
137  continue;
138 
139  assert(!HasRefTypeParam || DV->getAs<Loc>());
140 
141  // Process the case when the argument is not a location.
142  if (ExpectedToBeNonNull && !DV->getAs<Loc>()) {
143  // If the argument is a union type, we want to handle a potential
144  // transparent_union GCC extension.
145  if (!ArgE)
146  continue;
147 
148  QualType T = ArgE->getType();
149  const RecordType *UT = T->getAsUnionType();
150  if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
151  continue;
152 
153  auto CSV = DV->getAs<nonloc::CompoundVal>();
154 
155  // FIXME: Handle LazyCompoundVals?
156  if (!CSV)
157  continue;
158 
159  V = *(CSV->begin());
160  DV = V.getAs<DefinedSVal>();
161  assert(++CSV->begin() == CSV->end());
162  // FIXME: Handle (some_union){ some_other_union_val }, which turns into
163  // a LazyCompoundVal inside a CompoundVal.
164  if (!V.getAs<Loc>())
165  continue;
166 
167  // Retrieve the corresponding expression.
168  if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
169  if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer()))
170  ArgE = dyn_cast<Expr>(*(IE->begin()));
171  }
172 
173  ConstraintManager &CM = C.getConstraintManager();
174  ProgramStateRef stateNotNull, stateNull;
175  std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
176 
177  // Generate an error node. Check for a null node in case
178  // we cache out.
179  if (stateNull && !stateNotNull) {
180  if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
181 
182  std::unique_ptr<BugReport> R;
183  if (ExpectedToBeNonNull)
184  R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1);
185  else if (HasRefTypeParam)
186  R = genReportReferenceToNullPointer(errorNode, ArgE);
187 
188  // Highlight the range of the argument that was null.
189  R->addRange(Call.getArgSourceRange(idx));
190 
191  // Emit the bug report.
192  C.emitReport(std::move(R));
193  }
194 
195  // Always return. Either we cached out or we just emitted an error.
196  return;
197  }
198 
199  if (stateNull) {
200  if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {
201  ImplicitNullDerefEvent event = {
202  V, false, N, &C.getBugReporter(),
203  /*IsDirectDereference=*/HasRefTypeParam};
204  dispatchEvent(event);
205  }
206  }
207 
208  // If a pointer value passed the check we should assume that it is
209  // indeed not null from this point forward.
210  state = stateNotNull;
211  }
212 
213  // If we reach here all of the arguments passed the nonnull check.
214  // If 'state' has been updated generated a new node.
215  C.addTransition(state);
216 }
217 
218 /// We want to trust developer annotations and consider all 'nonnull' parameters
219 /// as non-null indeed. Each marked parameter will get a corresponding
220 /// constraint.
221 ///
222 /// This approach will not only help us to get rid of some false positives, but
223 /// remove duplicates and shorten warning traces as well.
224 ///
225 /// \code
226 /// void foo(int *x) [[gnu::nonnull]] {
227 /// // . . .
228 /// *x = 42; // we don't want to consider this as an error...
229 /// // . . .
230 /// }
231 ///
232 /// foo(nullptr); // ...and report here instead
233 /// \endcode
234 void NonNullParamChecker::checkBeginFunction(CheckerContext &Context) const {
235  // Planned assumption makes sense only for top-level functions.
236  // Inlined functions will get similar constraints as part of 'checkPreCall'.
237  if (!Context.inTopFrame())
238  return;
239 
240  const LocationContext *LocContext = Context.getLocationContext();
241 
242  const Decl *FD = LocContext->getDecl();
243  // AnyCall helps us here to avoid checking for FunctionDecl and ObjCMethodDecl
244  // separately and aggregates interfaces of these classes.
245  auto AbstractCall = AnyCall::forDecl(FD);
246  if (!AbstractCall)
247  return;
248 
249  ProgramStateRef State = Context.getState();
250  llvm::SmallBitVector ParameterNonNullMarks = getNonNullAttrs(*AbstractCall);
251 
252  for (const ParmVarDecl *Parameter : AbstractCall->parameters()) {
253  // 1. Check parameter if it is annotated as non-null
254  if (!ParameterNonNullMarks.test(Parameter->getFunctionScopeIndex()))
255  continue;
256 
257  // 2. Check that parameter is a pointer.
258  // Nonnull attribute can be applied to non-pointers (by default
259  // __attribute__(nonnull) implies "all parameters").
260  if (!Parameter->getType()->isPointerType())
261  continue;
262 
263  Loc ParameterLoc = State->getLValue(Parameter, LocContext);
264  // We never consider top-level function parameters undefined.
265  auto StoredVal =
266  State->getSVal(ParameterLoc).castAs<DefinedOrUnknownSVal>();
267 
268  // 3. Assume that it is indeed non-null
269  if (ProgramStateRef NewState = State->assume(StoredVal, true)) {
270  State = NewState;
271  }
272  }
273 
274  Context.addTransition(State);
275 }
276 
277 std::unique_ptr<PathSensitiveBugReport>
278 NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
279  const Expr *ArgE,
280  unsigned IdxOfArg) const {
281  // Lazily allocate the BugType object if it hasn't already been
282  // created. Ownership is transferred to the BugReporter object once
283  // the BugReport is passed to 'EmitWarning'.
284  if (!BTAttrNonNull)
285  BTAttrNonNull.reset(new BugType(
286  this, "Argument with 'nonnull' attribute passed null", "API"));
287 
289  llvm::raw_svector_ostream OS(SBuf);
290  OS << "Null pointer passed to "
291  << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg)
292  << " parameter expecting 'nonnull'";
293 
294  auto R =
295  std::make_unique<PathSensitiveBugReport>(*BTAttrNonNull, SBuf, ErrorNode);
296  if (ArgE)
297  bugreporter::trackExpressionValue(ErrorNode, ArgE, *R);
298 
299  return R;
300 }
301 
302 std::unique_ptr<PathSensitiveBugReport>
303 NonNullParamChecker::genReportReferenceToNullPointer(
304  const ExplodedNode *ErrorNode, const Expr *ArgE) const {
305  if (!BTNullRefArg)
306  BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
307 
308  auto R = std::make_unique<PathSensitiveBugReport>(
309  *BTNullRefArg, "Forming reference to null pointer", ErrorNode);
310  if (ArgE) {
311  const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
312  if (!ArgEDeref)
313  ArgEDeref = ArgE;
314  bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R);
315  }
316  return R;
317 
318 }
319 
320 void ento::registerNonNullParamChecker(CheckerManager &mgr) {
321  mgr.registerChecker<NonNullParamChecker>();
322 }
323 
324 bool ento::shouldRegisterNonNullParamChecker(const CheckerManager &mgr) {
325  return true;
326 }
clang::LocationContext
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
Definition: AnalysisDeclContext.h:215
clang::ObjCSubstitutionContext::Parameter
@ Parameter
The parameter type of a method or function.
clang::Decl::hasAttr
bool hasAttr() const
Definition: DeclBase.h:547
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
clang::QualType
A (possibly-)qualified type.
Definition: Type.h:673
Attr.h
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:54
clang::AnyCall::forDecl
static Optional< AnyCall > forDecl(const Decl *D)
If D is a callable (Objective-C method or a function), return a constructed AnyCall object.
Definition: AnyCall.h:133
clang::ParmVarDecl
Represents a parameter to a function.
Definition: Decl.h:1665
clang::index::SymbolRole::Call
@ Call
CallEvent.h
AnyCall.h
V
#define V(N, I)
Definition: ASTContext.h:3121
clang::RecordType
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
Definition: Type.h:4613
BuiltinCheckerRegistration.h
clang::AnyCall
An instance of this class corresponds to a call.
Definition: AnyCall.h:25
clang::Decl::specific_attrs
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
Definition: DeclBase.h:529
CheckerManager.h
clang::ParamIdx
A single parameter index whose accessors require each use to make explicit the parameter index encodi...
Definition: Attr.h:232
clang::Type::getAs
const T * getAs() const
Member-template getAs<specific type>'.
Definition: Type.h:7162
llvm::SmallString
Definition: LLVM.h:37
state
and static some checkers Checker The latter are built on top of the former via the Checker and CheckerVisitor and attempts to isolate them from much of the gore of the internal analysis the analyzer is basically a source code simulator that traces out possible paths of execution The state of the and the combination of state and program point is a node in an exploded which has the entry program point and initial state
Definition: README.txt:30
clang::ento::bugreporter::trackExpressionValue
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
clang::ento::bugreporter::getDerefExpr
const Expr * getDerefExpr(const Stmt *S)
clang::Type::getAsUnionType
const RecordType * getAsUnionType() const
NOTE: getAs*ArrayType are methods on ASTContext.
Definition: Type.cpp:660
BugType.h
llvm::ArrayRef
Definition: LLVM.h:34
clang::Decl
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:89
State
LineState State
Definition: UnwrappedLineFormatter.cpp:986
CheckerContext.h
Checker.h
clang
Definition: CalledOnceCheck.h:17
clang::Expr::getType
QualType getType() const
Definition: Expr.h:141
clang::RecordType::getDecl
RecordDecl * getDecl() const
Definition: Type.h:4623
clang::Expr
This represents one expression.
Definition: Expr.h:109
clang::NullabilityKind::NonNull
@ NonNull
Values of this type can never be null.
clang::LocationContext::getDecl
const Decl * getDecl() const
Definition: AnalysisDeclContext.h:247
clang::ento::ObjKind::OS
@ OS
Indicates that the tracking object is a descendant of a referenced-counted OSObject,...