clang  16.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  BugType BT{this, "Use of invalidated pointer", categories::MemoryError};
34 
35  void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
36 
37  using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
38  CheckerContext &C) const;
39 
40  // SEI CERT ENV31-C
41  const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
42  {{"setenv", 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
43  {{"unsetenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
44  {{"putenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
45  {{"_putenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
46  {{"_wputenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
47  };
48 
49  void postPreviousReturnInvalidatingCall(const CallEvent &Call,
50  CheckerContext &C) const;
51 
52  // SEI CERT ENV34-C
53  const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
54  {{"getenv", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
55  {{"setlocale", 2},
56  &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
57  {{"strerror", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
58  {{"localeconv", 0},
59  &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
60  {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
61  };
62 
63 public:
64  // Obtain the environment pointer from 'main()' (if present).
65  void checkBeginFunction(CheckerContext &C) const;
66 
67  // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
68  // pointer from 'main()'
69  // Handle functions in PreviousCallInvalidatingFunctions.
70  // Also, check if invalidated region is passed to a
71  // conservatively evaluated function call as an argument.
72  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
73 
74  // Check if invalidated region is being dereferenced.
75  void checkLocation(SVal l, bool isLoad, const Stmt *S,
76  CheckerContext &C) const;
77 };
78 
79 } // namespace
80 
81 // Set of memory regions that were invalidated
82 REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
83 
84 // Stores the region of the environment pointer of 'main' (if present).
85 REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const MemRegion *)
86 
87 // Stores key-value pairs, where key is function declaration and value is
88 // pointer to memory region returned by previous call of this function
89 REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
90  const MemRegion *)
91 
92 void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
93  CheckerContext &C) const {
94  StringRef FunctionName = Call.getCalleeIdentifier()->getName();
95  ProgramStateRef State = C.getState();
96  const MemRegion *SymbolicEnvPtrRegion = State->get<EnvPtrRegion>();
97  if (!SymbolicEnvPtrRegion)
98  return;
99 
100  State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion);
101 
102  const NoteTag *Note =
103  C.getNoteTag([SymbolicEnvPtrRegion, FunctionName](
104  PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
105  if (!BR.isInteresting(SymbolicEnvPtrRegion))
106  return;
107  Out << '\'' << FunctionName
108  << "' call may invalidate the environment parameter of 'main'";
109  });
110 
111  C.addTransition(State, Note);
112 }
113 
114 void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
115  const CallEvent &Call, CheckerContext &C) const {
116  ProgramStateRef State = C.getState();
117 
118  const NoteTag *Note = nullptr;
119  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
120  // Invalidate the region of the previously returned pointer - if there was
121  // one.
122  if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
123  const MemRegion *PrevReg = *Reg;
124  State = State->add<InvalidMemoryRegions>(PrevReg);
125  Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR,
126  llvm::raw_ostream &Out) {
127  if (!BR.isInteresting(PrevReg))
128  return;
129  Out << '\'';
130  FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
131  Out << "' call may invalidate the result of the previous " << '\'';
132  FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
133  Out << '\'';
134  });
135  }
136 
137  const LocationContext *LCtx = C.getLocationContext();
138  const auto *CE = cast<CallExpr>(Call.getOriginExpr());
139 
140  // Function call will return a pointer to the new symbolic region.
141  DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
142  CE, LCtx, CE->getType(), C.blockCount());
143  State = State->BindExpr(CE, LCtx, RetVal);
144 
145  // Remember to this region.
146  const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
147  const MemRegion *MR =
148  const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
149  State = State->set<PreviousCallResultMap>(FD, MR);
150 
151  ExplodedNode *Node = C.addTransition(State, Note);
152  const NoteTag *PreviousCallNote =
153  C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
154  if (!BR.isInteresting(MR))
155  return;
156  Out << '\'' << "'previous function call was here" << '\'';
157  });
158 
159  C.addTransition(State, Node, PreviousCallNote);
160 }
161 
162 // TODO: This seems really ugly. Simplify this.
163 static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
164  const MemRegion *Reg) {
165  while (Reg) {
166  if (State->contains<InvalidMemoryRegions>(Reg))
167  return Reg;
168  const auto *SymBase = Reg->getSymbolicBase();
169  if (!SymBase)
170  break;
171  const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
172  if (!SRV)
173  break;
174  Reg = SRV->getRegion();
175  if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
176  Reg = VarReg;
177  }
178  return nullptr;
179 }
180 
181 // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
182 // pointer from 'main()' Also, check if invalidated region is passed to a
183 // function call as an argument.
184 void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
185  CheckerContext &C) const {
186  // Check if function invalidates 'envp' argument of 'main'
187  if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
188  (this->**Handler)(Call, C);
189 
190  // Check if function invalidates the result of previous call
191  if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
192  (this->**Handler)(Call, C);
193 
194  // Check if one of the arguments of the function call is invalidated
195 
196  // If call was inlined, don't report invalidated argument
197  if (C.wasInlined)
198  return;
199 
200  ProgramStateRef State = C.getState();
201 
202  for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
203 
204  if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
205  Call.getArgSVal(I).getAsRegion())) {
206  if (const MemRegion *InvalidatedSymbolicBase =
207  findInvalidatedSymbolicBase(State, SR)) {
208  ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
209  if (!ErrorNode)
210  return;
211 
212  SmallString<256> Msg;
213  llvm::raw_svector_ostream Out(Msg);
214  Out << "use of invalidated pointer '";
215  Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
216  C.getASTContext().getPrintingPolicy());
217  Out << "' in a function call";
218 
219  auto Report =
220  std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode);
221  Report->markInteresting(InvalidatedSymbolicBase);
222  Report->addRange(Call.getArgSourceRange(I));
223  C.emitReport(std::move(Report));
224  }
225  }
226  }
227 }
228 
229 // Obtain the environment pointer from 'main()', if present.
230 void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
231  if (!C.inTopFrame())
232  return;
233 
234  const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
235  if (!FD || FD->param_size() != 3 || !FD->isMain())
236  return;
237 
238  ProgramStateRef State = C.getState();
239  const MemRegion *EnvpReg =
240  State->getRegion(FD->parameters()[2], C.getLocationContext());
241 
242  // Save the memory region pointed by the environment pointer parameter of
243  // 'main'.
244  C.addTransition(State->set<EnvPtrRegion>(EnvpReg));
245 }
246 
247 // Check if invalidated region is being dereferenced.
248 void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
249  CheckerContext &C) const {
250  ProgramStateRef State = C.getState();
251 
252  // Ignore memory operations involving 'non-invalidated' locations.
253  const MemRegion *InvalidatedSymbolicBase =
254  findInvalidatedSymbolicBase(State, Loc.getAsRegion());
255  if (!InvalidatedSymbolicBase)
256  return;
257 
258  ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
259  if (!ErrorNode)
260  return;
261 
262  auto Report = std::make_unique<PathSensitiveBugReport>(
263  BT, "dereferencing an invalid pointer", ErrorNode);
264  Report->markInteresting(InvalidatedSymbolicBase);
265  C.emitReport(std::move(Report));
266 }
267 
268 void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
269  Mgr.registerChecker<InvalidPtrChecker>();
270 }
271 
272 bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
273  return true;
274 }
clang::Decl::getASTContext
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:428
REGISTER_MAP_WITH_PROGRAMSTATE
REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *, const MemRegion *) void InvalidPtrChecker
Definition: InvalidPtrChecker.cpp:89
clang::LocationContext
It wraps the AnalysisDeclContext to represent both the call stack with the help of StackFrameContext ...
Definition: AnalysisDeclContext.h:215
CallDescription.h
clang::ento::ProgramStateRef
IntrusiveRefCntPtr< const ProgramState > ProgramStateRef
Definition: ProgramState_Fwd.h:37
AttributeLangSupport::C
@ C
Definition: SemaDeclAttr.cpp:56
clang::serialized_diags::Note
@ Note
Definition: SerializedDiagnostics.h:45
REGISTER_TRAIT_WITH_PROGRAMSTATE
#define REGISTER_TRAIT_WITH_PROGRAMSTATE(Name, Type)
Declares a program state trait for type Type called Name, and introduce a type named NameTy.
Definition: ProgramStateTrait.h:34
CallEvent.h
BuiltinCheckerRegistration.h
Node
DynTypedNode Node
Definition: ASTMatchFinder.cpp:68
clang::FunctionDecl::getNameForDiagnostic
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:2996
CheckerManager.h
llvm::SmallString
Definition: LLVM.h:37
REGISTER_SET_WITH_PROGRAMSTATE
#define REGISTER_SET_WITH_PROGRAMSTATE(Name, Elem)
Declares an immutable set of type NameTy, suitable for placement into the ProgramState.
Definition: ProgramStateTrait.h:112
clang::ento::categories::MemoryError
const char *const MemoryError
Definition: CommonBugCategories.cpp:20
BugType.h
State
LineState State
Definition: UnwrappedLineFormatter.cpp:1147
CheckerContext.h
Checker.h
clang
Definition: CalledOnceCheck.h:17
clang::Stmt
Stmt - This represents one statement.
Definition: Stmt.h:71
clang::interp::Call
bool Call(InterpState &S, CodePtr &PC, const Function *Func)
Definition: Interp.h:1246
clang::FunctionDecl
Represents a function declaration or definition.
Definition: Decl.h:1904
clang::ASTContext::getLangOpts
const LangOptions & getLangOpts() const
Definition: ASTContext.h:764