xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
1  //== InvalidPtrChecker.cpp ------------------------------------- -*- 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 file defines InvalidPtrChecker which finds usages of possibly
10  // invalidated pointer.
11  // CERT SEI Rules ENV31-C and ENV34-C
12  // For more information see:
13  // https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
14  // https://wiki.sei.cmu.edu/confluence/x/5NUxBQ
15  //===----------------------------------------------------------------------===//
16  
17  #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18  #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19  #include "clang/StaticAnalyzer/Core/Checker.h"
20  #include "clang/StaticAnalyzer/Core/CheckerManager.h"
21  #include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
22  #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23  #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24  
25  using namespace clang;
26  using namespace ento;
27  
28  namespace {
29  
30  class InvalidPtrChecker
31      : public Checker<check::Location, check::BeginFunction, check::PostCall> {
32  private:
33    // For accurate emission of NoteTags, the BugType of this checker should have
34    // a unique address.
35    BugType InvalidPtrBugType{this, "Use of invalidated pointer",
36                              categories::MemoryError};
37  
38    void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
39  
40    using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
41                                                  CheckerContext &C) const;
42  
43    // SEI CERT ENV31-C
44  
45    // If set to true, consider getenv calls as invalidating operations on the
46    // environment variable buffer. This is implied in the standard, but in
47    // practice does not cause problems (in the commonly used environments).
48    bool InvalidatingGetEnv = false;
49  
50    // GetEnv can be treated invalidating and non-invalidating as well.
51    const CallDescription GetEnvCall{CDM::CLibrary, {"getenv"}, 1};
52  
53    const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
54        {{CDM::CLibrary, {"setenv"}, 3},
55         &InvalidPtrChecker::EnvpInvalidatingCall},
56        {{CDM::CLibrary, {"unsetenv"}, 1},
57         &InvalidPtrChecker::EnvpInvalidatingCall},
58        {{CDM::CLibrary, {"putenv"}, 1},
59         &InvalidPtrChecker::EnvpInvalidatingCall},
60        {{CDM::CLibrary, {"_putenv_s"}, 2},
61         &InvalidPtrChecker::EnvpInvalidatingCall},
62        {{CDM::CLibrary, {"_wputenv_s"}, 2},
63         &InvalidPtrChecker::EnvpInvalidatingCall},
64    };
65  
66    void postPreviousReturnInvalidatingCall(const CallEvent &Call,
67                                            CheckerContext &C) const;
68  
69    // SEI CERT ENV34-C
70    const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
71        {{CDM::CLibrary, {"setlocale"}, 2},
72         &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
73        {{CDM::CLibrary, {"strerror"}, 1},
74         &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
75        {{CDM::CLibrary, {"localeconv"}, 0},
76         &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
77        {{CDM::CLibrary, {"asctime"}, 1},
78         &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
79    };
80  
81    // The private members of this checker corresponding to commandline options
82    // are set in this function.
83    friend void ento::registerInvalidPtrChecker(CheckerManager &);
84  
85  public:
86    // Obtain the environment pointer from 'main()' (if present).
87    void checkBeginFunction(CheckerContext &C) const;
88  
89    // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
90    // pointer from 'main()'
91    // Handle functions in PreviousCallInvalidatingFunctions.
92    // Also, check if invalidated region is passed to a
93    // conservatively evaluated function call as an argument.
94    void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
95  
96    // Check if invalidated region is being dereferenced.
97    void checkLocation(SVal l, bool isLoad, const Stmt *S,
98                       CheckerContext &C) const;
99  
100  private:
101    const NoteTag *createEnvInvalidationNote(CheckerContext &C,
102                                             ProgramStateRef State,
103                                             StringRef FunctionName) const;
104  };
105  
106  } // namespace
107  
108  // Set of memory regions that were invalidated
REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions,const MemRegion *)109  REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
110  
111  // Stores the region of the environment pointer of 'main' (if present).
112  REGISTER_TRAIT_WITH_PROGRAMSTATE(MainEnvPtrRegion, const MemRegion *)
113  
114  // Stores the regions of environments returned by getenv calls.
115  REGISTER_SET_WITH_PROGRAMSTATE(GetenvEnvPtrRegions, const MemRegion *)
116  
117  // Stores key-value pairs, where key is function declaration and value is
118  // pointer to memory region returned by previous call of this function
119  REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
120                                 const MemRegion *)
121  
122  const NoteTag *InvalidPtrChecker::createEnvInvalidationNote(
123      CheckerContext &C, ProgramStateRef State, StringRef FunctionName) const {
124  
125    const MemRegion *MainRegion = State->get<MainEnvPtrRegion>();
126    const auto GetenvRegions = State->get<GetenvEnvPtrRegions>();
127  
128    return C.getNoteTag([this, MainRegion, GetenvRegions,
129                         FunctionName = std::string{FunctionName}](
130                            PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
131      // Only handle the BugType of this checker.
132      if (&BR.getBugType() != &InvalidPtrBugType)
133        return;
134  
135      // Mark all regions that were interesting before as NOT interesting now
136      // to avoid extra notes coming from invalidation points higher up the
137      // bugpath. This ensures that only the last invalidation point is marked
138      // with a note tag.
139      llvm::SmallVector<std::string, 2> InvalidLocationNames;
140      if (BR.isInteresting(MainRegion)) {
141        BR.markNotInteresting(MainRegion);
142        InvalidLocationNames.push_back("the environment parameter of 'main'");
143      }
144      bool InterestingGetenvFound = false;
145      for (const MemRegion *MR : GetenvRegions) {
146        if (BR.isInteresting(MR)) {
147          BR.markNotInteresting(MR);
148          if (!InterestingGetenvFound) {
149            InterestingGetenvFound = true;
150            InvalidLocationNames.push_back(
151                "the environment returned by 'getenv'");
152          }
153        }
154      }
155  
156      // Emit note tag message.
157      if (InvalidLocationNames.size() >= 1)
158        Out << '\'' << FunctionName << "' call may invalidate "
159            << InvalidLocationNames[0];
160      if (InvalidLocationNames.size() == 2)
161        Out << ", and " << InvalidLocationNames[1];
162    });
163  }
164  
EnvpInvalidatingCall(const CallEvent & Call,CheckerContext & C) const165  void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
166                                               CheckerContext &C) const {
167    // This callevent invalidates all previously generated pointers to the
168    // environment.
169    ProgramStateRef State = C.getState();
170    if (const MemRegion *MainEnvPtr = State->get<MainEnvPtrRegion>())
171      State = State->add<InvalidMemoryRegions>(MainEnvPtr);
172    for (const MemRegion *EnvPtr : State->get<GetenvEnvPtrRegions>())
173      State = State->add<InvalidMemoryRegions>(EnvPtr);
174  
175    StringRef FunctionName = Call.getCalleeIdentifier()->getName();
176    const NoteTag *InvalidationNote =
177        createEnvInvalidationNote(C, State, FunctionName);
178  
179    C.addTransition(State, InvalidationNote);
180  }
181  
postPreviousReturnInvalidatingCall(const CallEvent & Call,CheckerContext & C) const182  void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
183      const CallEvent &Call, CheckerContext &C) const {
184    ProgramStateRef State = C.getState();
185  
186    const NoteTag *Note = nullptr;
187    const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
188    // Invalidate the region of the previously returned pointer - if there was
189    // one.
190    if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
191      const MemRegion *PrevReg = *Reg;
192      State = State->add<InvalidMemoryRegions>(PrevReg);
193      Note = C.getNoteTag([this, PrevReg, FD](PathSensitiveBugReport &BR,
194                                              llvm::raw_ostream &Out) {
195        if (!BR.isInteresting(PrevReg) || &BR.getBugType() != &InvalidPtrBugType)
196          return;
197        Out << '\'';
198        FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
199        Out << "' call may invalidate the result of the previous " << '\'';
200        FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
201        Out << '\'';
202      });
203    }
204  
205    const LocationContext *LCtx = C.getLocationContext();
206    const auto *CE = cast<CallExpr>(Call.getOriginExpr());
207  
208    // Function call will return a pointer to the new symbolic region.
209    DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
210        CE, LCtx, CE->getType(), C.blockCount());
211    State = State->BindExpr(CE, LCtx, RetVal);
212  
213    const auto *SymRegOfRetVal =
214        dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion());
215    if (!SymRegOfRetVal)
216      return;
217  
218    // Remember to this region.
219    const MemRegion *MR = SymRegOfRetVal->getBaseRegion();
220    State = State->set<PreviousCallResultMap>(FD, MR);
221  
222    ExplodedNode *Node = C.addTransition(State, Note);
223    const NoteTag *PreviousCallNote = C.getNoteTag(
224        [this, MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
225          if (!BR.isInteresting(MR) || &BR.getBugType() != &InvalidPtrBugType)
226            return;
227          Out << "previous function call was here";
228        });
229  
230    C.addTransition(State, Node, PreviousCallNote);
231  }
232  
233  // TODO: This seems really ugly. Simplify this.
findInvalidatedSymbolicBase(ProgramStateRef State,const MemRegion * Reg)234  static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
235                                                      const MemRegion *Reg) {
236    while (Reg) {
237      if (State->contains<InvalidMemoryRegions>(Reg))
238        return Reg;
239      const auto *SymBase = Reg->getSymbolicBase();
240      if (!SymBase)
241        break;
242      const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
243      if (!SRV)
244        break;
245      Reg = SRV->getRegion();
246      if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
247        Reg = VarReg;
248    }
249    return nullptr;
250  }
251  
252  // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
253  // pointer from 'main()' Also, check if invalidated region is passed to a
254  // function call as an argument.
checkPostCall(const CallEvent & Call,CheckerContext & C) const255  void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
256                                        CheckerContext &C) const {
257  
258    ProgramStateRef State = C.getState();
259  
260    // Model 'getenv' calls
261    if (GetEnvCall.matches(Call)) {
262      const MemRegion *Region = Call.getReturnValue().getAsRegion();
263      if (Region) {
264        State = State->add<GetenvEnvPtrRegions>(Region);
265        C.addTransition(State);
266      }
267    }
268  
269    // Check if function invalidates 'envp' argument of 'main'
270    if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
271      (this->**Handler)(Call, C);
272  
273    // Check if function invalidates the result of previous call
274    if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
275      (this->**Handler)(Call, C);
276  
277    // If pedantic mode is on, regard 'getenv' calls invalidating as well
278    if (InvalidatingGetEnv && GetEnvCall.matches(Call))
279      postPreviousReturnInvalidatingCall(Call, C);
280  
281    // Check if one of the arguments of the function call is invalidated
282  
283    // If call was inlined, don't report invalidated argument
284    if (C.wasInlined)
285      return;
286  
287    for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
288  
289      if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
290              Call.getArgSVal(I).getAsRegion())) {
291        if (const MemRegion *InvalidatedSymbolicBase =
292                findInvalidatedSymbolicBase(State, SR)) {
293          ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
294          if (!ErrorNode)
295            return;
296  
297          SmallString<256> Msg;
298          llvm::raw_svector_ostream Out(Msg);
299          Out << "use of invalidated pointer '";
300          Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
301                                          C.getASTContext().getPrintingPolicy());
302          Out << "' in a function call";
303  
304          auto Report = std::make_unique<PathSensitiveBugReport>(
305              InvalidPtrBugType, Out.str(), ErrorNode);
306          Report->markInteresting(InvalidatedSymbolicBase);
307          Report->addRange(Call.getArgSourceRange(I));
308          C.emitReport(std::move(Report));
309        }
310      }
311    }
312  }
313  
314  // Obtain the environment pointer from 'main()', if present.
checkBeginFunction(CheckerContext & C) const315  void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
316    if (!C.inTopFrame())
317      return;
318  
319    const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
320    if (!FD || FD->param_size() != 3 || !FD->isMain())
321      return;
322  
323    ProgramStateRef State = C.getState();
324    const MemRegion *EnvpReg =
325        State->getRegion(FD->parameters()[2], C.getLocationContext());
326  
327    // Save the memory region pointed by the environment pointer parameter of
328    // 'main'.
329    C.addTransition(State->set<MainEnvPtrRegion>(EnvpReg));
330  }
331  
332  // Check if invalidated region is being dereferenced.
checkLocation(SVal Loc,bool isLoad,const Stmt * S,CheckerContext & C) const333  void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
334                                        CheckerContext &C) const {
335    ProgramStateRef State = C.getState();
336  
337    // Ignore memory operations involving 'non-invalidated' locations.
338    const MemRegion *InvalidatedSymbolicBase =
339        findInvalidatedSymbolicBase(State, Loc.getAsRegion());
340    if (!InvalidatedSymbolicBase)
341      return;
342  
343    ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
344    if (!ErrorNode)
345      return;
346  
347    auto Report = std::make_unique<PathSensitiveBugReport>(
348        InvalidPtrBugType, "dereferencing an invalid pointer", ErrorNode);
349    Report->markInteresting(InvalidatedSymbolicBase);
350    C.emitReport(std::move(Report));
351  }
352  
registerInvalidPtrChecker(CheckerManager & Mgr)353  void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
354    auto *Checker = Mgr.registerChecker<InvalidPtrChecker>();
355    Checker->InvalidatingGetEnv =
356        Mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker,
357                                                         "InvalidatingGetEnv");
358  }
359  
shouldRegisterInvalidPtrChecker(const CheckerManager &)360  bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
361    return true;
362  }
363