22 using namespace clang;
30 class ValistChecker :
public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
32 mutable std::unique_ptr<BugType> BT_leakedvalist, BT_uninitaccess;
34 struct VAListAccepter {
39 static const CallDescription VaStart, VaEnd, VaCopy;
49 DefaultBool ChecksEnabled[CK_NumCheckKinds];
50 CheckName CheckNames[CK_NumCheckKinds];
52 void checkPreStmt(
const VAArgExpr *VAA, CheckerContext &C)
const;
53 void checkPreCall(
const CallEvent &Call, CheckerContext &C)
const;
54 void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C)
const;
57 const MemRegion *getVAListAsRegion(SVal SV,
const Expr *VAExpr,
58 bool &IsSymbolic, CheckerContext &C)
const;
59 const ExplodedNode *getStartCallSite(
const ExplodedNode *N,
60 const MemRegion *Reg)
const;
62 void reportUninitializedAccess(
const MemRegion *VAList, StringRef Msg,
63 CheckerContext &C)
const;
64 void reportLeakedVALists(
const RegionVector &LeakedVALists, StringRef Msg1,
65 StringRef Msg2, CheckerContext &C, ExplodedNode *N,
66 bool ReportUninit =
false)
const;
68 void checkVAListStartCall(
const CallEvent &Call, CheckerContext &C,
70 void checkVAListEndCall(
const CallEvent &Call, CheckerContext &C)
const;
74 ValistBugVisitor(
const MemRegion *Reg,
bool IsLeak =
false)
75 : Reg(Reg), IsLeak(IsLeak) {}
76 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
81 std::shared_ptr<PathDiagnosticPiece>
82 getEndPath(BugReporterContext &BRC,
const ExplodedNode *EndPathNode,
83 BugReport &BR)
override {
88 EndPathNode, BRC.getSourceManager());
90 return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),
false);
92 std::shared_ptr<PathDiagnosticPiece> VisitNode(
const ExplodedNode *N,
93 BugReporterContext &BRC,
94 BugReport &BR)
override;
103 ValistChecker::VAListAccepters = {
104 {{
"vfprintf", 3}, 2},
108 {{
"vsnprintf", 4}, 3},
109 {{
"vsprintf", 3}, 2},
111 {{
"vfwprintf", 3}, 2},
112 {{
"vfwscanf", 3}, 2},
113 {{
"vwprintf", 2}, 1},
115 {{
"vswprintf", 4}, 3},
118 {{
"vswscanf", 3}, 2}};
119 const CallDescription ValistChecker::VaStart(
"__builtin_va_start", 2),
120 ValistChecker::VaCopy(
"__builtin_va_copy", 2),
121 ValistChecker::VaEnd(
"__builtin_va_end", 1);
124 void ValistChecker::checkPreCall(
const CallEvent &Call,
125 CheckerContext &C)
const {
126 if (!Call.isGlobalCFunction())
128 if (Call.isCalled(VaStart))
129 checkVAListStartCall(Call, C,
false);
130 else if (Call.isCalled(VaCopy))
131 checkVAListStartCall(Call, C,
true);
132 else if (Call.isCalled(VaEnd))
133 checkVAListEndCall(Call, C);
135 for (
auto FuncInfo : VAListAccepters) {
136 if (!Call.isCalled(FuncInfo.Func))
139 const MemRegion *VAList =
140 getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos),
141 Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C);
145 if (C.getState()->contains<InitializedVALists>(VAList))
154 Errmsg += FuncInfo.Func.getFunctionName();
155 Errmsg +=
"' is called with an uninitialized va_list argument";
156 reportUninitializedAccess(VAList, Errmsg.c_str(), C);
162 const MemRegion *ValistChecker::getVAListAsRegion(SVal SV,
const Expr *E,
164 CheckerContext &C)
const {
165 const MemRegion *Reg = SV.getAsRegion();
169 bool VaListModelledAsArray =
false;
170 if (
const auto *Cast = dyn_cast<CastExpr>(E)) {
172 VaListModelledAsArray =
175 if (
const auto *DeclReg = Reg->getAs<DeclRegion>()) {
176 if (isa<ParmVarDecl>(DeclReg->getDecl()))
177 Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion();
179 IsSymbolic = Reg && Reg->getAs<SymbolicRegion>();
181 const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg);
182 return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg;
185 void ValistChecker::checkPreStmt(
const VAArgExpr *VAA,
186 CheckerContext &C)
const {
189 SVal VAListSVal = C.getSVal(VASubExpr);
191 const MemRegion *VAList =
192 getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C);
197 if (!State->contains<InitializedVALists>(VAList))
198 reportUninitializedAccess(
199 VAList,
"va_arg() is called on an uninitialized va_list", C);
202 void ValistChecker::checkDeadSymbols(SymbolReaper &SR,
203 CheckerContext &C)
const {
205 InitializedVAListsTy TrackedVALists = State->get<InitializedVALists>();
206 RegionVector LeakedVALists;
207 for (
auto Reg : TrackedVALists) {
208 if (SR.isLiveRegion(Reg))
210 LeakedVALists.push_back(Reg);
211 State = State->remove<InitializedVALists>(Reg);
213 if (ExplodedNode *N = C.addTransition(State))
214 reportLeakedVALists(LeakedVALists,
"Initialized va_list",
" is leaked", C,
223 ValistChecker::getStartCallSite(
const ExplodedNode *N,
224 const MemRegion *Reg)
const {
226 const ExplodedNode *StartCallNode = N;
228 bool FoundInitializedState =
false;
232 if (!State->contains<InitializedVALists>(Reg)) {
233 if (FoundInitializedState)
236 FoundInitializedState =
true;
239 if (NContext == LeakContext || NContext->
isParentOf(LeakContext))
241 N = N->pred_empty() ? nullptr : *(N->pred_begin());
244 return StartCallNode;
247 void ValistChecker::reportUninitializedAccess(
const MemRegion *VAList,
249 CheckerContext &C)
const {
250 if (!ChecksEnabled[CK_Uninitialized])
252 if (ExplodedNode *N = C.generateErrorNode()) {
253 if (!BT_uninitaccess)
254 BT_uninitaccess.reset(
new BugType(CheckNames[CK_Uninitialized],
255 "Uninitialized va_list",
257 auto R = llvm::make_unique<BugReport>(*BT_uninitaccess, Msg, N);
258 R->markInteresting(VAList);
259 R->addVisitor(llvm::make_unique<ValistBugVisitor>(VAList));
260 C.emitReport(std::move(R));
264 void ValistChecker::reportLeakedVALists(
const RegionVector &LeakedVALists,
265 StringRef Msg1, StringRef Msg2,
266 CheckerContext &C, ExplodedNode *N,
267 bool ReportUninit)
const {
268 if (!(ChecksEnabled[CK_Unterminated] ||
269 (ChecksEnabled[CK_Uninitialized] && ReportUninit)))
271 for (
auto Reg : LeakedVALists) {
272 if (!BT_leakedvalist) {
275 BT_leakedvalist.reset(
276 new BugType(CheckNames[CK_Unterminated].
getName().empty()
277 ? CheckNames[CK_Uninitialized]
278 : CheckNames[CK_Unterminated],
280 BT_leakedvalist->setSuppressOnSink(
true);
283 const ExplodedNode *StartNode = getStartCallSite(N, Reg);
284 PathDiagnosticLocation LocUsedForUniqueing;
288 StartCallStmt, C.getSourceManager(), StartNode->getLocationContext());
291 llvm::raw_svector_ostream
OS(Buf);
293 std::string VariableName = Reg->getDescriptiveName();
294 if (!VariableName.empty())
295 OS <<
" " << VariableName;
298 auto R = llvm::make_unique<BugReport>(
299 *BT_leakedvalist,
OS.str(), N, LocUsedForUniqueing,
300 StartNode->getLocationContext()->getDecl());
301 R->markInteresting(Reg);
302 R->addVisitor(llvm::make_unique<ValistBugVisitor>(Reg,
true));
303 C.emitReport(std::move(R));
307 void ValistChecker::checkVAListStartCall(
const CallEvent &Call,
308 CheckerContext &C,
bool IsCopy)
const {
310 const MemRegion *VAList =
311 getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C);
318 const MemRegion *Arg2 =
319 getVAListAsRegion(Call.getArgSVal(1), Call.getArgExpr(1), Symbolic, C);
321 if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) {
322 RegionVector LeakedVALists{VAList};
323 if (ExplodedNode *N = C.addTransition(State))
324 reportLeakedVALists(LeakedVALists,
"va_list",
325 " is copied onto itself", C, N,
true);
327 }
else if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) {
328 if (State->contains<InitializedVALists>(VAList)) {
329 State = State->remove<InitializedVALists>(VAList);
330 RegionVector LeakedVALists{VAList};
331 if (ExplodedNode *N = C.addTransition(State))
332 reportLeakedVALists(LeakedVALists,
"Initialized va_list",
333 " is overwritten by an uninitialized one", C, N,
336 reportUninitializedAccess(Arg2,
"Uninitialized va_list is copied", C);
342 if (State->contains<InitializedVALists>(VAList)) {
343 RegionVector LeakedVALists{VAList};
344 if (ExplodedNode *N = C.addTransition(State))
345 reportLeakedVALists(LeakedVALists,
"Initialized va_list",
346 " is initialized again", C, N);
350 State = State->add<InitializedVALists>(VAList);
351 C.addTransition(State);
354 void ValistChecker::checkVAListEndCall(
const CallEvent &Call,
355 CheckerContext &C)
const {
357 const MemRegion *VAList =
358 getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C);
367 if (!C.getState()->contains<InitializedVALists>(VAList)) {
368 reportUninitializedAccess(
369 VAList,
"va_end() is called on an uninitialized va_list", C);
373 State = State->remove<InitializedVALists>(VAList);
374 C.addTransition(State);
377 std::shared_ptr<PathDiagnosticPiece> ValistChecker::ValistBugVisitor::VisitNode(
378 const ExplodedNode *N, BugReporterContext &BRC,
388 if (State->contains<InitializedVALists>(Reg) &&
389 !StatePrev->contains<InitializedVALists>(Reg))
390 Msg =
"Initialized va_list";
391 else if (!State->contains<InitializedVALists>(Reg) &&
392 StatePrev->contains<InitializedVALists>(Reg))
393 Msg =
"Ended va_list";
398 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
399 N->getLocationContext());
400 return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg,
true);
403 #define REGISTER_CHECKER(name) \ 404 void ento::register##name##Checker(CheckerManager &mgr) { \ 405 ValistChecker *checker = mgr.registerChecker<ValistChecker>(); \ 406 checker->ChecksEnabled[ValistChecker::CK_##name] = true; \ 407 checker->CheckNames[ValistChecker::CK_##name] = mgr.getCurrentCheckName(); \
A (possibly-)qualified type.
const char *const MemoryError
Stmt - This represents one statement.
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee...
bool isRecordType() const
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
const Expr * getSubExpr() const
std::string getName(ArrayRef< StringRef > Parts) const
Get the platform-specific name separator.
bool isParentOf(const LocationContext *LC) const
Represents a call to the builtin function __builtin_va_arg.
This represents one expression.
static const Stmt * getStmt(const ExplodedNode *N)
Given an exploded node, retrieve the statement that should be used for the diagnostic location...
static PathDiagnosticLocation createBegin(const Decl *D, const SourceManager &SM)
Create a location for the beginning of the declaration.
#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set of type NameTy, suitable for placement into the ProgramState.
Dataflow Directional Tag Classes.
Indicates that the tracking object is a descendant of a referenced-counted OSObject, used in the Darwin kernel.
#define REGISTER_CHECKER(name)
X
Add a minimal nested name specifier fixit hint to allow lookup of a tag name from an outer enclosing ...
static PathDiagnosticLocation createEndOfPath(const ExplodedNode *N, const SourceManager &SM)
Create a location corresponding to the next valid ExplodedNode as end of path location.
bool isPointerType() const