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);
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 SVal L = State->getSVal(Callee,
C.getStackFrame());
436 if (!ChecksEnabled[CK_FunctionPointer]) {
440 emitBadCall(CallUndefBug,
C, Callee);
445 std::tie(StNonNull, StNull) = State->assume(L.
castAs<DefinedOrUnknownSVal>());
447 if (StNull && !StNonNull) {
448 if (!ChecksEnabled[CK_FunctionPointer]) {
452 emitBadCall(CallNullBug,
C, Callee);
464 unsigned Params =
Call.parameters().size();
465 if (
Call.getNumArgs() >= Params)
468 if (!ChecksEnabled[CK_ParameterCount]) {
473 ExplodedNode *N =
C.generateErrorNode();
477 SmallString<512> Str;
478 llvm::raw_svector_ostream os(Str);
485 os <<
"taking " << Params <<
" argument" << (Params == 1 ?
"" :
"s")
486 <<
" is called with fewer (" <<
Call.getNumArgs() <<
")";
489 std::make_unique<PathSensitiveBugReport>(CallFewArgsBug, os.str(), N));
494 const CXXInstanceCall *CC, CheckerContext &
C,
ProgramStateRef State)
const {
498 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
507 std::tie(StNonNull, StNull) = State->assume(
V.castAs<DefinedOrUnknownSVal>());
509 if (StNull && !StNonNull) {
510 if (!ChecksEnabled[CK_CXXThisMethodCall]) {
522CallAndMessageChecker::checkCXXDeallocation(
const CXXDeallocatorCall *DC,
531 if (!ChecksEnabled[CK_CXXDeallocationArg]) {
537 ExplodedNode *N =
C.generateErrorNode();
541 Desc =
"Argument to 'delete[]' is uninitialized";
543 Desc =
"Argument to 'delete' is uninitialized";
544 auto R = std::make_unique<PathSensitiveBugReport>(CXXDeleteUndefBug, Desc, N);
546 C.emitReport(std::move(R));
559 const bool checkUninitFields =
560 !(
C.getAnalysisManager().shouldInlineCall() && (D && D->
getBody()));
564 const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
565 for (
unsigned i = 0, e =
Call.getNumArgs(); i != e; ++i) {
566 const ParmVarDecl *ParamDecl =
nullptr;
567 if (FD && i < FD->getNumParams())
569 if (PreVisitProcessArg(
C,
Call.getArgSVal(i),
Call.getArgSourceRange(i),
570 Call.getArgExpr(i), i, checkUninitFields,
Call, BT,
577void CallAndMessageChecker::checkPreCall(
const CallEvent &
Call,
578 CheckerContext &
C)
const {
581 if (
const CallExpr *CE = dyn_cast_or_null<CallExpr>(
Call.getOriginExpr()))
582 State = checkFunctionPointerCall(CE,
C, State);
588 State = checkParameterCount(
Call,
C, State);
593 if (
const auto *CC = dyn_cast<CXXInstanceCall>(&
Call))
594 State = checkCXXMethodCall(CC,
C, State);
599 if (
const auto *DC = dyn_cast<CXXDeallocatorCall>(&
Call))
600 State = checkCXXDeallocation(DC,
C, State);
605 State = checkArgInitializedness(
Call,
C, State);
608 C.addTransition(State);
611void CallAndMessageChecker::checkPreObjCMessage(
const ObjCMethodCall &msg,
612 CheckerContext &
C)
const {
615 if (!ChecksEnabled[CK_UndefReceiver]) {
619 if (ExplodedNode *N =
C.generateErrorNode()) {
620 const BugType *BT =
nullptr;
626 BT = &ObjCPropUndefBug;
629 BT = &ObjCSubscriptUndefBug;
632 assert(BT &&
"Unknown message kind.");
634 auto R = std::make_unique<PathSensitiveBugReport>(*BT, BT->
getDescription(), N);
641 C.emitReport(std::move(R));
647void CallAndMessageChecker::checkObjCMessageNil(
const ObjCMethodCall &msg,
648 CheckerContext &
C)
const {
649 HandleNilReceiver(
C,
C.getState(), msg);
652void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &
C,
653 const ObjCMethodCall &msg,
654 ExplodedNode *N)
const {
655 if (!ChecksEnabled[CK_NilReceiver]) {
664 SmallString<200> buf;
665 llvm::raw_svector_ostream os(buf);
666 os <<
"The receiver of message '";
670 os <<
", which results in forming a null reference";
672 os <<
" and returns a value of type '";
674 os <<
"' that will be garbage";
678 std::make_unique<PathSensitiveBugReport>(MsgRetBug, os.str(), N);
684 C.emitReport(std::move(report));
688 return (triple.getVendor() == llvm::Triple::Apple &&
689 (triple.isiOS() || triple.isWatchOS() ||
690 !triple.isMacOSXVersionLT(10,5)));
693void CallAndMessageChecker::HandleNilReceiver(CheckerContext &
C,
695 const ObjCMethodCall &Msg)
const {
696 ASTContext &Ctx =
C.getASTContext();
702 const StackFrame *SF =
C.getStackFrame();
704 if (CanRetTy->isStructureOrClassType()) {
706 SVal
V =
C.getSValBuilder().makeZeroVal(RetTy);
712 if (CanRetTy != Ctx.
VoidTy &&
713 C.getStackFrame()->getParentMap().isConsumedExpr(Msg.
getOriginExpr())) {
719 (voidPtrSize < returnTypeSize &&
726 if (ExplodedNode *N =
C.generateErrorNode(state))
727 emitNilReceiverBug(
C, Msg, N);
744 SVal
V =
C.getSValBuilder().makeZeroVal(RetTy);
749 C.addTransition(state);
752void ento::registerCallAndMessageChecker(CheckerManager &Mgr) {
753 CallAndMessageChecker *Chk = Mgr.
registerChecker<CallAndMessageChecker>();
755#define QUERY_CHECKER_OPTION(OPTION) \
756 Chk->ChecksEnabled[CallAndMessageChecker::CK_##OPTION] = \
757 Mgr.getAnalyzerOptions().getCheckerBooleanOption( \
758 Mgr.getCurrentCheckerName(), #OPTION);
769 Chk->ArgPointeeInitializednessComplete =
774bool 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.
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)
U cast(CodeGen::Address addr)