xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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   const BugType BTAttrNonNull{
35       this, "Argument with 'nonnull' attribute passed null", "API"};
36   const BugType BTNullRefArg{this, "Dereference of null pointer"};
37 
38 public:
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 
50 template <class CallType>
51 void 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 are
58       // 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 to
65       // 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 
74 template <class CallType>
75 void 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 
87 template <class CallType>
88 llvm::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.
99 llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
100   return getNonNullAttrsImpl(Call, Call.getNumArgs());
101 }
102 
103 /// \return Bitvector marking non-null attributes.
104 llvm::SmallBitVector getNonNullAttrs(const AnyCall &Call) {
105   return getNonNullAttrsImpl(Call, Call.param_size());
106 }
107 } // end anonymous namespace
108 
109 void 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 reference
125     // 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 potential
145       // transparent_union GCC extension.
146       if (!ArgE)
147         continue;
148 
149       QualType T = ArgE->getType();
150       const RecordType *UT = T->getAsUnionType();
151       if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
152         continue;
153 
154       auto CSV = DV->getAs<nonloc::CompoundVal>();
155 
156       // FIXME: Handle LazyCompoundVals?
157       if (!CSV)
158         continue;
159 
160       V = *(CSV->begin());
161       DV = V.getAs<DefinedSVal>();
162       assert(++CSV->begin() == CSV->end());
163       // FIXME: Handle (some_union){ some_other_union_val }, which turns into
164       // a LazyCompoundVal inside a CompoundVal.
165       if (!isa<Loc>(V))
166         continue;
167 
168       // Retrieve the corresponding expression.
169       if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
170         if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer()))
171           ArgE = dyn_cast<Expr>(*(IE->begin()));
172     }
173 
174     ConstraintManager &CM = C.getConstraintManager();
175     ProgramStateRef stateNotNull, stateNull;
176     std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
177 
178     // Generate an error node.  Check for a null node in case
179     // we cache out.
180     if (stateNull && !stateNotNull) {
181       if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
182 
183         std::unique_ptr<BugReport> R;
184         if (ExpectedToBeNonNull)
185           R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1);
186         else if (HasRefTypeParam)
187           R = genReportReferenceToNullPointer(errorNode, ArgE);
188 
189         // Highlight the range of the argument that was null.
190         R->addRange(Call.getArgSourceRange(idx));
191 
192         // Emit the bug report.
193         C.emitReport(std::move(R));
194       }
195 
196       // Always return.  Either we cached out or we just emitted an error.
197       return;
198     }
199 
200     if (stateNull) {
201       if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {
202         ImplicitNullDerefEvent event = {
203             V, false, N, &C.getBugReporter(),
204             /*IsDirectDereference=*/HasRefTypeParam};
205         dispatchEvent(event);
206       }
207     }
208 
209     // If a pointer value passed the check we should assume that it is
210     // indeed not null from this point forward.
211     state = stateNotNull;
212   }
213 
214   // If we reach here all of the arguments passed the nonnull check.
215   // If 'state' has been updated generated a new node.
216   C.addTransition(state);
217 }
218 
219 /// We want to trust developer annotations and consider all 'nonnull' parameters
220 /// as non-null indeed. Each marked parameter will get a corresponding
221 /// constraint.
222 ///
223 /// This approach will not only help us to get rid of some false positives, but
224 /// remove duplicates and shorten warning traces as well.
225 ///
226 /// \code
227 ///   void foo(int *x) [[gnu::nonnull]] {
228 ///     // . . .
229 ///     *x = 42;    // we don't want to consider this as an error...
230 ///     // . . .
231 ///   }
232 ///
233 ///   foo(nullptr); // ...and report here instead
234 /// \endcode
235 void NonNullParamChecker::checkBeginFunction(CheckerContext &Context) const {
236   // Planned assumption makes sense only for top-level functions.
237   // Inlined functions will get similar constraints as part of 'checkPreCall'.
238   if (!Context.inTopFrame())
239     return;
240 
241   const LocationContext *LocContext = Context.getLocationContext();
242 
243   const Decl *FD = LocContext->getDecl();
244   // AnyCall helps us here to avoid checking for FunctionDecl and ObjCMethodDecl
245   // separately and aggregates interfaces of these classes.
246   auto AbstractCall = AnyCall::forDecl(FD);
247   if (!AbstractCall)
248     return;
249 
250   ProgramStateRef State = Context.getState();
251   llvm::SmallBitVector ParameterNonNullMarks = getNonNullAttrs(*AbstractCall);
252 
253   for (const ParmVarDecl *Parameter : AbstractCall->parameters()) {
254     // 1. Check parameter if it is annotated as non-null
255     if (!ParameterNonNullMarks.test(Parameter->getFunctionScopeIndex()))
256       continue;
257 
258     // 2. Check that parameter is a pointer.
259     //    Nonnull attribute can be applied to non-pointers (by default
260     //    __attribute__(nonnull) implies "all parameters").
261     if (!Parameter->getType()->isPointerType())
262       continue;
263 
264     Loc ParameterLoc = State->getLValue(Parameter, LocContext);
265     // We never consider top-level function parameters undefined.
266     auto StoredVal =
267         State->getSVal(ParameterLoc).castAs<DefinedOrUnknownSVal>();
268 
269     // 3. Assume that it is indeed non-null
270     if (ProgramStateRef NewState = State->assume(StoredVal, true)) {
271       State = NewState;
272     }
273   }
274 
275   Context.addTransition(State);
276 }
277 
278 std::unique_ptr<PathSensitiveBugReport>
279 NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
280                                               const Expr *ArgE,
281                                               unsigned IdxOfArg) const {
282   llvm::SmallString<256> SBuf;
283   llvm::raw_svector_ostream OS(SBuf);
284   OS << "Null pointer passed to "
285      << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg)
286      << " parameter expecting 'nonnull'";
287 
288   auto R =
289       std::make_unique<PathSensitiveBugReport>(BTAttrNonNull, SBuf, ErrorNode);
290   if (ArgE)
291     bugreporter::trackExpressionValue(ErrorNode, ArgE, *R);
292 
293   return R;
294 }
295 
296 std::unique_ptr<PathSensitiveBugReport>
297 NonNullParamChecker::genReportReferenceToNullPointer(
298     const ExplodedNode *ErrorNode, const Expr *ArgE) const {
299   auto R = std::make_unique<PathSensitiveBugReport>(
300       BTNullRefArg, "Forming reference to null pointer", ErrorNode);
301   if (ArgE) {
302     const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
303     if (!ArgEDeref)
304       ArgEDeref = ArgE;
305     bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R);
306   }
307   return R;
308 }
309 
310 void ento::registerNonNullParamChecker(CheckerManager &mgr) {
311   mgr.registerChecker<NonNullParamChecker>();
312 }
313 
314 bool ento::shouldRegisterNonNullParamChecker(const CheckerManager &mgr) {
315   return true;
316 }
317