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 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
REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions,const MemRegion *)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
postPreviousReturnInvalidatingCall(const CallEvent & Call,CheckerContext & C) const114 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.
findInvalidatedSymbolicBase(ProgramStateRef State,const MemRegion * Reg)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.
checkPostCall(const CallEvent & Call,CheckerContext & C) const184 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.
checkBeginFunction(CheckerContext & C) const230 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.
checkLocation(SVal Loc,bool isLoad,const Stmt * S,CheckerContext & C) const248 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
registerInvalidPtrChecker(CheckerManager & Mgr)268 void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
269 Mgr.registerChecker<InvalidPtrChecker>();
270 }
271
shouldRegisterInvalidPtrChecker(const CheckerManager &)272 bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
273 return true;
274 }
275