xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp (revision 5ca8e32633c4ffbbcd6762e5888b6a4ba0708c6c)
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"
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 
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 || isa<Loc>(*DV));
140 
141     // Process the case when the argument is not a location.
142     if (ExpectedToBeNonNull && !isa<Loc>(*DV)) {
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 (!isa<Loc>(V))
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 
288   llvm::SmallString<256> SBuf;
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 }
327