1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | |
14 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
15 | #include "clang/Basic/TargetInfo.h" |
16 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
17 | #include "clang/StaticAnalyzer/Core/Checker.h" |
18 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
19 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
20 | #include "llvm/ADT/Optional.h" |
21 | #include "llvm/ADT/STLExtras.h" |
22 | #include "llvm/ADT/SmallString.h" |
23 | #include "llvm/Support/raw_ostream.h" |
24 | |
25 | using namespace clang; |
26 | using namespace ento; |
27 | |
28 | enum class OpenVariant { |
29 | |
30 | |
31 | Open, |
32 | |
33 | |
34 | |
35 | OpenAt |
36 | }; |
37 | |
38 | namespace { |
39 | |
40 | class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > { |
41 | mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce; |
42 | mutable Optional<uint64_t> Val_O_CREAT; |
43 | |
44 | public: |
45 | DefaultBool CheckMisuse, CheckPortability; |
46 | |
47 | void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; |
48 | |
49 | void CheckOpen(CheckerContext &C, const CallExpr *CE) const; |
50 | void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const; |
51 | void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; |
52 | |
53 | void CheckOpenVariant(CheckerContext &C, |
54 | const CallExpr *CE, OpenVariant Variant) const; |
55 | |
56 | void ReportOpenBug(CheckerContext &C, |
57 | ProgramStateRef State, |
58 | const char *Msg, |
59 | SourceRange SR) const; |
60 | |
61 | }; |
62 | |
63 | class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > { |
64 | public: |
65 | void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; |
66 | |
67 | private: |
68 | mutable std::unique_ptr<BugType> BT_mallocZero; |
69 | |
70 | void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const; |
71 | void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; |
72 | void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const; |
73 | void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const; |
74 | void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const; |
75 | void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const; |
76 | void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const; |
77 | |
78 | bool ReportZeroByteAllocation(CheckerContext &C, |
79 | ProgramStateRef falseState, |
80 | const Expr *arg, |
81 | const char *fn_name) const; |
82 | void BasicAllocationCheck(CheckerContext &C, |
83 | const CallExpr *CE, |
84 | const unsigned numArgs, |
85 | const unsigned sizeArg, |
86 | const char *fn) const; |
87 | }; |
88 | |
89 | } |
90 | |
91 | static void LazyInitialize(const CheckerBase *Checker, |
92 | std::unique_ptr<BugType> &BT, |
93 | const char *name) { |
94 | if (BT) |
95 | return; |
96 | BT.reset(new BugType(Checker, name, categories::UnixAPI)); |
97 | } |
98 | |
99 | |
100 | |
101 | |
102 | |
103 | void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE, |
104 | CheckerContext &C) const { |
105 | const FunctionDecl *FD = C.getCalleeDecl(CE); |
106 | if (!FD || FD->getKind() != Decl::Function) |
107 | return; |
108 | |
109 | |
110 | |
111 | const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); |
112 | if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) |
113 | return; |
114 | |
115 | StringRef FName = C.getCalleeName(FD); |
116 | if (FName.empty()) |
117 | return; |
118 | |
119 | if (FName == "open") |
120 | CheckOpen(C, CE); |
121 | |
122 | else if (FName == "openat") |
123 | CheckOpenAt(C, CE); |
124 | |
125 | else if (FName == "pthread_once") |
126 | CheckPthreadOnce(C, CE); |
127 | } |
128 | void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C, |
129 | ProgramStateRef State, |
130 | const char *Msg, |
131 | SourceRange SR) const { |
132 | ExplodedNode *N = C.generateErrorNode(State); |
133 | if (!N) |
134 | return; |
135 | |
136 | LazyInitialize(this, BT_open, "Improper use of 'open'"); |
137 | |
138 | auto Report = llvm::make_unique<BugReport>(*BT_open, Msg, N); |
139 | Report->addRange(SR); |
140 | C.emitReport(std::move(Report)); |
141 | } |
142 | |
143 | void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C, |
144 | const CallExpr *CE) const { |
145 | CheckOpenVariant(C, CE, OpenVariant::Open); |
146 | } |
147 | |
148 | void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C, |
149 | const CallExpr *CE) const { |
150 | CheckOpenVariant(C, CE, OpenVariant::OpenAt); |
151 | } |
152 | |
153 | void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C, |
154 | const CallExpr *CE, |
155 | OpenVariant Variant) const { |
156 | |
157 | |
158 | unsigned int FlagsArgIndex; |
159 | const char *VariantName; |
160 | switch (Variant) { |
161 | case OpenVariant::Open: |
162 | FlagsArgIndex = 1; |
163 | VariantName = "open"; |
164 | break; |
165 | case OpenVariant::OpenAt: |
166 | FlagsArgIndex = 2; |
167 | VariantName = "openat"; |
168 | break; |
169 | }; |
170 | |
171 | |
172 | unsigned int MinArgCount = FlagsArgIndex + 1; |
173 | |
174 | |
175 | |
176 | unsigned int CreateModeArgIndex = FlagsArgIndex + 1; |
177 | |
178 | |
179 | unsigned int MaxArgCount = CreateModeArgIndex + 1; |
180 | |
181 | ProgramStateRef state = C.getState(); |
182 | |
183 | if (CE->getNumArgs() < MinArgCount) { |
184 | |
185 | |
186 | return; |
187 | } else if (CE->getNumArgs() == MaxArgCount) { |
188 | const Expr *Arg = CE->getArg(CreateModeArgIndex); |
189 | QualType QT = Arg->getType(); |
190 | if (!QT->isIntegerType()) { |
191 | SmallString<256> SBuf; |
192 | llvm::raw_svector_ostream OS(SBuf); |
193 | OS << "The " << CreateModeArgIndex + 1 |
194 | << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) |
195 | << " argument to '" << VariantName << "' is not an integer"; |
196 | |
197 | ReportOpenBug(C, state, |
198 | SBuf.c_str(), |
199 | Arg->getSourceRange()); |
200 | return; |
201 | } |
202 | } else if (CE->getNumArgs() > MaxArgCount) { |
203 | SmallString<256> SBuf; |
204 | llvm::raw_svector_ostream OS(SBuf); |
205 | OS << "Call to '" << VariantName << "' with more than " << MaxArgCount |
206 | << " arguments"; |
207 | |
208 | ReportOpenBug(C, state, |
209 | SBuf.c_str(), |
210 | CE->getArg(MaxArgCount)->getSourceRange()); |
211 | return; |
212 | } |
213 | |
214 | |
215 | |
216 | if (!Val_O_CREAT.hasValue()) { |
217 | if (C.getASTContext().getTargetInfo().getTriple().getVendor() |
218 | == llvm::Triple::Apple) |
219 | Val_O_CREAT = 0x0200; |
220 | else { |
221 | |
222 | |
223 | |
224 | |
225 | return; |
226 | } |
227 | } |
228 | |
229 | |
230 | const Expr *oflagsEx = CE->getArg(FlagsArgIndex); |
231 | const SVal V = C.getSVal(oflagsEx); |
232 | if (!V.getAs<NonLoc>()) { |
233 | |
234 | |
235 | return; |
236 | } |
237 | NonLoc oflags = V.castAs<NonLoc>(); |
238 | NonLoc ocreateFlag = C.getSValBuilder() |
239 | .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>(); |
240 | SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, |
241 | oflags, ocreateFlag, |
242 | oflagsEx->getType()); |
243 | if (maskedFlagsUC.isUnknownOrUndef()) |
244 | return; |
245 | DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>(); |
246 | |
247 | |
248 | ProgramStateRef trueState, falseState; |
249 | std::tie(trueState, falseState) = state->assume(maskedFlags); |
250 | |
251 | |
252 | |
253 | if (!(trueState && !falseState)) |
254 | return; |
255 | |
256 | if (CE->getNumArgs() < MaxArgCount) { |
257 | SmallString<256> SBuf; |
258 | llvm::raw_svector_ostream OS(SBuf); |
259 | OS << "Call to '" << VariantName << "' requires a " |
260 | << CreateModeArgIndex + 1 |
261 | << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) |
262 | << " argument when the 'O_CREAT' flag is set"; |
263 | ReportOpenBug(C, trueState, |
264 | SBuf.c_str(), |
265 | oflagsEx->getSourceRange()); |
266 | } |
267 | } |
268 | |
269 | |
270 | |
271 | |
272 | |
273 | void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C, |
274 | const CallExpr *CE) const { |
275 | |
276 | |
277 | |
278 | |
279 | if (CE->getNumArgs() < 1) |
280 | return; |
281 | |
282 | |
283 | |
284 | ProgramStateRef state = C.getState(); |
285 | const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion(); |
286 | if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) |
287 | return; |
288 | |
289 | ExplodedNode *N = C.generateErrorNode(state); |
290 | if (!N) |
291 | return; |
292 | |
293 | SmallString<256> S; |
294 | llvm::raw_svector_ostream os(S); |
295 | os << "Call to 'pthread_once' uses"; |
296 | if (const VarRegion *VR = dyn_cast<VarRegion>(R)) |
297 | os << " the local variable '" << VR->getDecl()->getName() << '\''; |
298 | else |
299 | os << " stack allocated memory"; |
300 | os << " for the \"control\" value. Using such transient memory for " |
301 | "the control value is potentially dangerous."; |
302 | if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) |
303 | os << " Perhaps you intended to declare the variable as 'static'?"; |
304 | |
305 | LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'"); |
306 | |
307 | auto report = llvm::make_unique<BugReport>(*BT_pthreadOnce, os.str(), N); |
308 | report->addRange(CE->getArg(0)->getSourceRange()); |
309 | C.emitReport(std::move(report)); |
310 | } |
311 | |
312 | |
313 | |
314 | |
315 | |
316 | |
317 | |
318 | |
319 | |
320 | |
321 | |
322 | static bool IsZeroByteAllocation(ProgramStateRef state, |
323 | const SVal argVal, |
324 | ProgramStateRef *trueState, |
325 | ProgramStateRef *falseState) { |
326 | std::tie(*trueState, *falseState) = |
327 | state->assume(argVal.castAs<DefinedSVal>()); |
328 | |
329 | return (*falseState && !*trueState); |
330 | } |
331 | |
332 | |
333 | |
334 | |
335 | bool UnixAPIPortabilityChecker::ReportZeroByteAllocation( |
336 | CheckerContext &C, |
337 | ProgramStateRef falseState, |
338 | const Expr *arg, |
339 | const char *fn_name) const { |
340 | ExplodedNode *N = C.generateErrorNode(falseState); |
341 | if (!N) |
342 | return false; |
343 | |
344 | LazyInitialize(this, BT_mallocZero, |
345 | "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); |
346 | |
347 | SmallString<256> S; |
348 | llvm::raw_svector_ostream os(S); |
349 | os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; |
350 | auto report = llvm::make_unique<BugReport>(*BT_mallocZero, os.str(), N); |
351 | |
352 | report->addRange(arg->getSourceRange()); |
353 | bugreporter::trackExpressionValue(N, arg, *report); |
354 | C.emitReport(std::move(report)); |
355 | |
356 | return true; |
357 | } |
358 | |
359 | |
360 | |
361 | void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C, |
362 | const CallExpr *CE, |
363 | const unsigned numArgs, |
364 | const unsigned sizeArg, |
365 | const char *fn) const { |
366 | |
367 | if (CE->getNumArgs() != numArgs) |
368 | return; |
369 | |
370 | |
371 | ProgramStateRef state = C.getState(); |
372 | ProgramStateRef trueState = nullptr, falseState = nullptr; |
373 | const Expr *arg = CE->getArg(sizeArg); |
374 | SVal argVal = C.getSVal(arg); |
375 | |
376 | if (argVal.isUnknownOrUndef()) |
377 | return; |
378 | |
379 | |
380 | if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { |
381 | (void) ReportZeroByteAllocation(C, falseState, arg, fn); |
382 | return; |
383 | } |
384 | |
385 | assert(trueState); |
386 | if (trueState != state) |
387 | C.addTransition(trueState); |
388 | } |
389 | |
390 | void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C, |
391 | const CallExpr *CE) const { |
392 | unsigned int nArgs = CE->getNumArgs(); |
393 | if (nArgs != 2) |
394 | return; |
395 | |
396 | ProgramStateRef state = C.getState(); |
397 | ProgramStateRef trueState = nullptr, falseState = nullptr; |
398 | |
399 | unsigned int i; |
400 | for (i = 0; i < nArgs; i++) { |
401 | const Expr *arg = CE->getArg(i); |
402 | SVal argVal = C.getSVal(arg); |
403 | if (argVal.isUnknownOrUndef()) { |
404 | if (i == 0) |
405 | continue; |
406 | else |
407 | return; |
408 | } |
409 | |
410 | if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { |
411 | if (ReportZeroByteAllocation(C, falseState, arg, "calloc")) |
412 | return; |
413 | else if (i == 0) |
414 | continue; |
415 | else |
416 | return; |
417 | } |
418 | } |
419 | |
420 | |
421 | assert(trueState); |
422 | if (trueState != state) |
423 | C.addTransition(trueState); |
424 | } |
425 | |
426 | void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C, |
427 | const CallExpr *CE) const { |
428 | BasicAllocationCheck(C, CE, 1, 0, "malloc"); |
429 | } |
430 | |
431 | void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C, |
432 | const CallExpr *CE) const { |
433 | BasicAllocationCheck(C, CE, 2, 1, "realloc"); |
434 | } |
435 | |
436 | void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C, |
437 | const CallExpr *CE) const { |
438 | BasicAllocationCheck(C, CE, 2, 1, "reallocf"); |
439 | } |
440 | |
441 | void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C, |
442 | const CallExpr *CE) const { |
443 | BasicAllocationCheck(C, CE, 1, 0, "alloca"); |
444 | } |
445 | |
446 | void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero( |
447 | CheckerContext &C, |
448 | const CallExpr *CE) const { |
449 | BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align"); |
450 | } |
451 | |
452 | void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C, |
453 | const CallExpr *CE) const { |
454 | BasicAllocationCheck(C, CE, 1, 0, "valloc"); |
455 | } |
456 | |
457 | void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE, |
458 | CheckerContext &C) const { |
459 | const FunctionDecl *FD = C.getCalleeDecl(CE); |
460 | if (!FD || FD->getKind() != Decl::Function) |
461 | return; |
462 | |
463 | |
464 | |
465 | const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); |
466 | if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) |
467 | return; |
468 | |
469 | StringRef FName = C.getCalleeName(FD); |
470 | if (FName.empty()) |
471 | return; |
472 | |
473 | if (FName == "calloc") |
474 | CheckCallocZero(C, CE); |
475 | |
476 | else if (FName == "malloc") |
477 | CheckMallocZero(C, CE); |
478 | |
479 | else if (FName == "realloc") |
480 | CheckReallocZero(C, CE); |
481 | |
482 | else if (FName == "reallocf") |
483 | CheckReallocfZero(C, CE); |
484 | |
485 | else if (FName == "alloca" || FName == "__builtin_alloca") |
486 | CheckAllocaZero(C, CE); |
487 | |
488 | else if (FName == "__builtin_alloca_with_align") |
489 | CheckAllocaWithAlignZero(C, CE); |
490 | |
491 | else if (FName == "valloc") |
492 | CheckVallocZero(C, CE); |
493 | } |
494 | |
495 | |
496 | |
497 | |
498 | |
499 | #define REGISTER_CHECKER(CHECKERNAME) \ |
500 | void ento::register##CHECKERNAME(CheckerManager &mgr) { \ |
501 | mgr.registerChecker<CHECKERNAME>(); \ |
502 | } \ |
503 | \ |
504 | bool ento::shouldRegister##CHECKERNAME(const LangOptions &LO) { \ |
505 | return true; \ |
506 | } |
507 | |
508 | REGISTER_CHECKER(UnixAPIMisuseChecker) |
509 | REGISTER_CHECKER(UnixAPIPortabilityChecker) |
510 | |