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