24#include "llvm/ADT/STLExtras.h"
25#include "llvm/ADT/StringExtras.h"
26#include "llvm/Support/FormatVariadic.h"
27#include "llvm/Support/raw_ostream.h"
34class CallAndMessageChecker
35 :
public Checker<check::PreObjCMessage, check::ObjCMessageNil,
37 const BugType CallNullBug{
38 this,
"Called function pointer is null (null dereference)"};
39 const BugType CallUndefBug{
40 this,
"Called function pointer is an uninitialized pointer value"};
41 const BugType CXXCallNullBug{
this,
"Called C++ object pointer is null"};
42 const BugType CXXCallUndefBug{
this,
43 "Called C++ object pointer is uninitialized"};
44 const BugType CallArgBug{
this,
"Uninitialized argument value"};
45 const BugType CXXDeleteUndefBug{
this,
"Uninitialized argument value"};
46 const BugType MsgUndefBug{
47 this,
"Receiver in message expression is an uninitialized value"};
48 const BugType ObjCPropUndefBug{
49 this,
"Property access on an uninitialized object pointer"};
50 const BugType ObjCSubscriptUndefBug{
51 this,
"Subscript access on an uninitialized object pointer"};
52 const BugType MsgArgBug{
this,
"Uninitialized argument value"};
53 const BugType MsgRetBug{
this,
"Receiver in message expression is 'nil'"};
54 const BugType CallFewArgsBug{
this,
"Function call with too few arguments"};
73 CK_CXXDeallocationArg,
74 CK_ArgInitializedness,
75 CK_ArgPointeeInitializedness,
81 bool ChecksEnabled[CK_NumCheckKinds] = {
false};
87 bool ArgPointeeInitializednessComplete =
true;
89 void checkPreObjCMessage(
const ObjCMethodCall &msg, CheckerContext &
C)
const;
94 void checkObjCMessageNil(
const ObjCMethodCall &msg, CheckerContext &
C)
const;
96 void checkPreCall(
const CallEvent &
Call, CheckerContext &
C)
const;
118 bool PreVisitProcessArg(CheckerContext &
C, SVal
V, SourceRange ArgRange,
119 const Expr *ArgEx,
int ArgumentNumber,
120 bool CheckUninitFields,
const CallEvent &
Call,
122 const ParmVarDecl *ParamDecl)
const;
124 static void emitBadCall(
const BugType &BT, CheckerContext &
C,
126 void emitNilReceiverBug(CheckerContext &
C,
const ObjCMethodCall &msg,
127 ExplodedNode *N)
const;
129 void HandleNilReceiver(CheckerContext &
C,
131 const ObjCMethodCall &msg)
const;
133 bool uninitRefOrPointer(CheckerContext &
C, SVal
V,
const CallEvent &
Call,
134 const BugType &BT,
const ParmVarDecl *ParamDecl,
135 int ArgumentNumber)
const;
140 CallDescriptionMap<int> FunctionsWithInOutPtrParam = {
141 {{CDM::CLibrary, {
"mbrlen"}, 3}, 2},
142 {{CDM::CLibrary, {
"mbrtowc"}, 4}, 3},
143 {{CDM::CLibrary, {
"wcrtomb"}, 3}, 2},
144 {{CDM::CLibrary, {
"mbsrtowcs"}, 4}, 3},
145 {{CDM::CLibrary, {
"wcsrtombs"}, 4}, 3},
146 {{CDM::CLibrary, {
"mbsnrtowcs"}, 5}, 4},
147 {{CDM::CLibrary, {
"wcsnrtombs"}, 5}, 4},
148 {{CDM::CLibrary, {
"wcrtomb_s"}, 5}, 4},
149 {{CDM::CLibrary, {
"mbsrtowcs_s"}, 6}, 5},
150 {{CDM::CLibrary, {
"wcsrtombs_s"}, 6}, 5},
152 {{CDM::CLibrary, {
"mbrtoc8"}, 4}, 3},
153 {{CDM::CLibrary, {
"c8rtomb"}, 3}, 2},
154 {{CDM::CLibrary, {
"mbrtoc16"}, 4}, 3},
155 {{CDM::CLibrary, {
"c16rtomb"}, 3}, 2},
156 {{CDM::CLibrary, {
"mbrtoc32"}, 4}, 3},
157 {{CDM::CLibrary, {
"c32rtomb"}, 3}, 2},
159 {{CDM::CLibrary, {
"mktime"}, 1}, 0},
160 {{CDM::CLibrary, {
"timegm"}, 1}, 0},
167 ExplodedNode *N =
C.generateErrorNode();
171 auto R = std::make_unique<PathSensitiveBugReport>(BT, BT.
getDescription(), N);
178 C.emitReport(std::move(R));
183 llvm::raw_svector_ostream &Os) {
184 switch (
Call.getKind()) {
189 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
190 <<
" argument in message expression is an uninitialized value";
193 assert(Msg.
isSetter() &&
"Getters have no args");
194 Os <<
"Argument for property setter is an uninitialized value";
197 if (Msg.
isSetter() && (ArgumentNumber == 0))
198 Os <<
"Argument for subscript setter is an uninitialized value";
200 Os <<
"Subscript index is an uninitialized value";
203 llvm_unreachable(
"Unknown message kind.");
206 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
207 <<
" block call argument is an uninitialized value";
210 Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
211 <<
" function call argument is an uninitialized value";
217class FindUninitializedField {
219 using FieldChainTy = SmallVector<const FieldDecl *, 10>;
220 FieldChainTy FieldChain;
223 StoreManager &StoreMgr;
224 MemRegionManager &MrMgr;
226 bool FindNotUninitialized;
229 FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr,
230 Store s,
bool FindNotUninitialized =
false)
231 : StoreMgr(storeMgr), MrMgr(mrMgr), store(
s),
232 FindNotUninitialized(FindNotUninitialized) {}
234 bool Find(
const TypedValueRegion *R) {
237 const RecordDecl *RD = RT->getDecl()->getDefinition();
238 assert(RD &&
"Referred record has no definition");
239 for (
const auto *I : RD->
fields()) {
240 if (I->isUnnamedBitField())
243 FieldChain.push_back(I);
246 if (FindNotUninitialized ? !Find(FR) : Find(FR))
247 return !FindNotUninitialized;
249 SVal
V = StoreMgr.
getBinding(store, loc::MemRegionVal(FR));
250 if (FindNotUninitialized ? !
V.isUndef() :
V.isUndef())
251 return !FindNotUninitialized;
253 FieldChain.pop_back();
257 return FindNotUninitialized;
263template <>
struct format_provider<FindUninitializedField::FieldChainTy> {
264 static void format(
const FindUninitializedField::FieldChainTy &
V,
265 raw_ostream &Stream, StringRef Style) {
268 else if (
V.size() == 1)
269 Stream <<
" (e.g., field: '" << *
V[0] <<
"')";
271 Stream <<
" (e.g., via the field chain: '";
273 V, Stream, [&Stream](
const FieldDecl *FD) { Stream << *FD; },
".");
280bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &
C, SVal
V,
281 const CallEvent &
Call,
283 const ParmVarDecl *ParamDecl,
284 int ArgumentNumber)
const {
286 if (!ChecksEnabled[CK_ArgPointeeInitializedness])
293 QualType ParamT = ParamDecl->
getType();
297 bool AllowPartialInitializedness = ArgPointeeInitializednessComplete;
300 if (
const int *PI = FunctionsWithInOutPtrParam.lookup(
Call)) {
301 if (*PI != ArgumentNumber)
304 AllowPartialInitializedness =
true;
310 const MemRegion *SValMemRegion =
V.getAsRegion();
320 PointeeT =
C.getASTContext().CharTy;
321 const SVal PointeeV = State->getSVal(SValMemRegion, PointeeT);
322 const Expr *ArgEx =
Call.getArgExpr(ArgumentNumber);
325 if (ExplodedNode *N =
C.generateErrorNode()) {
326 std::string Msg = llvm::formatv(
327 "{0}{1} function call argument is {2} uninitialized value",
328 ArgumentNumber + 1, llvm::getOrdinalSuffix(ArgumentNumber + 1),
330 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
331 R->addRange(
Call.getArgSourceRange(ArgumentNumber));
335 C.emitReport(std::move(R));
340 if (
auto LV = PointeeV.
getAs<nonloc::LazyCompoundVal>()) {
341 const LazyCompoundValData *D = LV->getCVData();
342 FindUninitializedField F(
C.getState()->getStateManager().getStoreManager(),
343 C.getSValBuilder().getRegionManager(),
344 D->
getStore(), AllowPartialInitializedness);
347 if (ExplodedNode *N =
C.generateErrorNode()) {
348 std::string Msg = llvm::formatv(
349 "{0}{1} function call argument {2} an uninitialized value{3}",
350 (ArgumentNumber + 1), llvm::getOrdinalSuffix(ArgumentNumber + 1),
351 ParamT->
isPointerType() ?
"points to" :
"references", F.FieldChain);
352 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
353 R->addRange(
Call.getArgSourceRange(ArgumentNumber));
357 C.emitReport(std::move(R));
366bool CallAndMessageChecker::PreVisitProcessArg(
367 CheckerContext &
C, SVal
V, SourceRange ArgRange,
const Expr *ArgEx,
368 int ArgumentNumber,
bool CheckUninitFields,
const CallEvent &
Call,
369 const BugType &BT,
const ParmVarDecl *ParamDecl)
const {
370 if (uninitRefOrPointer(
C,
V,
Call, BT, ParamDecl, ArgumentNumber))
374 if (!ChecksEnabled[CK_ArgInitializedness]) {
378 if (ExplodedNode *N =
C.generateErrorNode()) {
380 SmallString<200> Buf;
381 llvm::raw_svector_ostream Os(Buf);
383 auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
385 R->addRange(ArgRange);
388 C.emitReport(std::move(R));
393 if (!CheckUninitFields)
396 if (
auto LV =
V.getAs<nonloc::LazyCompoundVal>()) {
397 const LazyCompoundValData *D = LV->getCVData();
398 FindUninitializedField F(
C.getState()->getStateManager().getStoreManager(),
399 C.getSValBuilder().getRegionManager(),
403 if (!ChecksEnabled[CK_ArgInitializedness]) {
407 if (ExplodedNode *N =
C.generateErrorNode()) {
408 std::string Msg = llvm::formatv(
409 "Passed-by-value struct argument contains uninitialized data{0}",
413 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
414 R->addRange(ArgRange);
420 C.emitReport(std::move(R));
433 const LocationContext *LCtx =
C.getLocationContext();
434 SVal L = State->getSVal(Callee, LCtx);
437 if (!ChecksEnabled[CK_FunctionPointer]) {
441 emitBadCall(CallUndefBug,
C, Callee);
446 std::tie(StNonNull, StNull) = State->assume(L.
castAs<DefinedOrUnknownSVal>());
448 if (StNull && !StNonNull) {
449 if (!ChecksEnabled[CK_FunctionPointer]) {
453 emitBadCall(CallNullBug,
C, Callee);
465 unsigned Params =
Call.parameters().size();
466 if (
Call.getNumArgs() >= Params)
469 if (!ChecksEnabled[CK_ParameterCount]) {
474 ExplodedNode *N =
C.generateErrorNode();
478 SmallString<512> Str;
479 llvm::raw_svector_ostream os(Str);
486 os <<
"taking " << Params <<
" argument" << (Params == 1 ?
"" :
"s")
487 <<
" is called with fewer (" <<
Call.getNumArgs() <<
")";
490 std::make_unique<PathSensitiveBugReport>(CallFewArgsBug, os.str(), N));
495 const CXXInstanceCall *CC, CheckerContext &
C,
ProgramStateRef State)
const {
499 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
508 std::tie(StNonNull, StNull) = State->assume(
V.castAs<DefinedOrUnknownSVal>());
510 if (StNull && !StNonNull) {
511 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
523CallAndMessageChecker::checkCXXDeallocation(
const CXXDeallocatorCall *DC,
532 if (!ChecksEnabled[CK_CXXDeallocationArg]) {
538 ExplodedNode *N =
C.generateErrorNode();
542 Desc =
"Argument to 'delete[]' is uninitialized";
544 Desc =
"Argument to 'delete' is uninitialized";
545 auto R = std::make_unique<PathSensitiveBugReport>(CXXDeleteUndefBug, Desc, N);
547 C.emitReport(std::move(R));
560 const bool checkUninitFields =
561 !(
C.getAnalysisManager().shouldInlineCall() && (D && D->
getBody()));
565 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
566 for (
unsigned i = 0, e =
Call.getNumArgs(); i != e; ++i) {
567 const ParmVarDecl *ParamDecl =
nullptr;
568 if (FD && i < FD->getNumParams())
570 if (PreVisitProcessArg(
C,
Call.getArgSVal(i),
Call.getArgSourceRange(i),
571 Call.getArgExpr(i), i, checkUninitFields,
Call, BT,
578void CallAndMessageChecker::checkPreCall(
const CallEvent &
Call,
579 CheckerContext &
C)
const {
582 if (
const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr()))
583 State = checkFunctionPointerCall(CE,
C, State);
589 State = checkParameterCount(
Call,
C, State);
594 if (
const auto *CC = dyn_cast<CXXInstanceCall>(&
Call))
595 State = checkCXXMethodCall(CC,
C, State);
600 if (
const auto *DC = dyn_cast<CXXDeallocatorCall>(&
Call))
601 State = checkCXXDeallocation(DC,
C, State);
606 State = checkArgInitializedness(
Call,
C, State);
609 C.addTransition(State);
612void CallAndMessageChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
613 CheckerContext &
C)
const {
616 if (!ChecksEnabled[CK_UndefReceiver]) {
620 if (ExplodedNode *N =
C.generateErrorNode()) {
621 const BugType *BT =
nullptr;
627 BT = &ObjCPropUndefBug;
630 BT = &ObjCSubscriptUndefBug;
633 assert(BT &&
"Unknown message kind.");
635 auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->
getDescription(), N);
642 C.emitReport(std::move(R));
648void CallAndMessageChecker::checkObjCMessageNil(
const ObjCMethodCall &msg,
649 CheckerContext &
C)
const {
650 HandleNilReceiver(
C,
C.getState(), msg);
653void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &
C,
654 const ObjCMethodCall &msg,
655 ExplodedNode *N)
const {
656 if (!ChecksEnabled[CK_NilReceiver]) {
665 SmallString<200> buf;
666 llvm::raw_svector_ostream os(buf);
667 os <<
"The receiver of message '";
671 os <<
", which results in forming a null reference";
673 os <<
" and returns a value of type '";
675 os <<
"' that will be garbage";
679 std::make_unique<PathSensitiveBugReport>(MsgRetBug, os.str(), N);
685 C.emitReport(std::move(report));
689 return (triple.getVendor() == llvm::Triple::Apple &&
690 (triple.isiOS() || triple.isWatchOS() ||
691 !triple.isMacOSXVersionLT(10,5)));
694void CallAndMessageChecker::HandleNilReceiver(CheckerContext &
C,
696 const ObjCMethodCall &Msg)
const {
697 ASTContext &Ctx =
C.getASTContext();
703 const LocationContext *LCtx =
C.getLocationContext();
705 if (CanRetTy->isStructureOrClassType()) {
707 SVal
V =
C.getSValBuilder().makeZeroVal(RetTy);
713 if (CanRetTy != Ctx.
VoidTy &&
C.getLocationContext()->getParentMap()
720 (voidPtrSize < returnTypeSize &&
727 if (ExplodedNode *N =
C.generateErrorNode(state))
728 emitNilReceiverBug(
C, Msg, N);
745 SVal
V =
C.getSValBuilder().makeZeroVal(RetTy);
750 C.addTransition(state);
753void ento::registerCallAndMessageChecker(CheckerManager &Mgr) {
754 CallAndMessageChecker *Chk = Mgr.
registerChecker<CallAndMessageChecker>();
756#define QUERY_CHECKER_OPTION(OPTION) \
757 Chk->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \
758 Mgr.getAnalyzerOptions().getCheckerBooleanOption( \
759 Mgr.getCurrentCheckerName(), #OPTION);
770 Chk->ArgPointeeInitializednessComplete =
775bool ento::shouldRegisterCallAndMessageChecker(
const CheckerManager &) {
#define QUERY_CHECKER_OPTION(OPTION)
static bool supportsNilWithFloatRet(const llvm::Triple &triple)
static void describeUninitializedArgumentInCall(const CallEvent &Call, int ArgumentNumber, llvm::raw_svector_ostream &Os)
Defines the clang::Expr interface and subclasses for C++ expressions.
__device__ __2f16 float __ockl_bool s
static CanQualType getCanonicalType(QualType T)
Return the canonical (structural) type corresponding to the specified potentially non-canonical type ...
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
CanQualType UnsignedLongLongTy
const TargetInfo & getTargetInfo() const
bool isArrayFormAsWritten() const
const T * getTypePtr() const
Retrieve the underlying type pointer, which refers to a canonical type.
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
This represents one expression.
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Represents a member of a struct/union/class.
const ParmVarDecl * getParamDecl(unsigned i) const
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
Selector getSelector() const
SourceRange getReceiverRange() const
Source range of the receiver.
void print(raw_ostream &OS, const PrintingPolicy &Policy, const Twine &PlaceHolder=Twine(), unsigned Indentation=0) const
bool isConstQualified() const
Determine whether this type is const-qualified.
field_range fields() const
void print(llvm::raw_ostream &OS) const
Prints the full selector name (e.g. "foo:bar:").
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
const llvm::Triple & getTriple() const
Returns the target triple of the primary target.
bool isStructureType() const
bool isPointerType() const
bool isReferenceType() const
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
const RecordType * getAsStructureType() const
bool isPointerOrReferenceType() const
StringRef getDescription() const
const CXXDeleteExpr * getOriginExpr() const override
Returns the expression whose value will be the result of this call.
virtual SVal getCXXThisVal() const
Returns the value of the implicit 'this' object.
virtual const Expr * getCXXThisExpr() const
Returns the expression representing the implicit 'this' object.
Represents an abstract call to a function or method along a particular path.
QualType getResultType() const
Returns the result type, adjusted for references.
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
CheckerNameRef getCurrentCheckerName() const
Simple checker classes that implement one frontend (i.e.
const void * getStore() const
It might return null.
LLVM_ATTRIBUTE_RETURNS_NONNULL const TypedValueRegion * getRegion() const
const FieldRegion * getFieldRegion(const FieldDecl *FD, const SubRegion *SuperRegion)
getFieldRegion - Retrieve or create the memory region associated with a specified FieldDecl.
Represents any expression that calls an Objective-C method.
ObjCMessageKind getMessageKind() const
Returns how the message was written in the source (property access, subscript, or explicit message se...
bool isSetter() const
Returns true if this property access or subscript is a setter (has the form of an assignment).
const ObjCMessageExpr * getOriginExpr() const override
Returns the expression whose value will be the result of this call.
SVal getReceiverSVal() const
Returns the value of the receiver at the time of this call.
std::optional< T > getAs() const
Convert to the specified SVal type, returning std::nullopt if this SVal is not of the desired type.
T castAs() const
Convert to the specified SVal type, asserting that this SVal is of the desired type.
virtual SVal getBinding(Store store, Loc loc, QualType T=QualType())=0
Return the value bound to specified location in a given state.
virtual QualType getValueType() const =0
Defines the clang::TargetInfo interface.
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.
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const void * Store
Store - This opaque type encapsulates an immutable mapping from locations to values.
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
CanQual< Type > CanQualType
Represents a canonical, potentially-qualified type.
bool isa(CodeGen::Address addr)
const FunctionProtoType * T
U cast(CodeGen::Address addr)