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) {
235 QualType
T =
R->getValueType();
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);
324 auto DescribeArgument = [ParamT, PointeeT,
325 &
Call](
bool ArgIsStruct) -> std::string {
326 if (PointeeT.isConstQualified())
327 return llvm::formatv(
328 "this argument is const{0} and {1} input data of the function",
330 ArgIsStruct ?
"may contain" :
"is likely");
332 return llvm::formatv(
333 "function '{0}' expects {1}this argument to be initialized",
339 if (ExplodedNode *N =
C.generateErrorNode()) {
340 std::string Msg = llvm::formatv(
341 "{0}{1} function call argument {2} an uninitialized value; {3}",
342 ArgumentNumber + 1, llvm::getOrdinalSuffix(ArgumentNumber + 1),
344 DescribeArgument(
false));
345 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
346 R->addRange(
Call.getArgSourceRange(ArgumentNumber));
350 C.emitReport(std::move(R));
355 if (
auto LV = PointeeV.
getAs<nonloc::LazyCompoundVal>()) {
356 const LazyCompoundValData *D = LV->getCVData();
357 FindUninitializedField F(
C.getState()->getStateManager().getStoreManager(),
358 C.getSValBuilder().getRegionManager(),
359 D->
getStore(), AllowPartialInitializedness);
362 if (ExplodedNode *N =
C.generateErrorNode()) {
363 std::string Msg = llvm::formatv(
364 "{0}{1} function call argument {2} an uninitialized value{3}; {4}",
365 (ArgumentNumber + 1), llvm::getOrdinalSuffix(ArgumentNumber + 1),
367 DescribeArgument(
true));
368 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
369 R->addRange(
Call.getArgSourceRange(ArgumentNumber));
373 C.emitReport(std::move(R));
382bool CallAndMessageChecker::PreVisitProcessArg(
383 CheckerContext &
C, SVal
V, SourceRange ArgRange,
const Expr *ArgEx,
384 int ArgumentNumber,
bool CheckUninitFields,
const CallEvent &
Call,
385 const BugType &BT,
const ParmVarDecl *ParamDecl)
const {
386 if (uninitRefOrPointer(
C,
V,
Call, BT, ParamDecl, ArgumentNumber))
390 if (!ChecksEnabled[CK_ArgInitializedness]) {
394 if (ExplodedNode *N =
C.generateErrorNode()) {
396 SmallString<200> Buf;
397 llvm::raw_svector_ostream Os(Buf);
399 auto R = std::make_unique<PathSensitiveBugReport>(BT, Os.str(), N);
401 R->addRange(ArgRange);
404 C.emitReport(std::move(R));
409 if (!CheckUninitFields)
412 if (
auto LV =
V.getAs<nonloc::LazyCompoundVal>()) {
413 const LazyCompoundValData *D = LV->getCVData();
414 FindUninitializedField F(
C.getState()->getStateManager().getStoreManager(),
415 C.getSValBuilder().getRegionManager(),
419 if (!ChecksEnabled[CK_ArgInitializedness]) {
423 if (ExplodedNode *N =
C.generateErrorNode()) {
424 std::string Msg = llvm::formatv(
425 "Passed-by-value struct argument contains uninitialized data{0}",
429 auto R = std::make_unique<PathSensitiveBugReport>(BT, Msg, N);
430 R->addRange(ArgRange);
436 C.emitReport(std::move(R));
449 SVal L = State->getSVal(Callee,
C.getStackFrame());
452 if (!ChecksEnabled[CK_FunctionPointer]) {
456 emitBadCall(CallUndefBug,
C, Callee);
461 std::tie(StNonNull, StNull) = State->assume(L.
castAs<DefinedOrUnknownSVal>());
463 if (StNull && !StNonNull) {
464 if (!ChecksEnabled[CK_FunctionPointer]) {
468 emitBadCall(CallNullBug,
C, Callee);
480 unsigned Params =
Call.parameters().size();
481 if (
Call.getNumArgs() >= Params)
484 if (!ChecksEnabled[CK_ParameterCount]) {
489 ExplodedNode *N =
C.generateErrorNode();
493 SmallString<512> Str;
494 llvm::raw_svector_ostream os(Str);
501 os <<
"taking " << Params <<
" argument" << (Params == 1 ?
"" :
"s")
502 <<
" is called with fewer (" <<
Call.getNumArgs() <<
")";
505 std::make_unique<PathSensitiveBugReport>(CallFewArgsBug, os.str(), N));
510 const CXXInstanceCall *CC, CheckerContext &
C,
ProgramStateRef State)
const {
514 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
523 std::tie(StNonNull, StNull) = State->assume(
V.castAs<DefinedOrUnknownSVal>());
525 if (StNull && !StNonNull) {
526 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
538CallAndMessageChecker::checkCXXDeallocation(
const CXXDeallocatorCall *DC,
547 if (!ChecksEnabled[CK_CXXDeallocationArg]) {
553 ExplodedNode *N =
C.generateErrorNode();
557 Desc =
"Argument to 'delete[]' is uninitialized";
559 Desc =
"Argument to 'delete' is uninitialized";
560 auto R = std::make_unique<PathSensitiveBugReport>(CXXDeleteUndefBug, Desc, N);
562 C.emitReport(std::move(R));
575 const bool checkUninitFields =
576 !(
C.getAnalysisManager().shouldInlineCall() && (D && D->
getBody()));
580 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
581 for (
unsigned i = 0, e =
Call.getNumArgs(); i != e; ++i) {
582 const ParmVarDecl *ParamDecl =
nullptr;
583 if (FD && i < FD->getNumParams())
585 if (PreVisitProcessArg(
C,
Call.getArgSVal(i),
Call.getArgSourceRange(i),
586 Call.getArgExpr(i), i, checkUninitFields,
Call, BT,
593void CallAndMessageChecker::checkPreCall(
const CallEvent &
Call,
594 CheckerContext &
C)
const {
597 if (
const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr()))
598 State = checkFunctionPointerCall(CE,
C, State);
604 State = checkParameterCount(
Call,
C, State);
609 if (
const auto *CC = dyn_cast<CXXInstanceCall>(&
Call))
610 State = checkCXXMethodCall(CC,
C, State);
615 if (
const auto *DC = dyn_cast<CXXDeallocatorCall>(&
Call))
616 State = checkCXXDeallocation(DC,
C, State);
621 State = checkArgInitializedness(
Call,
C, State);
624 C.addTransition(State);
627void CallAndMessageChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
628 CheckerContext &
C)
const {
631 if (!ChecksEnabled[CK_UndefReceiver]) {
635 if (ExplodedNode *N =
C.generateErrorNode()) {
636 const BugType *BT =
nullptr;
642 BT = &ObjCPropUndefBug;
645 BT = &ObjCSubscriptUndefBug;
648 assert(BT &&
"Unknown message kind.");
650 auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->
getDescription(), N);
657 C.emitReport(std::move(R));
663void CallAndMessageChecker::checkObjCMessageNil(
const ObjCMethodCall &msg,
664 CheckerContext &
C)
const {
665 HandleNilReceiver(
C,
C.getState(), msg);
668void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &
C,
669 const ObjCMethodCall &msg,
670 ExplodedNode *N)
const {
671 if (!ChecksEnabled[CK_NilReceiver]) {
680 SmallString<200> buf;
681 llvm::raw_svector_ostream os(buf);
682 os <<
"The receiver of message '";
686 os <<
", which results in forming a null reference";
688 os <<
" and returns a value of type '";
690 os <<
"' that will be garbage";
694 std::make_unique<PathSensitiveBugReport>(MsgRetBug, os.str(), N);
700 C.emitReport(std::move(report));
704 return (triple.getVendor() == llvm::Triple::Apple &&
705 (triple.isiOS() || triple.isWatchOS() ||
706 !triple.isMacOSXVersionLT(10,5)));
709void CallAndMessageChecker::HandleNilReceiver(CheckerContext &
C,
711 const ObjCMethodCall &Msg)
const {
712 ASTContext &Ctx =
C.getASTContext();
718 const StackFrame *SF =
C.getStackFrame();
720 if (CanRetTy->isStructureOrClassType()) {
722 SVal
V =
C.getSValBuilder().makeZeroVal(RetTy);
728 if (CanRetTy != Ctx.
VoidTy &&
729 C.getStackFrame()->getParentMap().isConsumedExpr(Msg.
getOriginExpr())) {
735 (voidPtrSize < returnTypeSize &&
742 if (ExplodedNode *N =
C.generateErrorNode(state))
743 emitNilReceiverBug(
C, Msg, N);
760 SVal
V =
C.getSValBuilder().makeZeroVal(RetTy);
765 C.addTransition(state);
768void ento::registerCallAndMessageChecker(CheckerManager &Mgr) {
769 CallAndMessageChecker *Chk = Mgr.
registerChecker<CallAndMessageChecker>();
771#define QUERY_CHECKER_OPTION(OPTION) \
772 Chk->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \
773 Mgr.getAnalyzerOptions().getCheckerBooleanOption( \
774 Mgr.getCurrentCheckerName(), #OPTION);
785 Chk->ArgPointeeInitializednessComplete =
790bool 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.
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.
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.
Top level wrappers for InstallAPI frontend operations.
CanQual< Type > CanQualType
Represents a canonical, potentially-qualified type.
bool isa(CodeGen::Address addr)
const FunctionProtoType * T
U cast(CodeGen::Address addr)