26#include "llvm/ADT/StringExtras.h"
32class NonNullParamChecker
33 :
public Checker<check::PreCall, check::BeginFunction,
34 EventDispatcher<ImplicitNullDerefEvent>> {
36 this,
"Argument with 'nonnull' attribute passed null",
"API"};
37 const BugType BTNullRefArg{
this,
"Dereference of null pointer"};
43 std::unique_ptr<PathSensitiveBugReport>
45 unsigned IdxOfArg)
const;
46 std::unique_ptr<PathSensitiveBugReport>
47 genReportReferenceToNullPointer(
const ExplodedNode *ErrorN,
48 const Expr *ArgE)
const;
51template <
class CallType>
52void setBitsAccordingToFunctionAttributes(
const CallType &
Call,
53 llvm::SmallBitVector &AttrNonNull) {
67 unsigned IdxAST = Idx.getASTIndex();
68 if (IdxAST >= AttrNonNull.size())
70 AttrNonNull.set(IdxAST);
75template <
class CallType>
76void setBitsAccordingToParameterAttributes(
const CallType &
Call,
77 llvm::SmallBitVector &AttrNonNull) {
79 unsigned ParameterIndex =
Parameter->getFunctionScopeIndex();
80 if (ParameterIndex == AttrNonNull.size())
84 AttrNonNull.set(ParameterIndex);
88template <
class CallType>
89llvm::SmallBitVector getNonNullAttrsImpl(
const CallType &
Call,
90 unsigned ExpectedSize) {
91 llvm::SmallBitVector AttrNonNull(ExpectedSize);
93 setBitsAccordingToFunctionAttributes(
Call, AttrNonNull);
94 setBitsAccordingToParameterAttributes(
Call, AttrNonNull);
101 return getNonNullAttrsImpl(
Call,
Call.getNumArgs());
105llvm::SmallBitVector getNonNullAttrs(
const AnyCall &
Call) {
106 return getNonNullAttrsImpl(
Call,
Call.param_size());
115 llvm::SmallBitVector AttrNonNull = getNonNullAttrs(
Call);
116 unsigned NumArgs =
Call.getNumArgs();
121 for (
unsigned idx = 0; idx < NumArgs; ++idx) {
123 bool HasParam = idx < parms.size();
127 bool HasRefTypeParam =
128 HasParam ? parms[idx]->getType()->isReferenceType() :
false;
129 bool ExpectedToBeNonNull = AttrNonNull.test(idx);
131 if (!ExpectedToBeNonNull && !HasRefTypeParam)
135 const Expr *ArgE =
Call.getArgExpr(idx);
141 assert(!HasRefTypeParam || isa<Loc>(*DV));
144 if (ExpectedToBeNonNull && !isa<Loc>(*DV)) {
163 assert(++CSV->begin() == CSV->end());
170 if (
const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
171 if (
const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer()))
172 ArgE = dyn_cast<Expr>(*(IE->begin()));
177 std::tie(stateNotNull, stateNull) = CM.
assumeDual(state, *DV);
181 if (stateNull && !stateNotNull) {
182 if (
ExplodedNode *errorNode =
C.generateErrorNode(stateNull)) {
184 std::unique_ptr<BugReport> R;
185 if (ExpectedToBeNonNull)
186 R = genReportNullAttrNonNull(errorNode, ArgE, idx + 1);
187 else if (HasRefTypeParam)
188 R = genReportReferenceToNullPointer(errorNode, ArgE);
191 R->addRange(
Call.getArgSourceRange(idx));
194 C.emitReport(std::move(R));
202 if (
ExplodedNode *N =
C.generateSink(stateNull,
C.getPredecessor())) {
204 V,
false, N, &
C.getBugReporter(),
206 dispatchEvent(event);
212 state = stateNotNull;
217 C.addTransition(state);
236void NonNullParamChecker::checkBeginFunction(
CheckerContext &Context)
const {
239 if (!Context.inTopFrame())
252 llvm::SmallBitVector ParameterNonNullMarks = getNonNullAttrs(*AbstractCall);
256 if (!ParameterNonNullMarks.test(
Parameter->getFunctionScopeIndex()))
262 if (!
Parameter->getType()->isPointerType())
265 Loc ParameterLoc = State->getLValue(
Parameter, LocContext);
276 Context.addTransition(State);
279std::unique_ptr<PathSensitiveBugReport>
280NonNullParamChecker::genReportNullAttrNonNull(
const ExplodedNode *ErrorNode,
282 unsigned IdxOfArg)
const {
284 llvm::raw_svector_ostream OS(SBuf);
285 OS <<
"Null pointer passed to "
286 << IdxOfArg << llvm::getOrdinalSuffix(IdxOfArg)
287 <<
" parameter expecting 'nonnull'";
290 std::make_unique<PathSensitiveBugReport>(BTAttrNonNull, SBuf, ErrorNode);
297std::unique_ptr<PathSensitiveBugReport>
298NonNullParamChecker::genReportReferenceToNullPointer(
300 auto R = std::make_unique<PathSensitiveBugReport>(
301 BTNullRefArg,
"Forming reference to null pointer", ErrorNode);
315bool ento::shouldRegisterNonNullParamChecker(
const CheckerManager &mgr) {
An instance of this class corresponds to a call.
static std::optional< AnyCall > forDecl(const Decl *D)
If D is a callable (Objective-C method or a function), return a constructed AnyCall object.
Decl - This represents one declaration (or definition), e.g.
llvm::iterator_range< specific_attr_iterator< T > > specific_attrs() const
This represents one expression.
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
const Decl * getDecl() const
A single parameter index whose accessors require each use to make explicit the parameter index encodi...
Represents a parameter to a function.
A (possibly-)qualified type.
A helper class that allows the use of isa/cast/dyncast to detect TagType objects of structs/unions/cl...
RecordDecl * getDecl() const
const RecordType * getAsUnionType() const
NOTE: getAs*ArrayType are methods on ASTContext.
const T * getAs() const
Member-template getAs<specific type>'.
Represents an abstract call to a function or method along a particular path.
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
ProgramStatePair assumeDual(ProgramStateRef State, DefinedSVal Cond)
Returns a pair of states (StTrue, StFalse) where the given condition is assumed to be true or false,...
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
The simplest example of a concrete compound value is nonloc::CompoundVal, which represents a concrete...
const Expr * getDerefExpr(const Stmt *S)
Given that expression S represents a pointer that would be dereferenced, try to find a sub-expression...
bool trackExpressionValue(const ExplodedNode *N, const Expr *E, PathSensitiveBugReport &R, TrackingOptions Opts={})
Attempts to add visitors to track expression value back to its point of origin.
The JSON file list parser is used to communicate input to InstallAPI.
@ NonNull
Values of this type can never be null.
@ Parameter
The parameter type of a method or function.
const FunctionProtoType * T
We dereferenced a location that may be null.