Skip to content

Commit 56e8aa5

Browse files
committedAug 18, 2016
[analyzer] Added valist related checkers.
Differential Revision: https://reviews.llvm.org/D15227 llvm-svn: 279041
1 parent 4b6f959 commit 56e8aa5

File tree

6 files changed

+739
-0
lines changed

6 files changed

+739
-0
lines changed
 

‎clang/include/clang/StaticAnalyzer/Checkers/Checkers.td

+24
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ def Nullability : Package<"nullability">;
4343
def Cplusplus : Package<"cplusplus">;
4444
def CplusplusAlpha : Package<"cplusplus">, InPackage<Alpha>, Hidden;
4545

46+
def Valist : Package<"valist">;
47+
def ValistAlpha : Package<"valist">, InPackage<Alpha>, Hidden;
48+
4649
def DeadCode : Package<"deadcode">;
4750
def DeadCodeAlpha : Package<"deadcode">, InPackage<Alpha>, Hidden;
4851

@@ -267,6 +270,27 @@ def VirtualCallChecker : Checker<"VirtualCall">,
267270

268271
} // end: "alpha.cplusplus"
269272

273+
274+
//===----------------------------------------------------------------------===//
275+
// Valist checkers.
276+
//===----------------------------------------------------------------------===//
277+
278+
let ParentPackage = ValistAlpha in {
279+
280+
def UninitializedChecker : Checker<"Uninitialized">,
281+
HelpText<"Check for usages of uninitialized (or already released) va_lists.">,
282+
DescFile<"ValistChecker.cpp">;
283+
284+
def UnterminatedChecker : Checker<"Unterminated">,
285+
HelpText<"Check for va_lists which are not released by a va_end call.">,
286+
DescFile<"ValistChecker.cpp">;
287+
288+
def CopyToSelfChecker : Checker<"CopyToSelf">,
289+
HelpText<"Check for va_lists which are copied onto itself.">,
290+
DescFile<"ValistChecker.cpp">;
291+
292+
} // end : "alpha.valist"
293+
270294
//===----------------------------------------------------------------------===//
271295
// Deadcode checkers.
272296
//===----------------------------------------------------------------------===//

‎clang/lib/StaticAnalyzer/Checkers/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ add_clang_library(clangStaticAnalyzerCheckers
8181
UnreachableCodeChecker.cpp
8282
VforkChecker.cpp
8383
VLASizeChecker.cpp
84+
ValistChecker.cpp
8485
VirtualCallChecker.cpp
8586

8687
DEPENDS
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
//== ValistChecker.cpp - stdarg.h macro usage checker -----------*- C++ -*--==//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This defines checkers which detect usage of uninitialized va_list values
11+
// and va_start calls with no matching va_end.
12+
//
13+
//===----------------------------------------------------------------------===//
14+
15+
#include "ClangSACheckers.h"
16+
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17+
#include "clang/StaticAnalyzer/Core/Checker.h"
18+
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
19+
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
20+
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21+
22+
using namespace clang;
23+
using namespace ento;
24+
25+
REGISTER_SET_WITH_PROGRAMSTATE(InitializedVALists, const MemRegion *)
26+
27+
namespace {
28+
typedef SmallVector<const MemRegion *, 2> RegionVector;
29+
30+
class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
31+
check::DeadSymbols> {
32+
mutable std::unique_ptr<BugType> BT_leakedvalist, BT_uninitaccess;
33+
34+
struct VAListAccepter {
35+
CallDescription Func;
36+
int VAListPos;
37+
};
38+
static const SmallVector<VAListAccepter, 15> VAListAccepters;
39+
static const CallDescription VaStart, VaEnd, VaCopy;
40+
41+
public:
42+
enum CheckKind {
43+
CK_Uninitialized,
44+
CK_Unterminated,
45+
CK_CopyToSelf,
46+
CK_NumCheckKinds
47+
};
48+
49+
DefaultBool ChecksEnabled[CK_NumCheckKinds];
50+
CheckName CheckNames[CK_NumCheckKinds];
51+
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;
55+
56+
private:
57+
const MemRegion *getVAListAsRegion(SVal SV, CheckerContext &C) const;
58+
StringRef getVariableNameFromRegion(const MemRegion *Reg) const;
59+
const ExplodedNode *getStartCallSite(const ExplodedNode *N,
60+
const MemRegion *Reg,
61+
CheckerContext &C) const;
62+
63+
void reportUninitializedAccess(const MemRegion *VAList, StringRef Msg,
64+
CheckerContext &C) const;
65+
void reportLeakedVALists(const RegionVector &LeakedVALists, StringRef Msg1,
66+
StringRef Msg2, CheckerContext &C, ExplodedNode *N,
67+
bool ForceReport = false) const;
68+
69+
void checkVAListStartCall(const CallEvent &Call, CheckerContext &C,
70+
bool IsCopy) const;
71+
void checkVAListEndCall(const CallEvent &Call, CheckerContext &C) const;
72+
73+
class ValistBugVisitor : public BugReporterVisitorImpl<ValistBugVisitor> {
74+
public:
75+
ValistBugVisitor(const MemRegion *Reg, bool IsLeak = false)
76+
: Reg(Reg), IsLeak(IsLeak) {}
77+
void Profile(llvm::FoldingSetNodeID &ID) const override {
78+
static int X = 0;
79+
ID.AddPointer(&X);
80+
ID.AddPointer(Reg);
81+
}
82+
std::unique_ptr<PathDiagnosticPiece>
83+
getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
84+
BugReport &BR) override {
85+
if (!IsLeak)
86+
return nullptr;
87+
88+
PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(
89+
EndPathNode, BRC.getSourceManager());
90+
// Do not add the statement itself as a range in case of leak.
91+
return llvm::make_unique<PathDiagnosticEventPiece>(L, BR.getDescription(),
92+
false);
93+
}
94+
PathDiagnosticPiece *VisitNode(const ExplodedNode *N,
95+
const ExplodedNode *PrevN,
96+
BugReporterContext &BRC,
97+
BugReport &BR) override;
98+
99+
private:
100+
const MemRegion *Reg;
101+
bool IsLeak;
102+
};
103+
};
104+
105+
const SmallVector<ValistChecker::VAListAccepter, 15>
106+
ValistChecker::VAListAccepters = {
107+
{{"vfprintf", 3}, 2},
108+
{{"vfscanf", 3}, 2},
109+
{{"vprintf", 2}, 1},
110+
{{"vscanf", 2}, 1},
111+
{{"vsnprintf", 4}, 3},
112+
{{"vsprintf", 3}, 2},
113+
{{"vsscanf", 3}, 2},
114+
{{"vfwprintf", 3}, 2},
115+
{{"vfwscanf", 3}, 2},
116+
{{"vwprintf", 2}, 1},
117+
{{"vwscanf", 2}, 1},
118+
{{"vswprintf", 4}, 3},
119+
// vswprintf is the wide version of vsnprintf,
120+
// vsprintf has no wide version
121+
{{"vswscanf", 3}, 2}};
122+
const CallDescription ValistChecker::VaStart("__builtin_va_start", 2),
123+
ValistChecker::VaCopy("__builtin_va_copy", 2),
124+
ValistChecker::VaEnd("__builtin_va_end", 1);
125+
} // end anonymous namespace
126+
127+
void ValistChecker::checkPreCall(const CallEvent &Call,
128+
CheckerContext &C) const {
129+
if (!Call.isGlobalCFunction())
130+
return;
131+
if (Call.isCalled(VaStart))
132+
checkVAListStartCall(Call, C, false);
133+
else if (Call.isCalled(VaCopy))
134+
checkVAListStartCall(Call, C, true);
135+
else if (Call.isCalled(VaEnd))
136+
checkVAListEndCall(Call, C);
137+
else {
138+
for (auto FuncInfo : VAListAccepters) {
139+
if (!Call.isCalled(FuncInfo.Func))
140+
continue;
141+
const MemRegion *VAList =
142+
getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos), C);
143+
if (!VAList)
144+
return;
145+
146+
if (C.getState()->contains<InitializedVALists>(VAList))
147+
return;
148+
149+
SmallString<80> Errmsg("Function '");
150+
Errmsg += FuncInfo.Func.getFunctionName();
151+
Errmsg += "' is called with an uninitialized va_list argument";
152+
reportUninitializedAccess(VAList, Errmsg.c_str(), C);
153+
break;
154+
}
155+
}
156+
}
157+
158+
void ValistChecker::checkPreStmt(const VAArgExpr *VAA,
159+
CheckerContext &C) const {
160+
ProgramStateRef State = C.getState();
161+
SVal VAListSVal = State->getSVal(VAA->getSubExpr(), C.getLocationContext());
162+
const MemRegion *VAList = getVAListAsRegion(VAListSVal, C);
163+
if (!VAList)
164+
return;
165+
if (!State->contains<InitializedVALists>(VAList))
166+
reportUninitializedAccess(
167+
VAList, "va_arg() is called on an uninitialized va_list", C);
168+
}
169+
170+
void ValistChecker::checkDeadSymbols(SymbolReaper &SR,
171+
CheckerContext &C) const {
172+
ProgramStateRef State = C.getState();
173+
InitializedVAListsTy TrackedVALists = State->get<InitializedVALists>();
174+
RegionVector LeakedVALists;
175+
for (auto Reg : TrackedVALists) {
176+
if (SR.isLiveRegion(Reg))
177+
continue;
178+
LeakedVALists.push_back(Reg);
179+
State = State->remove<InitializedVALists>(Reg);
180+
}
181+
if (ExplodedNode *N = C.addTransition(State))
182+
reportLeakedVALists(LeakedVALists, "Initialized va_list", " is leaked", C,
183+
N);
184+
}
185+
186+
const MemRegion *ValistChecker::getVAListAsRegion(SVal SV,
187+
CheckerContext &C) const {
188+
const MemRegion *Reg = SV.getAsRegion();
189+
const auto *TReg = dyn_cast_or_null<TypedValueRegion>(Reg);
190+
// Some VarRegion based VLAs reach here as ElementRegions.
191+
const auto *EReg = dyn_cast_or_null<ElementRegion>(TReg);
192+
return EReg ? EReg->getSuperRegion() : TReg;
193+
}
194+
195+
// This function traverses the exploded graph backwards and finds the node where
196+
// the va_list is initialized. That node is used for uniquing the bug paths.
197+
// It is not likely that there are several different va_lists that belongs to
198+
// different stack frames, so that case is not yet handled.
199+
const ExplodedNode *ValistChecker::getStartCallSite(const ExplodedNode *N,
200+
const MemRegion *Reg,
201+
CheckerContext &C) const {
202+
const LocationContext *LeakContext = N->getLocationContext();
203+
const ExplodedNode *StartCallNode = N;
204+
205+
bool FoundInitializedState = false;
206+
207+
while (N) {
208+
ProgramStateRef State = N->getState();
209+
if (!State->contains<InitializedVALists>(Reg)) {
210+
if (FoundInitializedState)
211+
break;
212+
} else {
213+
FoundInitializedState = true;
214+
}
215+
const LocationContext *NContext = N->getLocationContext();
216+
if (NContext == LeakContext || NContext->isParentOf(LeakContext))
217+
StartCallNode = N;
218+
N = N->pred_empty() ? nullptr : *(N->pred_begin());
219+
}
220+
221+
return StartCallNode;
222+
}
223+
224+
void ValistChecker::reportUninitializedAccess(const MemRegion *VAList,
225+
StringRef Msg,
226+
CheckerContext &C) const {
227+
if (!ChecksEnabled[CK_Uninitialized])
228+
return;
229+
if (ExplodedNode *N = C.generateErrorNode()) {
230+
if (!BT_uninitaccess)
231+
BT_uninitaccess.reset(new BugType(CheckNames[CK_Uninitialized],
232+
"Uninitialized va_list",
233+
"Memory Error"));
234+
auto R = llvm::make_unique<BugReport>(*BT_uninitaccess, Msg, N);
235+
R->markInteresting(VAList);
236+
R->addVisitor(llvm::make_unique<ValistBugVisitor>(VAList));
237+
C.emitReport(std::move(R));
238+
}
239+
}
240+
241+
void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
242+
StringRef Msg1, StringRef Msg2,
243+
CheckerContext &C, ExplodedNode *N,
244+
bool ForceReport) const {
245+
if (!(ChecksEnabled[CK_Unterminated] ||
246+
(ChecksEnabled[CK_Uninitialized] && ForceReport)))
247+
return;
248+
for (auto Reg : LeakedVALists) {
249+
if (!BT_leakedvalist) {
250+
BT_leakedvalist.reset(new BugType(CheckNames[CK_Unterminated],
251+
"Leaked va_list", "Memory Error"));
252+
BT_leakedvalist->setSuppressOnSink(true);
253+
}
254+
255+
const ExplodedNode *StartNode = getStartCallSite(N, Reg, C);
256+
PathDiagnosticLocation LocUsedForUniqueing;
257+
258+
if (const Stmt *StartCallStmt = PathDiagnosticLocation::getStmt(StartNode))
259+
LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
260+
StartCallStmt, C.getSourceManager(), StartNode->getLocationContext());
261+
262+
SmallString<100> Buf;
263+
llvm::raw_svector_ostream OS(Buf);
264+
OS << Msg1;
265+
StringRef VariableName = Reg->getDescriptiveName();
266+
if (!VariableName.empty())
267+
OS << " " << VariableName;
268+
OS << Msg2;
269+
270+
auto R = llvm::make_unique<BugReport>(
271+
*BT_leakedvalist, OS.str(), N, LocUsedForUniqueing,
272+
StartNode->getLocationContext()->getDecl());
273+
R->markInteresting(Reg);
274+
R->addVisitor(llvm::make_unique<ValistBugVisitor>(Reg, true));
275+
C.emitReport(std::move(R));
276+
}
277+
}
278+
279+
void ValistChecker::checkVAListStartCall(const CallEvent &Call,
280+
CheckerContext &C, bool IsCopy) const {
281+
const MemRegion *VAList = getVAListAsRegion(Call.getArgSVal(0), C);
282+
ProgramStateRef State = C.getState();
283+
if (!VAList)
284+
return;
285+
286+
if (IsCopy) {
287+
const MemRegion *Arg2 = getVAListAsRegion(Call.getArgSVal(1), C);
288+
if (Arg2) {
289+
if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) {
290+
RegionVector LeakedVALists{VAList};
291+
if (ExplodedNode *N = C.addTransition(State))
292+
reportLeakedVALists(LeakedVALists, "va_list",
293+
" is copied onto itself", C, N, true);
294+
return;
295+
} else if (!State->contains<InitializedVALists>(Arg2)) {
296+
if (State->contains<InitializedVALists>(VAList)) {
297+
State = State->remove<InitializedVALists>(VAList);
298+
RegionVector LeakedVALists{VAList};
299+
if (ExplodedNode *N = C.addTransition(State))
300+
reportLeakedVALists(LeakedVALists, "Initialized va_list",
301+
" is overwritten by an uninitialized one", C, N,
302+
true);
303+
} else {
304+
reportUninitializedAccess(Arg2, "Uninitialized va_list is copied", C);
305+
}
306+
return;
307+
}
308+
}
309+
}
310+
if (State->contains<InitializedVALists>(VAList)) {
311+
RegionVector LeakedVALists{VAList};
312+
if (ExplodedNode *N = C.addTransition(State))
313+
reportLeakedVALists(LeakedVALists, "Initialized va_list",
314+
" is initialized again", C, N);
315+
return;
316+
}
317+
318+
State = State->add<InitializedVALists>(VAList);
319+
C.addTransition(State);
320+
}
321+
322+
void ValistChecker::checkVAListEndCall(const CallEvent &Call,
323+
CheckerContext &C) const {
324+
const MemRegion *VAList = getVAListAsRegion(Call.getArgSVal(0), C);
325+
if (!VAList)
326+
return;
327+
328+
if (!C.getState()->contains<InitializedVALists>(VAList)) {
329+
reportUninitializedAccess(
330+
VAList, "va_end() is called on an uninitialized va_list", C);
331+
return;
332+
}
333+
ProgramStateRef State = C.getState();
334+
State = State->remove<InitializedVALists>(VAList);
335+
C.addTransition(State);
336+
}
337+
338+
PathDiagnosticPiece *ValistChecker::ValistBugVisitor::VisitNode(
339+
const ExplodedNode *N, const ExplodedNode *PrevN, BugReporterContext &BRC,
340+
BugReport &BR) {
341+
ProgramStateRef State = N->getState();
342+
ProgramStateRef StatePrev = PrevN->getState();
343+
344+
const Stmt *S = PathDiagnosticLocation::getStmt(N);
345+
if (!S)
346+
return nullptr;
347+
348+
StringRef Msg;
349+
if (State->contains<InitializedVALists>(Reg) &&
350+
!StatePrev->contains<InitializedVALists>(Reg))
351+
Msg = "Initialized va_list";
352+
else if (!State->contains<InitializedVALists>(Reg) &&
353+
StatePrev->contains<InitializedVALists>(Reg))
354+
Msg = "Ended va_list";
355+
356+
if (Msg.empty())
357+
return nullptr;
358+
359+
PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
360+
N->getLocationContext());
361+
return new PathDiagnosticEventPiece(Pos, Msg, true);
362+
}
363+
364+
#define REGISTER_CHECKER(name) \
365+
void ento::register##name##Checker(CheckerManager &mgr) { \
366+
ValistChecker *checker = mgr.registerChecker<ValistChecker>(); \
367+
checker->ChecksEnabled[ValistChecker::CK_##name] = true; \
368+
checker->CheckNames[ValistChecker::CK_##name] = mgr.getCurrentCheckName(); \
369+
}
370+
371+
REGISTER_CHECKER(Uninitialized)
372+
REGISTER_CHECKER(Unterminated)
373+
REGISTER_CHECKER(CopyToSelf)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Like the compiler, the static analyzer treats some functions differently if
2+
// they come from a system header -- for example, it is assumed that system
3+
// functions do not arbitrarily free() their parameters, and that some bugs
4+
// found in system headers cannot be fixed by the user and should be
5+
// suppressed.
6+
7+
#pragma clang system_header
8+
9+
#ifdef __cplusplus
10+
#define restrict /*restrict*/
11+
#endif
12+
13+
typedef __builtin_va_list va_list;
14+
15+
#define va_start(ap, param) __builtin_va_start(ap, param)
16+
#define va_end(ap) __builtin_va_end(ap)
17+
#define va_arg(ap, type) __builtin_va_arg(ap, type)
18+
#define va_copy(dst, src) __builtin_va_copy(dst, src)
19+
20+
int vprintf (const char *restrict format, va_list arg);
21+
22+
int vsprintf (char *restrict s, const char *restrict format, va_list arg);
23+
24+
int some_library_function(int n, va_list arg);
25+
26+
// No warning from system header.
27+
inline void __impl_detail(int fst, ...) {
28+
va_list va;
29+
(void)va_arg(va, int);
30+
}
+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.valist.Uninitialized,alpha.valist.CopyToSelf -analyzer-output=text -analyzer-store=region -verify %s
2+
3+
#include "Inputs/system-header-simulator-for-valist.h"
4+
5+
void f1(int fst, ...) {
6+
va_list va;
7+
(void)va_arg(va, int); //expected-warning{{va_arg() is called on an uninitialized va_list}} expected-note{{va_arg() is called on an uninitialized va_list}}
8+
}
9+
10+
int f2(int fst, ...) {
11+
va_list va;
12+
va_start(va, fst); // expected-note{{Initialized va_list}}
13+
va_end(va); // expected-note{{Ended va_list}}
14+
return va_arg(va, int); //expected-warning{{va_arg() is called on an uninitialized va_list}} expected-note{{va_arg() is called on an uninitialized va_list}}
15+
}
16+
17+
void f3(int fst, ...) {
18+
va_list va, va2;
19+
va_start(va, fst);
20+
va_copy(va2, va);
21+
va_end(va);
22+
(void)va_arg(va2, int);
23+
va_end(va2);
24+
} //no-warning
25+
26+
void f4(int cond, ...) {
27+
va_list va;
28+
if (cond) { // expected-note{{Assuming 'cond' is 0}} expected-note{{Taking false branch}}
29+
va_start(va, cond);
30+
(void)va_arg(va,int);
31+
}
32+
va_end(va); //expected-warning{{va_end() is called on an uninitialized va_list}} expected-note{{va_end() is called on an uninitialized va_list}}
33+
}
34+
35+
void f5(va_list fst, ...) {
36+
va_start(fst, fst);
37+
(void)va_arg(fst, int);
38+
va_end(fst);
39+
} // no-warning
40+
41+
//FIXME: this should not cause a warning
42+
void f6(va_list *fst, ...) {
43+
va_start(*fst, fst);
44+
(void)va_arg(*fst, int); //expected-warning{{va_arg() is called on an uninitialized va_list}} expected-note{{va_arg() is called on an uninitialized va_list}}
45+
va_end(*fst);
46+
}
47+
48+
void f7(int *fst, ...) {
49+
va_list x;
50+
va_list *y = &x;
51+
va_start(*y,fst);
52+
(void)va_arg(x, int);
53+
va_end(x);
54+
} // no-warning
55+
56+
void f8(int *fst, ...) {
57+
va_list x;
58+
va_list *y = &x;
59+
va_start(*y,fst); // expected-note{{Initialized va_list}}
60+
va_end(x); // expected-note{{Ended va_list}}
61+
(void)va_arg(*y, int); //expected-warning{{va_arg() is called on an uninitialized va_list}} expected-note{{va_arg() is called on an uninitialized va_list}}
62+
} // no-warning
63+
64+
// This only contains problems which are handled by varargs.Unterminated.
65+
void reinit(int *fst, ...) {
66+
va_list va;
67+
va_start(va, fst);
68+
va_start(va, fst);
69+
(void)va_arg(va, int);
70+
} // no-warning
71+
72+
void reinitOk(int *fst, ...) {
73+
va_list va;
74+
va_start(va, fst);
75+
(void)va_arg(va, int);
76+
va_end(va);
77+
va_start(va, fst);
78+
(void)va_arg(va, int);
79+
va_end(va);
80+
} // no-warning
81+
82+
void reinit3(int *fst, ...) {
83+
va_list va;
84+
va_start(va, fst); // expected-note{{Initialized va_list}}
85+
(void)va_arg(va, int);
86+
va_end(va); // expected-note{{Ended va_list}}
87+
va_start(va, fst); // expected-note{{Initialized va_list}}
88+
(void)va_arg(va, int);
89+
va_end(va); // expected-note{{Ended va_list}}
90+
(void)va_arg(va, int); //expected-warning{{va_arg() is called on an uninitialized va_list}} expected-note{{va_arg() is called on an uninitialized va_list}}
91+
}
92+
93+
void copyself(int fst, ...) {
94+
va_list va;
95+
va_start(va, fst); // expected-note{{Initialized va_list}}
96+
va_copy(va, va); // expected-warning{{va_list 'va' is copied onto itself}} expected-note{{va_list 'va' is copied onto itself}}
97+
va_end(va);
98+
} // no-warning
99+
100+
void copyselfUninit(int fst, ...) {
101+
va_list va;
102+
va_copy(va, va); // expected-warning{{va_list 'va' is copied onto itself}} expected-note{{va_list 'va' is copied onto itself}}
103+
} // no-warning
104+
105+
void copyOverwrite(int fst, ...) {
106+
va_list va, va2;
107+
va_start(va, fst); // expected-note{{Initialized va_list}}
108+
va_copy(va, va2); // expected-warning{{Initialized va_list 'va' is overwritten by an uninitialized one}} expected-note{{Initialized va_list 'va' is overwritten by an uninitialized one}}
109+
} // no-warning
110+
111+
void copyUnint(int fst, ...) {
112+
va_list va, va2;
113+
va_copy(va, va2); // expected-warning{{Uninitialized va_list is copied}} expected-note{{Uninitialized va_list is copied}}
114+
}
115+
116+
void g1(int fst, ...) {
117+
va_list va;
118+
va_end(va); // expected-warning{{va_end() is called on an uninitialized va_list}} expected-note{{va_end() is called on an uninitialized va_list}}
119+
}
120+
121+
void g2(int fst, ...) {
122+
va_list va;
123+
va_start(va, fst); // expected-note{{Initialized va_list}}
124+
va_end(va); // expected-note{{Ended va_list}}
125+
va_end(va); // expected-warning{{va_end() is called on an uninitialized va_list}} expected-note{{va_end() is called on an uninitialized va_list}}
126+
}
127+
128+
void is_sink(int fst, ...) {
129+
va_list va;
130+
va_end(va); // expected-warning{{va_end() is called on an uninitialized va_list}} expected-note{{va_end() is called on an uninitialized va_list}}
131+
*((volatile int *)0) = 1; //no-warning
132+
}
133+
134+
// NOTE: this is invalid, as the man page of va_end requires that "Each invocation of va_start()
135+
// must be matched by a corresponding invocation of va_end() in the same function."
136+
void ends_arg(va_list arg) {
137+
va_end(arg);
138+
} //no-warning
139+
140+
void uses_arg(va_list arg) {
141+
(void)va_arg(arg, int);
142+
} //no-warning
143+
144+
// This is the same function as the previous one, but it is called in call_uses_arg2(),
145+
// and the warning is generated during the analysis of call_uses_arg2().
146+
void inlined_uses_arg(va_list arg) {
147+
(void)va_arg(arg, int); //expected-warning{{va_arg() is called on an uninitialized va_list}} expected-note{{va_arg() is called on an uninitialized va_list}}
148+
}
149+
150+
void call_inlined_uses_arg(int fst, ...) {
151+
va_list va;
152+
inlined_uses_arg(va); // expected-note{{Calling 'inlined_uses_arg'}}
153+
}
154+
155+
void call_vprintf_ok(int isstring, ...) {
156+
va_list va;
157+
va_start(va, isstring);
158+
vprintf(isstring ? "%s" : "%d", va);
159+
va_end(va);
160+
} //no-warning
161+
162+
void call_vprintf_bad(int isstring, ...) {
163+
va_list va;
164+
vprintf(isstring ? "%s" : "%d", va); //expected-warning{{Function 'vprintf' is called with an uninitialized va_list argument}} expected-note{{Function 'vprintf' is called with an uninitialized va_list argument}} expected-note{{Assuming 'isstring' is 0}} expected-note{{'?' condition is false}}
165+
}
166+
167+
void call_vsprintf_bad(char *buffer, ...) {
168+
va_list va;
169+
va_start(va, buffer); // expected-note{{Initialized va_list}}
170+
va_end(va); // expected-note{{Ended va_list}}
171+
vsprintf(buffer, "%s %d %d %lf %03d", va); //expected-warning{{Function 'vsprintf' is called with an uninitialized va_list argument}} expected-note{{Function 'vsprintf' is called with an uninitialized va_list argument}}
172+
}
173+
174+
void call_some_other_func(int n, ...) {
175+
va_list va;
176+
some_library_function(n, va);
177+
} //no-warning
178+
+133
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.valist.Unterminated,alpha.valist.CopyToSelf -analyzer-output=text -analyzer-store=region -verify %s
2+
3+
#include "Inputs/system-header-simulator-for-valist.h"
4+
5+
void f1(int fst, ...) {
6+
va_list va;
7+
va_start(va, fst); // expected-note{{Initialized va_list}}
8+
return; // expected-warning{{Initialized va_list 'va' is leaked}} expected-note{{Initialized va_list 'va' is leaked}}
9+
}
10+
11+
void f2(int fst, ...) {
12+
va_list va;
13+
va_start(va, fst); // expected-note{{Initialized va_list}}
14+
va_end(va); // expected-note{{Ended va_list}}
15+
va_start(va, fst); // expected-note{{Initialized va_list}}
16+
} // expected-warning{{Initialized va_list 'va' is leaked}} expected-note{{Initialized va_list 'va' is leaked}}}
17+
18+
void f3(int fst, ...) {
19+
va_list va, va2;
20+
va_start(va, fst);
21+
va_copy(va2, va); // expected-note{{Initialized va_list}}
22+
va_end(va); // expected-warning{{Initialized va_list 'va2' is leaked}} expected-note{{Initialized va_list 'va2' is leaked}}
23+
}
24+
25+
void f4(va_list *fst, ...) {
26+
va_start(*fst, fst); // expected-note{{Initialized va_list}}
27+
return; // expected-warning{{Initialized va_list is leaked}} expected-note{{Initialized va_list is leaked}}
28+
}
29+
30+
void f5(va_list fst, ...) {
31+
va_start(fst, fst);
32+
//FIXME: this should cause a warning
33+
} // no-warning
34+
35+
void f6(va_list *fst, ...) {
36+
va_start(*fst, fst); // expected-note{{Initialized va_list}}
37+
(void)va_arg(*fst, int);
38+
//FIXME: this should NOT cause a warning
39+
va_end(*fst); // expected-warning{{Initialized va_list is leaked}} expected-note{{Initialized va_list is leaked}}
40+
}
41+
42+
void f7(int *fst, ...) {
43+
va_list x;
44+
va_list *y = &x;
45+
va_start(*y,fst); // expected-note{{Initialized va_list}}
46+
} // expected-warning{{Initialized va_list 'x' is leaked}} expected-note{{Initialized va_list 'x' is leaked}}
47+
48+
void f8(int *fst, ...) {
49+
va_list x;
50+
va_list *y = &x;
51+
va_start(*y,fst);
52+
va_end(x);
53+
} // no-warning
54+
55+
void reinit(int *fst, ...) {
56+
va_list va;
57+
va_start(va, fst); // expected-note{{Initialized va_list}} expected-note{{Initialized va_list}}
58+
va_start(va, fst); // expected-warning{{Initialized va_list 'va' is initialized again}} expected-note{{Initialized va_list 'va' is initialized again}}
59+
} // expected-warning{{Initialized va_list 'va' is leaked}} expected-note{{Initialized va_list 'va' is leaked}}
60+
61+
void reinitOk(int *fst, ...) {
62+
va_list va;
63+
va_start(va, fst);
64+
va_end(va);
65+
va_start(va, fst);
66+
va_end(va);
67+
} // no-warning
68+
69+
void copyself(int fst, ...) {
70+
va_list va;
71+
va_start(va, fst); // expected-note{{Initialized va_list}}
72+
va_copy(va, va); // expected-warning{{va_list 'va' is copied onto itself}} expected-note{{va_list 'va' is copied onto itself}}
73+
va_end(va);
74+
} // no-warning
75+
76+
void copyselfUninit(int fst, ...) {
77+
va_list va;
78+
va_copy(va, va); // expected-warning{{va_list 'va' is copied onto itself}} expected-note{{va_list 'va' is copied onto itself}}
79+
} // no-warning
80+
81+
void copyOverwrite(int fst, ...) {
82+
va_list va, va2;
83+
va_start(va, fst); // expected-note{{Initialized va_list}}
84+
va_copy(va, va2); // expected-warning{{Initialized va_list 'va' is overwritten by an uninitialized one}} expected-note{{Initialized va_list 'va' is overwritten by an uninitialized one}}
85+
} // no-warning
86+
87+
//This only generates a warning for the valist.Uninitialized checker
88+
void copyUnint(int fst, ...) {
89+
va_list va, va2;
90+
va_copy(va, va2);
91+
} // no-warning
92+
93+
void recopy(int fst, ...) {
94+
va_list va, va2;
95+
va_start(va, fst);
96+
va_copy(va2, va); // expected-note{{Initialized va_list}}
97+
va_copy(va2, va); // expected-warning{{Initialized va_list 'va2' is initialized again}} expected-note{{Initialized va_list 'va2' is initialized again}}
98+
va_end(va);
99+
va_end(va2);
100+
} //no-warning
101+
102+
void doublemsg(int fst, ...) {
103+
va_list va, va2;
104+
va_start(va, fst), va_start(va2, fst); // expected-warning{{Initialized va_list 'va' is leaked}} expected-warning{{Initialized va_list 'va2' is leaked}} expected-note{{Initialized va_list}} expected-note{{Initialized va_list}} expected-note{{Initialized va_list}} expected-note{{Initialized va_list 'va' is leaked}}
105+
}
106+
107+
void in_array(int fst, ...) {
108+
va_list va_array[8];
109+
va_start(va_array[3], fst); // expected-note{{Initialized va_list}}
110+
} // expected-warning{{Initialized va_list 'va_array[3]' is leaked}} expected-note{{Initialized va_list 'va_array[3]' is leaked}}
111+
112+
struct containing_a_valist {
113+
va_list vafield;
114+
int foobar;
115+
};
116+
117+
void in_struct(int fst, ...) {
118+
struct containing_a_valist s;
119+
va_start(s.vafield, fst); // expected-note{{Initialized va_list}}
120+
} // expected-warning{{Initialized va_list 's.vafield' is leaked}} expected-note{{Initialized va_list 's.vafield' is leaked}}
121+
122+
void casting(int fst, ...) {
123+
char mem[sizeof(va_list)];
124+
va_start(*(va_list *) mem, fst); // expected-note{{Initialized va_list}}
125+
} // expected-warning{{Initialized va_list 'mem[0]' is leaked}} expected-note{{Initialized va_list 'mem[0]' is leaked}}
126+
127+
128+
void castingOk(int fst, ...) {
129+
char mem[sizeof(va_list)];
130+
va_start(*(va_list *) mem, fst);
131+
va_end(*(va_list *) mem);
132+
} // no-warning
133+

0 commit comments

Comments
 (0)
Please sign in to comment.