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