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