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/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18 #include "clang/AST/Attr.h"
19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20 #include "clang/StaticAnalyzer/Core/Checker.h"
21 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24 
25 using namespace clang;
26 using namespace ento;
27 
28 namespace {
29 class NonNullParamChecker
30   : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > {
31   mutable std::unique_ptr<BugType> BTAttrNonNull;
32   mutable std::unique_ptr<BugType> BTNullRefArg;
33 
34 public:
35 
36   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
37 
38   std::unique_ptr<PathSensitiveBugReport>
39   genReportNullAttrNonNull(const ExplodedNode *ErrorN,
40                            const Expr *ArgE,
41                            unsigned IdxOfArg) const;
42   std::unique_ptr<PathSensitiveBugReport>
43   genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
44                                   const Expr *ArgE) const;
45 };
46 } // end anonymous namespace
47 
48 /// \return Bitvector marking non-null attributes.
49 static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
50   const Decl *FD = Call.getDecl();
51   unsigned NumArgs = Call.getNumArgs();
52   llvm::SmallBitVector AttrNonNull(NumArgs);
53   for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
54     if (!NonNull->args_size()) {
55       AttrNonNull.set(0, NumArgs);
56       break;
57     }
58     for (const ParamIdx &Idx : NonNull->args()) {
59       unsigned IdxAST = Idx.getASTIndex();
60       if (IdxAST >= NumArgs)
61         continue;
62       AttrNonNull.set(IdxAST);
63     }
64   }
65   return AttrNonNull;
66 }
67 
68 void NonNullParamChecker::checkPreCall(const CallEvent &Call,
69                                        CheckerContext &C) const {
70   if (!Call.getDecl())
71     return;
72 
73   llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call);
74   unsigned NumArgs = Call.getNumArgs();
75 
76   ProgramStateRef state = C.getState();
77   ArrayRef<ParmVarDecl*> parms = Call.parameters();
78 
79   for (unsigned idx = 0; idx < NumArgs; ++idx) {
80     // For vararg functions, a corresponding parameter decl may not exist.
81     bool HasParam = idx < parms.size();
82 
83     // Check if the parameter is a reference. We want to report when reference
84     // to a null pointer is passed as a parameter.
85     bool haveRefTypeParam =
86         HasParam ? parms[idx]->getType()->isReferenceType() : false;
87     bool haveAttrNonNull = AttrNonNull[idx];
88 
89     // Check if the parameter is also marked 'nonnull'.
90     if (!haveAttrNonNull && HasParam)
91       haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
92 
93     if (!haveAttrNonNull && !haveRefTypeParam)
94       continue;
95 
96     // If the value is unknown or undefined, we can't perform this check.
97     const Expr *ArgE = Call.getArgExpr(idx);
98     SVal V = Call.getArgSVal(idx);
99     auto DV = V.getAs<DefinedSVal>();
100     if (!DV)
101       continue;
102 
103     assert(!haveRefTypeParam || DV->getAs<Loc>());
104 
105     // Process the case when the argument is not a location.
106     if (haveAttrNonNull && !DV->getAs<Loc>()) {
107       // If the argument is a union type, we want to handle a potential
108       // transparent_union GCC extension.
109       if (!ArgE)
110         continue;
111 
112       QualType T = ArgE->getType();
113       const RecordType *UT = T->getAsUnionType();
114       if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
115         continue;
116 
117       auto CSV = DV->getAs<nonloc::CompoundVal>();
118 
119       // FIXME: Handle LazyCompoundVals?
120       if (!CSV)
121         continue;
122 
123       V = *(CSV->begin());
124       DV = V.getAs<DefinedSVal>();
125       assert(++CSV->begin() == CSV->end());
126       // FIXME: Handle (some_union){ some_other_union_val }, which turns into
127       // a LazyCompoundVal inside a CompoundVal.
128       if (!V.getAs<Loc>())
129         continue;
130 
131       // Retrieve the corresponding expression.
132       if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
133         if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer()))
134           ArgE = dyn_cast<Expr>(*(IE->begin()));
135     }
136 
137     ConstraintManager &CM = C.getConstraintManager();
138     ProgramStateRef stateNotNull, stateNull;
139     std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
140 
141     // Generate an error node.  Check for a null node in case
142     // we cache out.
143     if (stateNull && !stateNotNull) {
144       if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
145 
146         std::unique_ptr<BugReport> R;
147         if (haveAttrNonNull)
148           R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1);
149         else if (haveRefTypeParam)
150           R = genReportReferenceToNullPointer(errorNode, ArgE);
151 
152         // Highlight the range of the argument that was null.
153         R->addRange(Call.getArgSourceRange(idx));
154 
155         // Emit the bug report.
156         C.emitReport(std::move(R));
157       }
158 
159       // Always return.  Either we cached out or we just emitted an error.
160       return;
161     }
162 
163     if (stateNull) {
164       if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {
165         ImplicitNullDerefEvent event = {
166           V, false, N, &C.getBugReporter(),
167           /*IsDirectDereference=*/haveRefTypeParam};
168         dispatchEvent(event);
169       }
170     }
171 
172     // If a pointer value passed the check we should assume that it is
173     // indeed not null from this point forward.
174     state = stateNotNull;
175   }
176 
177   // If we reach here all of the arguments passed the nonnull check.
178   // If 'state' has been updated generated a new node.
179   C.addTransition(state);
180 }
181 
182 std::unique_ptr<PathSensitiveBugReport>
183 NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
184                                               const Expr *ArgE,
185                                               unsigned IdxOfArg) const {
186   // Lazily allocate the BugType object if it hasn't already been
187   // created. Ownership is transferred to the BugReporter object once
188   // the BugReport is passed to 'EmitWarning'.
189   if (!BTAttrNonNull)
190     BTAttrNonNull.reset(new BugType(
191         this, "Argument with 'nonnull' attribute passed null", "API"));
192 
193   llvm::SmallString<256> SBuf;
194   llvm::raw_svector_ostream OS(SBuf);
195   OS << "Null pointer passed to "
196      << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg)
197      << " parameter expecting 'nonnull'";
198 
199   auto R =
200       std::make_unique<PathSensitiveBugReport>(*BTAttrNonNull, SBuf, ErrorNode);
201   if (ArgE)
202     bugreporter::trackExpressionValue(ErrorNode, ArgE, *R);
203 
204   return R;
205 }
206 
207 std::unique_ptr<PathSensitiveBugReport>
208 NonNullParamChecker::genReportReferenceToNullPointer(
209     const ExplodedNode *ErrorNode, const Expr *ArgE) const {
210   if (!BTNullRefArg)
211     BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
212 
213   auto R = std::make_unique<PathSensitiveBugReport>(
214       *BTNullRefArg, "Forming reference to null pointer", ErrorNode);
215   if (ArgE) {
216     const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
217     if (!ArgEDeref)
218       ArgEDeref = ArgE;
219     bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R);
220   }
221   return R;
222 
223 }
224 
225 void ento::registerNonNullParamChecker(CheckerManager &mgr) {
226   mgr.registerChecker<NonNullParamChecker>();
227 }
228 
229 bool ento::shouldRegisterNonNullParamChecker(const LangOptions &LO) {
230   return true;
231 }
232