1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | |
15 | |
16 | |
17 | |
18 | |
19 | |
20 | |
21 | |
22 | |
23 | |
24 | #include "clang/Analysis/AnyCall.h" |
25 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
26 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
27 | #include "clang/StaticAnalyzer/Core/Checker.h" |
28 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
29 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
30 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
31 | |
32 | using namespace clang; |
33 | using namespace ento; |
34 | |
35 | namespace { |
36 | class MIGChecker : public Checker<check::PostCall, check::PreStmt<ReturnStmt>, |
37 | check::EndFunction> { |
38 | BugType BT{this, "Use-after-free (MIG calling convention violation)", |
39 | categories::MemoryError}; |
40 | |
41 | |
42 | |
43 | |
44 | |
45 | |
46 | std::vector<std::pair<CallDescription, unsigned>> Deallocators = { |
47 | #define CALL(required_args, deallocated_arg, ...) \ |
48 | {{{__VA_ARGS__}, required_args}, deallocated_arg} |
49 | |
50 | |
51 | |
52 | |
53 | CALL(3, 1, "vm_deallocate"), |
54 | CALL(3, 1, "mach_vm_deallocate"), |
55 | CALL(2, 0, "mig_deallocate"), |
56 | CALL(2, 1, "mach_port_deallocate"), |
57 | |
58 | |
59 | |
60 | |
61 | CALL(1, 0, "IOUserClient", "releaseAsyncReference64"), |
62 | #undef CALL |
63 | }; |
64 | |
65 | void checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const; |
66 | |
67 | public: |
68 | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
69 | |
70 | |
71 | |
72 | |
73 | |
74 | |
75 | |
76 | void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { |
77 | checkReturnAux(RS, C); |
78 | } |
79 | void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const { |
80 | checkReturnAux(RS, C); |
81 | } |
82 | |
83 | class Visitor : public BugReporterVisitor { |
84 | public: |
85 | void Profile(llvm::FoldingSetNodeID &ID) const { |
86 | static int X = 0; |
87 | ID.AddPointer(&X); |
88 | } |
89 | |
90 | std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, |
91 | BugReporterContext &BRC, BugReport &R); |
92 | }; |
93 | }; |
94 | } |
95 | |
96 | |
97 | |
98 | REGISTER_TRAIT_WITH_PROGRAMSTATE(ReleasedParameter, const void *) |
99 | |
100 | std::shared_ptr<PathDiagnosticPiece> |
101 | MIGChecker::Visitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC, |
102 | BugReport &R) { |
103 | const auto *NewPVD = static_cast<const ParmVarDecl *>( |
104 | N->getState()->get<ReleasedParameter>()); |
105 | const auto *OldPVD = static_cast<const ParmVarDecl *>( |
106 | N->getFirstPred()->getState()->get<ReleasedParameter>()); |
107 | if (OldPVD == NewPVD) |
108 | return nullptr; |
109 | |
110 | (0) . __assert_fail ("NewPVD && \"What is deallocated cannot be un-deallocated!\"", "/home/seafit/code_projects/clang_source/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp", 110, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(NewPVD && "What is deallocated cannot be un-deallocated!"); |
111 | SmallString<64> Str; |
112 | llvm::raw_svector_ostream OS(Str); |
113 | OS << "Value passed through parameter '" << NewPVD->getName() |
114 | << "' is deallocated"; |
115 | |
116 | PathDiagnosticLocation Loc = |
117 | PathDiagnosticLocation::create(N->getLocation(), BRC.getSourceManager()); |
118 | return std::make_shared<PathDiagnosticEventPiece>(Loc, OS.str()); |
119 | } |
120 | |
121 | static const ParmVarDecl *getOriginParam(SVal V, CheckerContext &C) { |
122 | SymbolRef Sym = V.getAsSymbol(); |
123 | if (!Sym) |
124 | return nullptr; |
125 | |
126 | |
127 | |
128 | |
129 | |
130 | |
131 | while (const MemRegion *MR = Sym->getOriginRegion()) { |
132 | const auto *VR = dyn_cast<VarRegion>(MR); |
133 | if (VR && VR->hasStackParametersStorage() && |
134 | VR->getStackFrame()->inTopFrame()) |
135 | return cast<ParmVarDecl>(VR->getDecl()); |
136 | |
137 | const SymbolicRegion *SR = MR->getSymbolicBase(); |
138 | if (!SR) |
139 | return nullptr; |
140 | |
141 | Sym = SR->getSymbol(); |
142 | } |
143 | |
144 | return nullptr; |
145 | } |
146 | |
147 | static bool isInMIGCall(CheckerContext &C) { |
148 | const LocationContext *LC = C.getLocationContext(); |
149 | const StackFrameContext *SFC; |
150 | |
151 | while (LC) { |
152 | SFC = LC->getStackFrame(); |
153 | LC = SFC->getParent(); |
154 | } |
155 | |
156 | const Decl *D = SFC->getDecl(); |
157 | |
158 | if (Optional<AnyCall> AC = AnyCall::forDecl(D)) { |
159 | |
160 | |
161 | |
162 | |
163 | |
164 | if (!AC->getReturnType(C.getASTContext()) |
165 | .getCanonicalType()->isSignedIntegerType()) |
166 | return false; |
167 | } |
168 | |
169 | if (D->hasAttr<MIGServerRoutineAttr>()) |
170 | return true; |
171 | |
172 | |
173 | if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) |
174 | for (const auto *OMD: MD->overridden_methods()) |
175 | if (OMD->hasAttr<MIGServerRoutineAttr>()) |
176 | return true; |
177 | |
178 | return false; |
179 | } |
180 | |
181 | void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { |
182 | if (!isInMIGCall(C)) |
183 | return; |
184 | |
185 | auto I = std::find_if(Deallocators.begin(), Deallocators.end(), |
186 | [&](const std::pair<CallDescription, unsigned> &Item) { |
187 | return Call.isCalled(Item.first); |
188 | }); |
189 | if (I == Deallocators.end()) |
190 | return; |
191 | |
192 | unsigned ArgIdx = I->second; |
193 | SVal Arg = Call.getArgSVal(ArgIdx); |
194 | const ParmVarDecl *PVD = getOriginParam(Arg, C); |
195 | if (!PVD) |
196 | return; |
197 | |
198 | C.addTransition(C.getState()->set<ReleasedParameter>(PVD)); |
199 | } |
200 | |
201 | |
202 | static bool mayBeSuccess(SVal V, CheckerContext &C) { |
203 | ProgramStateRef State = C.getState(); |
204 | |
205 | |
206 | if (!State->isNull(V).isConstrainedFalse()) |
207 | return true; |
208 | |
209 | SValBuilder &SVB = C.getSValBuilder(); |
210 | ASTContext &ACtx = C.getASTContext(); |
211 | |
212 | |
213 | static const int MigNoReply = -305; |
214 | V = SVB.evalEQ(C.getState(), V, SVB.makeIntVal(MigNoReply, ACtx.IntTy)); |
215 | if (!State->isNull(V).isConstrainedTrue()) |
216 | return true; |
217 | |
218 | |
219 | return false; |
220 | } |
221 | |
222 | void MIGChecker::checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const { |
223 | |
224 | |
225 | |
226 | |
227 | |
228 | |
229 | |
230 | |
231 | if (!C.inTopFrame()) |
232 | return; |
233 | |
234 | if (!isInMIGCall(C)) |
235 | return; |
236 | |
237 | |
238 | |
239 | if (!RS) |
240 | return; |
241 | |
242 | ProgramStateRef State = C.getState(); |
243 | if (!State->get<ReleasedParameter>()) |
244 | return; |
245 | |
246 | SVal V = C.getSVal(RS); |
247 | if (mayBeSuccess(V, C)) |
248 | return; |
249 | |
250 | ExplodedNode *N = C.generateErrorNode(); |
251 | if (!N) |
252 | return; |
253 | |
254 | auto R = llvm::make_unique<BugReport>( |
255 | BT, |
256 | "MIG callback fails with error after deallocating argument value. " |
257 | "This is a use-after-free vulnerability because the caller will try to " |
258 | "deallocate it again", |
259 | N); |
260 | |
261 | R->addRange(RS->getSourceRange()); |
262 | bugreporter::trackExpressionValue(N, RS->getRetValue(), *R, false); |
263 | R->addVisitor(llvm::make_unique<Visitor>()); |
264 | C.emitReport(std::move(R)); |
265 | } |
266 | |
267 | void ento::registerMIGChecker(CheckerManager &Mgr) { |
268 | Mgr.registerChecker<MIGChecker>(); |
269 | } |
270 | |
271 | bool ento::shouldRegisterMIGChecker(const LangOptions &LO) { |
272 | return true; |
273 | } |
274 | |