clang  19.0.0git
InvalidPtrChecker.cpp
Go to the documentation of this file.
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 
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",
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,
103  StringRef FunctionName) const;
104 };
105 
106 } // namespace
107 
108 // Set of memory regions that were invalidated
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 
165 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 
182 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.
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.
255 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.
315 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.
333 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 
353 void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
354  auto *Checker = Mgr.registerChecker<InvalidPtrChecker>();
355  Checker->InvalidatingGetEnv =
357  "InvalidatingGetEnv");
358 }
359 
360 bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
361  return true;
362 }
DynTypedNode Node
REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *, const MemRegion *) const NoteTag *InvalidPtrChecker
#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set of type NameTy, suitable for placement into the ProgramState.
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy.
LineState State
const LangOptions & getLangOpts() const
Definition: ASTContext.h:778
bool getCheckerBooleanOption(StringRef CheckerName, StringRef OptionName, bool SearchInParents=false) const
Interprets an option's string value as a boolean.
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:501
Represents a function declaration or definition.
Definition: Decl.h:1972
void getNameForDiagnostic(raw_ostream &OS, const PrintingPolicy &Policy, bool Qualified) const override
Appends a human-readable name for this declaration into the given stream.
Definition: Decl.cpp:3085
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
Stmt - This represents one statement.
Definition: Stmt.h:84
const BugType & getBugType() const
Definition: BugReporter.h:149
An immutable map from CallDescriptions to arbitrary data.
A CallDescription is a pattern that can be used to match calls based on the qualified name and the ar...
@ CLibrary
Match calls to functions from the C standard library.
Represents an abstract call to a function or method along a particular path.
Definition: CallEvent.h:153
const AnalyzerOptions & getAnalyzerOptions() const
CHECKER * registerChecker(AT &&... Args)
Used to register checkers.
MemRegion - The root abstract class for all memory regions.
Definition: MemRegion.h:96
const SymbolicRegion * getSymbolicBase() const
If this is a symbolic region, returns the region.
Definition: MemRegion.cpp:1401
LLVM_ATTRIBUTE_RETURNS_NONNULL const MemRegion * getBaseRegion() const
Definition: MemRegion.cpp:1343
The tag upon which the TagVisitor reacts.
Definition: BugReporter.h:779
bool isInteresting(SymbolRef sym) const
SVal - This represents a symbolic expression, which can be either an L-value or an R-value.
Definition: SVals.h:55
const MemRegion * getAsRegion() const
Definition: SVals.cpp:120
bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)
Definition: Interp.h:2179
The JSON file list parser is used to communicate input to InstallAPI.