Clang Project

clang_source_code/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
1//== ValistChecker.cpp - stdarg.h macro usage checker -----------*- C++ -*--==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This defines checkers which detect usage of uninitialized va_list values
10// and va_start calls with no matching va_end.
11//
12//===----------------------------------------------------------------------===//
13
14#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
16#include "clang/StaticAnalyzer/Core/Checker.h"
17#include "clang/StaticAnalyzer/Core/CheckerManager.h"
18#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
19#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20
21using namespace clang;
22using namespace ento;
23
24REGISTER_SET_WITH_PROGRAMSTATE(InitializedVALists, const MemRegion *)
25
26namespace {
27typedef SmallVector<const MemRegion *, 2RegionVector;
28
29class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
30                                     check::DeadSymbols> {
31  mutable std::unique_ptr<BugTypeBT_leakedvalistBT_uninitaccess;
32
33  struct VAListAccepter {
34    CallDescription Func;
35    int VAListPos;
36  };
37  static const SmallVector<VAListAccepter15VAListAccepters;
38  static const CallDescription VaStartVaEndVaCopy;
39
40public:
41  enum CheckKind {
42    CK_Uninitialized,
43    CK_Unterminated,
44    CK_CopyToSelf,
45    CK_NumCheckKinds
46  };
47
48  DefaultBool ChecksEnabled[CK_NumCheckKinds];
49  CheckName CheckNames[CK_NumCheckKinds];
50
51  void checkPreStmt(const VAArgExpr *VAACheckerContext &Cconst;
52  void checkPreCall(const CallEvent &CallCheckerContext &Cconst;
53  void checkDeadSymbols(SymbolReaper &SRCheckerContext &Cconst;
54
55private:
56  const MemRegion *getVAListAsRegion(SVal SVconst Expr *VAExpr,
57                                     bool &IsSymbolicCheckerContext &Cconst;
58  const ExplodedNode *getStartCallSite(const ExplodedNode *N,
59                                       const MemRegion *Regconst;
60
61  void reportUninitializedAccess(const MemRegion *VAListStringRef Msg,
62                                 CheckerContext &Cconst;
63  void reportLeakedVALists(const RegionVector &LeakedVAListsStringRef Msg1,
64                           StringRef Msg2CheckerContext &CExplodedNode *N,
65                           bool ReportUninit = falseconst;
66
67  void checkVAListStartCall(const CallEvent &CallCheckerContext &C,
68                            bool IsCopyconst;
69  void checkVAListEndCall(const CallEvent &CallCheckerContext &Cconst;
70
71  class ValistBugVisitor : public BugReporterVisitor {
72  public:
73    ValistBugVisitor(const MemRegion *Regbool IsLeak = false)
74        : Reg(Reg), IsLeak(IsLeak) {}
75    void Profile(llvm::FoldingSetNodeID &IDconst override {
76      static int X = 0;
77      ID.AddPointer(&X);
78      ID.AddPointer(Reg);
79    }
80    std::shared_ptr<PathDiagnosticPiece>
81    getEndPath(BugReporterContext &BRCconst ExplodedNode *EndPathNode,
82               BugReport &BR) override {
83      if (!IsLeak)
84        return nullptr;
85
86      PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(
87          EndPathNodeBRC.getSourceManager());
88      // Do not add the statement itself as a range in case of leak.
89      return std::make_shared<PathDiagnosticEventPiece>(LBR.getDescription(), false);
90    }
91    std::shared_ptr<PathDiagnosticPieceVisitNode(const ExplodedNode *N,
92                                                   BugReporterContext &BRC,
93                                                   BugReport &BR) override;
94
95  private:
96    const MemRegion *Reg;
97    bool IsLeak;
98  };
99};
100
101const SmallVector<ValistChecker::VAListAccepter15>
102    ValistChecker::VAListAccepters = {
103        {{"vfprintf"3}, 2},
104        {{"vfscanf"3}, 2},
105        {{"vprintf"2}, 1},
106        {{"vscanf"2}, 1},
107        {{"vsnprintf"4}, 3},
108        {{"vsprintf"3}, 2},
109        {{"vsscanf"3}, 2},
110        {{"vfwprintf"3}, 2},
111        {{"vfwscanf"3}, 2},
112        {{"vwprintf"2}, 1},
113        {{"vwscanf"2}, 1},
114        {{"vswprintf"4}, 3},
115        // vswprintf is the wide version of vsnprintf,
116        // vsprintf has no wide version
117        {{"vswscanf"3}, 2}};
118const CallDescription ValistChecker::VaStart("__builtin_va_start"2),
119    ValistChecker::VaCopy("__builtin_va_copy"2),
120    ValistChecker::VaEnd("__builtin_va_end"1);
121// end anonymous namespace
122
123void ValistChecker::checkPreCall(const CallEvent &Call,
124                                 CheckerContext &Cconst {
125  if (!Call.isGlobalCFunction())
126    return;
127  if (Call.isCalled(VaStart))
128    checkVAListStartCall(CallCfalse);
129  else if (Call.isCalled(VaCopy))
130    checkVAListStartCall(CallCtrue);
131  else if (Call.isCalled(VaEnd))
132    checkVAListEndCall(CallC);
133  else {
134    for (auto FuncInfo : VAListAccepters) {
135      if (!Call.isCalled(FuncInfo.Func))
136        continue;
137      bool Symbolic;
138      const MemRegion *VAList =
139          getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos),
140                            Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C);
141      if (!VAList)
142        return;
143
144      if (C.getState()->contains<InitializedVALists>(VAList))
145        return;
146
147      // We did not see va_start call, but the source of the region is unknown.
148      // Be conservative and assume the best.
149      if (Symbolic)
150        return;
151
152      SmallString<80> Errmsg("Function '");
153      Errmsg += FuncInfo.Func.getFunctionName();
154      Errmsg += "' is called with an uninitialized va_list argument";
155      reportUninitializedAccess(VAList, Errmsg.c_str(), C);
156      break;
157    }
158  }
159}
160
161const MemRegion *ValistChecker::getVAListAsRegion(SVal SVconst Expr *E,
162                                                  bool &IsSymbolic,
163                                                  CheckerContext &Cconst {
164  const MemRegion *Reg = SV.getAsRegion();
165  if (!Reg)
166    return nullptr;
167  // TODO: In the future this should be abstracted away by the analyzer.
168  bool VaListModelledAsArray = false;
169  if (const auto *Cast = dyn_cast<CastExpr>(E)) {
170    QualType Ty = Cast->getType();
171    VaListModelledAsArray =
172        Ty->isPointerType() && Ty->getPointeeType()->isRecordType();
173  }
174  if (const auto *DeclReg = Reg->getAs<DeclRegion>()) {
175    if (isa<ParmVarDecl>(DeclReg->getDecl()))
176      Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion();
177  }
178  IsSymbolic = Reg && Reg->getAs<SymbolicRegion>();
179  // Some VarRegion based VA lists reach here as ElementRegions.
180  const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg);
181  return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg;
182}
183
184void ValistChecker::checkPreStmt(const VAArgExpr *VAA,
185                                 CheckerContext &Cconst {
186  ProgramStateRef State = C.getState();
187  const Expr *VASubExpr = VAA->getSubExpr();
188  SVal VAListSVal = C.getSVal(VASubExpr);
189  bool Symbolic;
190  const MemRegion *VAList =
191      getVAListAsRegion(VAListSValVASubExprSymbolicC);
192  if (!VAList)
193    return;
194  if (Symbolic)
195    return;
196  if (!State->contains<InitializedVALists>(VAList))
197    reportUninitializedAccess(
198        VAList"va_arg() is called on an uninitialized va_list"C);
199}
200
201void ValistChecker::checkDeadSymbols(SymbolReaper &SR,
202                                     CheckerContext &Cconst {
203  ProgramStateRef State = C.getState();
204  InitializedVAListsTy TrackedVALists = State->get<InitializedVALists>();
205  RegionVector LeakedVALists;
206  for (auto Reg : TrackedVALists) {
207    if (SR.isLiveRegion(Reg))
208      continue;
209    LeakedVALists.push_back(Reg);
210    State = State->remove<InitializedVALists>(Reg);
211  }
212  if (ExplodedNode *N = C.addTransition(State))
213    reportLeakedVALists(LeakedVALists, "Initialized va_list"" is leaked", C,
214                        N);
215}
216
217// This function traverses the exploded graph backwards and finds the node where
218// the va_list is initialized. That node is used for uniquing the bug paths.
219// It is not likely that there are several different va_lists that belongs to
220// different stack frames, so that case is not yet handled.
221const ExplodedNode *
222ValistChecker::getStartCallSite(const ExplodedNode *N,
223                                const MemRegion *Regconst {
224  const LocationContext *LeakContext = N->getLocationContext();
225  const ExplodedNode *StartCallNode = N;
226
227  bool FoundInitializedState = false;
228
229  while (N) {
230    ProgramStateRef State = N->getState();
231    if (!State->contains<InitializedVALists>(Reg)) {
232      if (FoundInitializedState)
233        break;
234    } else {
235      FoundInitializedState = true;
236    }
237    const LocationContext *NContext = N->getLocationContext();
238    if (NContext == LeakContext || NContext->isParentOf(LeakContext))
239      StartCallNode = N;
240    N = N->pred_empty() ? nullptr : *(N->pred_begin());
241  }
242
243  return StartCallNode;
244}
245
246void ValistChecker::reportUninitializedAccess(const MemRegion *VAList,
247                                              StringRef Msg,
248                                              CheckerContext &Cconst {
249  if (!ChecksEnabled[CK_Uninitialized])
250    return;
251  if (ExplodedNode *N = C.generateErrorNode()) {
252    if (!BT_uninitaccess)
253      BT_uninitaccess.reset(new BugType(CheckNames[CK_Uninitialized],
254                                        "Uninitialized va_list",
255                                        categories::MemoryError));
256    auto R = llvm::make_unique<BugReport>(*BT_uninitaccess, Msg, N);
257    R->markInteresting(VAList);
258    R->addVisitor(llvm::make_unique<ValistBugVisitor>(VAList));
259    C.emitReport(std::move(R));
260  }
261}
262
263void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
264                                        StringRef Msg1StringRef Msg2,
265                                        CheckerContext &CExplodedNode *N,
266                                        bool ReportUninitconst {
267  if (!(ChecksEnabled[CK_Unterminated] ||
268        (ChecksEnabled[CK_Uninitialized] && ReportUninit)))
269    return;
270  for (auto Reg : LeakedVALists) {
271    if (!BT_leakedvalist) {
272      // FIXME: maybe creating a new check name for this type of bug is a better
273      // solution.
274      BT_leakedvalist.reset(
275          new BugType(CheckNames[CK_Unterminated].getName().empty()
276                          ? CheckNames[CK_Uninitialized]
277                          : CheckNames[CK_Unterminated],
278                      "Leaked va_list", categories::MemoryError,
279                      /*SuppressOnSink=*/true));
280    }
281
282    const ExplodedNode *StartNode = getStartCallSite(N, Reg);
283    PathDiagnosticLocation LocUsedForUniqueing;
284
285    if (const Stmt *StartCallStmt = PathDiagnosticLocation::getStmt(StartNode))
286      LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
287          StartCallStmt, C.getSourceManager(), StartNode->getLocationContext());
288
289    SmallString<100> Buf;
290    llvm::raw_svector_ostream OS(Buf);
291    OS << Msg1;
292    std::string VariableName = Reg->getDescriptiveName();
293    if (!VariableName.empty())
294      OS << " " << VariableName;
295    OS << Msg2;
296
297    auto R = llvm::make_unique<BugReport>(
298        *BT_leakedvalist, OS.str(), N, LocUsedForUniqueing,
299        StartNode->getLocationContext()->getDecl());
300    R->markInteresting(Reg);
301    R->addVisitor(llvm::make_unique<ValistBugVisitor>(Reg, true));
302    C.emitReport(std::move(R));
303  }
304}
305
306void ValistChecker::checkVAListStartCall(const CallEvent &Call,
307                                         CheckerContext &Cbool IsCopyconst {
308  bool Symbolic;
309  const MemRegion *VAList =
310      getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), SymbolicC);
311  if (!VAList)
312    return;
313
314  ProgramStateRef State = C.getState();
315
316  if (IsCopy) {
317    const MemRegion *Arg2 =
318        getVAListAsRegion(Call.getArgSVal(1), Call.getArgExpr(1), SymbolicC);
319    if (Arg2) {
320      if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) {
321        RegionVector LeakedVALists{VAList};
322        if (ExplodedNode *N = C.addTransition(State))
323          reportLeakedVALists(LeakedVALists, "va_list",
324                              " is copied onto itself", C, N, true);
325        return;
326      } else if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) {
327        if (State->contains<InitializedVALists>(VAList)) {
328          State = State->remove<InitializedVALists>(VAList);
329          RegionVector LeakedVALists{VAList};
330          if (ExplodedNode *N = C.addTransition(State))
331            reportLeakedVALists(LeakedVALists, "Initialized va_list",
332                                " is overwritten by an uninitialized one", C, N,
333                                true);
334        } else {
335          reportUninitializedAccess(Arg2"Uninitialized va_list is copied"C);
336        }
337        return;
338      }
339    }
340  }
341  if (State->contains<InitializedVALists>(VAList)) {
342    RegionVector LeakedVALists{VAList};
343    if (ExplodedNode *N = C.addTransition(State))
344      reportLeakedVALists(LeakedVALists, "Initialized va_list",
345                          " is initialized again", C, N);
346    return;
347  }
348
349  State = State->add<InitializedVALists>(VAList);
350  C.addTransition(State);
351}
352
353void ValistChecker::checkVAListEndCall(const CallEvent &Call,
354                                       CheckerContext &Cconst {
355  bool Symbolic;
356  const MemRegion *VAList =
357      getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), SymbolicC);
358  if (!VAList)
359    return;
360
361  // We did not see va_start call, but the source of the region is unknown.
362  // Be conservative and assume the best.
363  if (Symbolic)
364    return;
365
366  if (!C.getState()->contains<InitializedVALists>(VAList)) {
367    reportUninitializedAccess(
368        VAList"va_end() is called on an uninitialized va_list"C);
369    return;
370  }
371  ProgramStateRef State = C.getState();
372  State = State->remove<InitializedVALists>(VAList);
373  C.addTransition(State);
374}
375
376std::shared_ptr<PathDiagnosticPieceValistChecker::ValistBugVisitor::VisitNode(
377    const ExplodedNode *NBugReporterContext &BRC,
378    BugReport &) {
379  ProgramStateRef State = N->getState();
380  ProgramStateRef StatePrev = N->getFirstPred()->getState();
381
382  const Stmt *S = PathDiagnosticLocation::getStmt(N);
383  if (!S)
384    return nullptr;
385
386  StringRef Msg;
387  if (State->contains<InitializedVALists>(Reg) &&
388      !StatePrev->contains<InitializedVALists>(Reg))
389    Msg = "Initialized va_list";
390  else if (!State->contains<InitializedVALists>(Reg) &&
391           StatePrev->contains<InitializedVALists>(Reg))
392    Msg = "Ended va_list";
393
394  if (Msg.empty())
395    return nullptr;
396
397  PathDiagnosticLocation Pos(SBRC.getSourceManager(),
398                             N->getLocationContext());
399  return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true);
400}
401
402void ento::registerValistBase(CheckerManager &mgr) {
403  mgr.registerChecker<ValistChecker>();
404}
405
406bool ento::shouldRegisterValistBase(const LangOptions &LO) {
407  return true;
408}
409
410#define REGISTER_CHECKER(name)                                                 \
411  void ento::register##name##Checker(CheckerManager &mgr) {                    \
412    ValistChecker *checker = mgr.getChecker<ValistChecker>();                  \
413    checker->ChecksEnabled[ValistChecker::CK_##name] = true;                   \
414    checker->CheckNames[ValistChecker::CK_##name] = mgr.getCurrentCheckName(); \
415  }                                                                            \
416                                                                               \
417  bool ento::shouldRegister##name##Checker(const LangOptions &LO) {            \
418    return true;                                                               \
419  }
420
421REGISTER_CHECKER(Uninitialized)
422REGISTER_CHECKER(Unterminated)
423REGISTER_CHECKER(CopyToSelf)
424