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 bool ChecksEnabled[CK_NumCheckKinds] = {
false};
50 CheckerNameRef 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;
72 class ValistBugVisitor :
public BugReporterVisitor {
74 ValistBugVisitor(
const MemRegion *Reg,
bool IsLeak =
false)
75 : Reg(Reg), IsLeak(IsLeak) {}
76 void Profile(llvm::FoldingSetNodeID &
ID)
const override {
82 const ExplodedNode *EndPathNode,
83 PathSensitiveBugReport &BR)
override {
87 PathDiagnosticLocation L = BR.getLocation();
89 return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),
93 BugReporterContext &BRC,
94 PathSensitiveBugReport &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}};
120 const CallDescription
121 ValistChecker::VaStart(
"__builtin_va_start", 2, 1),
122 ValistChecker::VaCopy(
"__builtin_va_copy", 2),
123 ValistChecker::VaEnd(
"__builtin_va_end", 1);
126 void ValistChecker::checkPreCall(
const CallEvent &Call,
127 CheckerContext &C)
const {
128 if (!
Call.isGlobalCFunction())
130 if (VaStart.matches(Call))
131 checkVAListStartCall(Call, C,
false);
132 else if (VaCopy.matches(Call))
133 checkVAListStartCall(Call, C,
true);
134 else if (VaEnd.matches(Call))
135 checkVAListEndCall(Call, C);
137 for (
auto FuncInfo : VAListAccepters) {
138 if (!FuncInfo.Func.matches(Call))
141 const MemRegion *VAList =
142 getVAListAsRegion(
Call.getArgSVal(FuncInfo.VAListPos),
143 Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C);
147 if (
C.getState()->contains<InitializedVALists>(VAList))
156 Errmsg += FuncInfo.Func.getFunctionName();
157 Errmsg +=
"' is called with an uninitialized va_list argument";
158 reportUninitializedAccess(VAList, Errmsg.c_str(), C);
164 const MemRegion *ValistChecker::getVAListAsRegion(SVal SV,
const Expr *E,
166 CheckerContext &C)
const {
167 const MemRegion *Reg = SV.getAsRegion();
171 bool VaListModelledAsArray =
false;
172 if (
const auto *
Cast = dyn_cast<CastExpr>(E)) {
174 VaListModelledAsArray =
177 if (
const auto *DeclReg = Reg->getAs<DeclRegion>()) {
178 if (isa<ParmVarDecl>(DeclReg->getDecl()))
179 Reg =
C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion();
181 IsSymbolic = Reg && Reg->getBaseRegion()->getAs<SymbolicRegion>();
183 const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg);
184 return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg;
187 void ValistChecker::checkPreStmt(
const VAArgExpr *VAA,
188 CheckerContext &C)
const {
191 SVal VAListSVal =
C.getSVal(VASubExpr);
193 const MemRegion *VAList =
194 getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C);
199 if (!
State->contains<InitializedVALists>(VAList))
200 reportUninitializedAccess(
201 VAList,
"va_arg() is called on an uninitialized va_list", C);
204 void ValistChecker::checkDeadSymbols(SymbolReaper &SR,
205 CheckerContext &C)
const {
207 InitializedVAListsTy TrackedVALists =
State->get<InitializedVALists>();
208 RegionVector LeakedVALists;
209 for (
auto Reg : TrackedVALists) {
210 if (SR.isLiveRegion(Reg))
212 LeakedVALists.push_back(Reg);
213 State =
State->remove<InitializedVALists>(Reg);
215 if (ExplodedNode *N =
C.addTransition(
State))
216 reportLeakedVALists(LeakedVALists,
"Initialized va_list",
" is leaked", C,
225 ValistChecker::getStartCallSite(
const ExplodedNode *N,
226 const MemRegion *Reg)
const {
228 const ExplodedNode *StartCallNode = N;
230 bool FoundInitializedState =
false;
234 if (!
State->contains<InitializedVALists>(Reg)) {
235 if (FoundInitializedState)
238 FoundInitializedState =
true;
241 if (NContext == LeakContext || NContext->
isParentOf(LeakContext))
243 N = N->pred_empty() ? nullptr : *(N->pred_begin());
246 return StartCallNode;
249 void ValistChecker::reportUninitializedAccess(
const MemRegion *VAList,
251 CheckerContext &C)
const {
252 if (!ChecksEnabled[CK_Uninitialized])
254 if (ExplodedNode *N =
C.generateErrorNode()) {
255 if (!BT_uninitaccess)
256 BT_uninitaccess.reset(
new BugType(CheckNames[CK_Uninitialized],
257 "Uninitialized va_list",
259 auto R = std::make_unique<PathSensitiveBugReport>(*BT_uninitaccess, Msg, N);
260 R->markInteresting(VAList);
261 R->addVisitor(std::make_unique<ValistBugVisitor>(VAList));
262 C.emitReport(std::move(R));
266 void ValistChecker::reportLeakedVALists(
const RegionVector &LeakedVALists,
267 StringRef Msg1, StringRef Msg2,
268 CheckerContext &C, ExplodedNode *N,
269 bool ReportUninit)
const {
270 if (!(ChecksEnabled[CK_Unterminated] ||
271 (ChecksEnabled[CK_Uninitialized] && ReportUninit)))
273 for (
auto Reg : LeakedVALists) {
274 if (!BT_leakedvalist) {
277 BT_leakedvalist.reset(
278 new BugType(CheckNames[CK_Unterminated].
getName().empty()
279 ? CheckNames[CK_Uninitialized]
280 : CheckNames[CK_Unterminated],
285 const ExplodedNode *StartNode = getStartCallSite(N, Reg);
286 PathDiagnosticLocation LocUsedForUniqueing;
288 if (
const Stmt *StartCallStmt = StartNode->getStmtForDiagnostics())
290 StartCallStmt,
C.getSourceManager(), StartNode->getLocationContext());
293 llvm::raw_svector_ostream
OS(Buf);
295 std::string VariableName = Reg->getDescriptiveName();
296 if (!VariableName.empty())
297 OS <<
" " << VariableName;
300 auto R = std::make_unique<PathSensitiveBugReport>(
301 *BT_leakedvalist,
OS.str(), N, LocUsedForUniqueing,
302 StartNode->getLocationContext()->getDecl());
303 R->markInteresting(Reg);
304 R->addVisitor(std::make_unique<ValistBugVisitor>(Reg,
true));
305 C.emitReport(std::move(R));
309 void ValistChecker::checkVAListStartCall(
const CallEvent &Call,
310 CheckerContext &C,
bool IsCopy)
const {
312 const MemRegion *VAList =
313 getVAListAsRegion(
Call.getArgSVal(0),
Call.getArgExpr(0), Symbolic, C);
320 const MemRegion *Arg2 =
321 getVAListAsRegion(
Call.getArgSVal(1),
Call.getArgExpr(1), Symbolic, C);
323 if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) {
324 RegionVector LeakedVALists{VAList};
325 if (ExplodedNode *N =
C.addTransition(
State))
326 reportLeakedVALists(LeakedVALists,
"va_list",
327 " is copied onto itself", C, N,
true);
329 }
else if (!
State->contains<InitializedVALists>(Arg2) && !Symbolic) {
330 if (
State->contains<InitializedVALists>(VAList)) {
331 State =
State->remove<InitializedVALists>(VAList);
332 RegionVector LeakedVALists{VAList};
333 if (ExplodedNode *N =
C.addTransition(
State))
334 reportLeakedVALists(LeakedVALists,
"Initialized va_list",
335 " is overwritten by an uninitialized one", C, N,
338 reportUninitializedAccess(Arg2,
"Uninitialized va_list is copied", C);
344 if (
State->contains<InitializedVALists>(VAList)) {
345 RegionVector LeakedVALists{VAList};
346 if (ExplodedNode *N =
C.addTransition(
State))
347 reportLeakedVALists(LeakedVALists,
"Initialized va_list",
348 " is initialized again", C, N);
352 State =
State->add<InitializedVALists>(VAList);
356 void ValistChecker::checkVAListEndCall(
const CallEvent &Call,
357 CheckerContext &C)
const {
359 const MemRegion *VAList =
360 getVAListAsRegion(
Call.getArgSVal(0),
Call.getArgExpr(0), Symbolic, C);
369 if (!
C.getState()->contains<InitializedVALists>(VAList)) {
370 reportUninitializedAccess(
371 VAList,
"va_end() is called on an uninitialized va_list", C);
375 State =
State->remove<InitializedVALists>(VAList);
380 const ExplodedNode *N, BugReporterContext &BRC, PathSensitiveBugReport &) {
384 const Stmt *S = N->getStmtForDiagnostics();
389 if (
State->contains<InitializedVALists>(Reg) &&
390 !StatePrev->contains<InitializedVALists>(Reg))
391 Msg =
"Initialized va_list";
392 else if (!
State->contains<InitializedVALists>(Reg) &&
393 StatePrev->contains<InitializedVALists>(Reg))
394 Msg =
"Ended va_list";
399 PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
400 N->getLocationContext());
401 return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg,
true);
404 void ento::registerValistBase(CheckerManager &mgr) {
405 mgr.registerChecker<ValistChecker>();
408 bool ento::shouldRegisterValistBase(
const CheckerManager &mgr) {
412 #define REGISTER_CHECKER(name) \
413 void ento::register##name##Checker(CheckerManager &mgr) { \
414 ValistChecker *checker = mgr.getChecker<ValistChecker>(); \
415 checker->ChecksEnabled[ValistChecker::CK_##name] = true; \
416 checker->CheckNames[ValistChecker::CK_##name] = \
417 mgr.getCurrentCheckerName(); \
420 bool ento::shouldRegister##name##Checker(const CheckerManager &mgr) { \