1 //= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- 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 defines UnixAPIChecker, which is an assortment of checks on calls
10 // to various, widely used UNIX/Posix functions.
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/ADT/StringExtras.h"
24 #include "llvm/Support/raw_ostream.h"
25
26 using namespace clang;
27 using namespace ento;
28
29 enum class OpenVariant {
30 /// The standard open() call:
31 /// int open(const char *path, int oflag, ...);
32 Open,
33
34 /// The variant taking a directory file descriptor and a relative path:
35 /// int openat(int fd, const char *path, int oflag, ...);
36 OpenAt
37 };
38
39 namespace {
40
41 class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > {
42 mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce;
43 mutable Optional<uint64_t> Val_O_CREAT;
44
45 public:
46 DefaultBool CheckMisuse, CheckPortability;
47
48 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
49
50 void CheckOpen(CheckerContext &C, const CallExpr *CE) const;
51 void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const;
52 void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const;
53
54 void CheckOpenVariant(CheckerContext &C,
55 const CallExpr *CE, OpenVariant Variant) const;
56
57 void ReportOpenBug(CheckerContext &C,
58 ProgramStateRef State,
59 const char *Msg,
60 SourceRange SR) const;
61
62 };
63
64 class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
65 public:
66 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
67
68 private:
69 mutable std::unique_ptr<BugType> BT_mallocZero;
70
71 void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
72 void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
73 void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
74 void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const;
75 void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
76 void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const;
77 void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
78
79 bool ReportZeroByteAllocation(CheckerContext &C,
80 ProgramStateRef falseState,
81 const Expr *arg,
82 const char *fn_name) const;
83 void BasicAllocationCheck(CheckerContext &C,
84 const CallExpr *CE,
85 const unsigned numArgs,
86 const unsigned sizeArg,
87 const char *fn) const;
88 };
89
90 } //end anonymous namespace
91
LazyInitialize(const CheckerBase * Checker,std::unique_ptr<BugType> & BT,const char * name)92 static void LazyInitialize(const CheckerBase *Checker,
93 std::unique_ptr<BugType> &BT,
94 const char *name) {
95 if (BT)
96 return;
97 BT.reset(new BugType(Checker, name, categories::UnixAPI));
98 }
99
100 //===----------------------------------------------------------------------===//
101 // "open" (man 2 open)
102 //===----------------------------------------------------------------------===/
103
checkPreStmt(const CallExpr * CE,CheckerContext & C) const104 void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE,
105 CheckerContext &C) const {
106 const FunctionDecl *FD = C.getCalleeDecl(CE);
107 if (!FD || FD->getKind() != Decl::Function)
108 return;
109
110 // Don't treat functions in namespaces with the same name a Unix function
111 // as a call to the Unix function.
112 const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
113 if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx))
114 return;
115
116 StringRef FName = C.getCalleeName(FD);
117 if (FName.empty())
118 return;
119
120 if (FName == "open")
121 CheckOpen(C, CE);
122
123 else if (FName == "openat")
124 CheckOpenAt(C, CE);
125
126 else if (FName == "pthread_once")
127 CheckPthreadOnce(C, CE);
128 }
ReportOpenBug(CheckerContext & C,ProgramStateRef State,const char * Msg,SourceRange SR) const129 void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
130 ProgramStateRef State,
131 const char *Msg,
132 SourceRange SR) const {
133 ExplodedNode *N = C.generateErrorNode(State);
134 if (!N)
135 return;
136
137 LazyInitialize(this, BT_open, "Improper use of 'open'");
138
139 auto Report = std::make_unique<PathSensitiveBugReport>(*BT_open, Msg, N);
140 Report->addRange(SR);
141 C.emitReport(std::move(Report));
142 }
143
CheckOpen(CheckerContext & C,const CallExpr * CE) const144 void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,
145 const CallExpr *CE) const {
146 CheckOpenVariant(C, CE, OpenVariant::Open);
147 }
148
CheckOpenAt(CheckerContext & C,const CallExpr * CE) const149 void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,
150 const CallExpr *CE) const {
151 CheckOpenVariant(C, CE, OpenVariant::OpenAt);
152 }
153
CheckOpenVariant(CheckerContext & C,const CallExpr * CE,OpenVariant Variant) const154 void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
155 const CallExpr *CE,
156 OpenVariant Variant) const {
157 // The index of the argument taking the flags open flags (O_RDONLY,
158 // O_WRONLY, O_CREAT, etc.),
159 unsigned int FlagsArgIndex;
160 const char *VariantName;
161 switch (Variant) {
162 case OpenVariant::Open:
163 FlagsArgIndex = 1;
164 VariantName = "open";
165 break;
166 case OpenVariant::OpenAt:
167 FlagsArgIndex = 2;
168 VariantName = "openat";
169 break;
170 };
171
172 // All calls should at least provide arguments up to the 'flags' parameter.
173 unsigned int MinArgCount = FlagsArgIndex + 1;
174
175 // If the flags has O_CREAT set then open/openat() require an additional
176 // argument specifying the file mode (permission bits) for the created file.
177 unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
178
179 // The create mode argument should be the last argument.
180 unsigned int MaxArgCount = CreateModeArgIndex + 1;
181
182 ProgramStateRef state = C.getState();
183
184 if (CE->getNumArgs() < MinArgCount) {
185 // The frontend should issue a warning for this case, so this is a sanity
186 // check.
187 return;
188 } else if (CE->getNumArgs() == MaxArgCount) {
189 const Expr *Arg = CE->getArg(CreateModeArgIndex);
190 QualType QT = Arg->getType();
191 if (!QT->isIntegerType()) {
192 SmallString<256> SBuf;
193 llvm::raw_svector_ostream OS(SBuf);
194 OS << "The " << CreateModeArgIndex + 1
195 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
196 << " argument to '" << VariantName << "' is not an integer";
197
198 ReportOpenBug(C, state,
199 SBuf.c_str(),
200 Arg->getSourceRange());
201 return;
202 }
203 } else if (CE->getNumArgs() > MaxArgCount) {
204 SmallString<256> SBuf;
205 llvm::raw_svector_ostream OS(SBuf);
206 OS << "Call to '" << VariantName << "' with more than " << MaxArgCount
207 << " arguments";
208
209 ReportOpenBug(C, state,
210 SBuf.c_str(),
211 CE->getArg(MaxArgCount)->getSourceRange());
212 return;
213 }
214
215 // The definition of O_CREAT is platform specific. We need a better way
216 // of querying this information from the checking environment.
217 if (!Val_O_CREAT.hasValue()) {
218 if (C.getASTContext().getTargetInfo().getTriple().getVendor()
219 == llvm::Triple::Apple)
220 Val_O_CREAT = 0x0200;
221 else {
222 // FIXME: We need a more general way of getting the O_CREAT value.
223 // We could possibly grovel through the preprocessor state, but
224 // that would require passing the Preprocessor object to the ExprEngine.
225 // See also: MallocChecker.cpp / M_ZERO.
226 return;
227 }
228 }
229
230 // Now check if oflags has O_CREAT set.
231 const Expr *oflagsEx = CE->getArg(FlagsArgIndex);
232 const SVal V = C.getSVal(oflagsEx);
233 if (!V.getAs<NonLoc>()) {
234 // The case where 'V' can be a location can only be due to a bad header,
235 // so in this case bail out.
236 return;
237 }
238 NonLoc oflags = V.castAs<NonLoc>();
239 NonLoc ocreateFlag = C.getSValBuilder()
240 .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>();
241 SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
242 oflags, ocreateFlag,
243 oflagsEx->getType());
244 if (maskedFlagsUC.isUnknownOrUndef())
245 return;
246 DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
247
248 // Check if maskedFlags is non-zero.
249 ProgramStateRef trueState, falseState;
250 std::tie(trueState, falseState) = state->assume(maskedFlags);
251
252 // Only emit an error if the value of 'maskedFlags' is properly
253 // constrained;
254 if (!(trueState && !falseState))
255 return;
256
257 if (CE->getNumArgs() < MaxArgCount) {
258 SmallString<256> SBuf;
259 llvm::raw_svector_ostream OS(SBuf);
260 OS << "Call to '" << VariantName << "' requires a "
261 << CreateModeArgIndex + 1
262 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
263 << " argument when the 'O_CREAT' flag is set";
264 ReportOpenBug(C, trueState,
265 SBuf.c_str(),
266 oflagsEx->getSourceRange());
267 }
268 }
269
270 //===----------------------------------------------------------------------===//
271 // pthread_once
272 //===----------------------------------------------------------------------===//
273
CheckPthreadOnce(CheckerContext & C,const CallExpr * CE) const274 void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
275 const CallExpr *CE) const {
276
277 // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
278 // They can possibly be refactored.
279
280 if (CE->getNumArgs() < 1)
281 return;
282
283 // Check if the first argument is stack allocated. If so, issue a warning
284 // because that's likely to be bad news.
285 ProgramStateRef state = C.getState();
286 const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
287 if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
288 return;
289
290 ExplodedNode *N = C.generateErrorNode(state);
291 if (!N)
292 return;
293
294 SmallString<256> S;
295 llvm::raw_svector_ostream os(S);
296 os << "Call to 'pthread_once' uses";
297 if (const VarRegion *VR = dyn_cast<VarRegion>(R))
298 os << " the local variable '" << VR->getDecl()->getName() << '\'';
299 else
300 os << " stack allocated memory";
301 os << " for the \"control\" value. Using such transient memory for "
302 "the control value is potentially dangerous.";
303 if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
304 os << " Perhaps you intended to declare the variable as 'static'?";
305
306 LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'");
307
308 auto report =
309 std::make_unique<PathSensitiveBugReport>(*BT_pthreadOnce, os.str(), N);
310 report->addRange(CE->getArg(0)->getSourceRange());
311 C.emitReport(std::move(report));
312 }
313
314 //===----------------------------------------------------------------------===//
315 // "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
316 // with allocation size 0
317 //===----------------------------------------------------------------------===//
318
319 // FIXME: Eventually these should be rolled into the MallocChecker, but right now
320 // they're more basic and valuable for widespread use.
321
322 // Returns true if we try to do a zero byte allocation, false otherwise.
323 // Fills in trueState and falseState.
IsZeroByteAllocation(ProgramStateRef state,const SVal argVal,ProgramStateRef * trueState,ProgramStateRef * falseState)324 static bool IsZeroByteAllocation(ProgramStateRef state,
325 const SVal argVal,
326 ProgramStateRef *trueState,
327 ProgramStateRef *falseState) {
328 std::tie(*trueState, *falseState) =
329 state->assume(argVal.castAs<DefinedSVal>());
330
331 return (*falseState && !*trueState);
332 }
333
334 // Generates an error report, indicating that the function whose name is given
335 // will perform a zero byte allocation.
336 // Returns false if an error occurred, true otherwise.
ReportZeroByteAllocation(CheckerContext & C,ProgramStateRef falseState,const Expr * arg,const char * fn_name) const337 bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
338 CheckerContext &C,
339 ProgramStateRef falseState,
340 const Expr *arg,
341 const char *fn_name) const {
342 ExplodedNode *N = C.generateErrorNode(falseState);
343 if (!N)
344 return false;
345
346 LazyInitialize(this, BT_mallocZero,
347 "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
348
349 SmallString<256> S;
350 llvm::raw_svector_ostream os(S);
351 os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
352 auto report =
353 std::make_unique<PathSensitiveBugReport>(*BT_mallocZero, os.str(), N);
354
355 report->addRange(arg->getSourceRange());
356 bugreporter::trackExpressionValue(N, arg, *report);
357 C.emitReport(std::move(report));
358
359 return true;
360 }
361
362 // Does a basic check for 0-sized allocations suitable for most of the below
363 // functions (modulo "calloc")
BasicAllocationCheck(CheckerContext & C,const CallExpr * CE,const unsigned numArgs,const unsigned sizeArg,const char * fn) const364 void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,
365 const CallExpr *CE,
366 const unsigned numArgs,
367 const unsigned sizeArg,
368 const char *fn) const {
369 // Sanity check for the correct number of arguments
370 if (CE->getNumArgs() != numArgs)
371 return;
372
373 // Check if the allocation size is 0.
374 ProgramStateRef state = C.getState();
375 ProgramStateRef trueState = nullptr, falseState = nullptr;
376 const Expr *arg = CE->getArg(sizeArg);
377 SVal argVal = C.getSVal(arg);
378
379 if (argVal.isUnknownOrUndef())
380 return;
381
382 // Is the value perfectly constrained to zero?
383 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
384 (void) ReportZeroByteAllocation(C, falseState, arg, fn);
385 return;
386 }
387 // Assume the value is non-zero going forward.
388 assert(trueState);
389 if (trueState != state)
390 C.addTransition(trueState);
391 }
392
CheckCallocZero(CheckerContext & C,const CallExpr * CE) const393 void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,
394 const CallExpr *CE) const {
395 unsigned int nArgs = CE->getNumArgs();
396 if (nArgs != 2)
397 return;
398
399 ProgramStateRef state = C.getState();
400 ProgramStateRef trueState = nullptr, falseState = nullptr;
401
402 unsigned int i;
403 for (i = 0; i < nArgs; i++) {
404 const Expr *arg = CE->getArg(i);
405 SVal argVal = C.getSVal(arg);
406 if (argVal.isUnknownOrUndef()) {
407 if (i == 0)
408 continue;
409 else
410 return;
411 }
412
413 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
414 if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
415 return;
416 else if (i == 0)
417 continue;
418 else
419 return;
420 }
421 }
422
423 // Assume the value is non-zero going forward.
424 assert(trueState);
425 if (trueState != state)
426 C.addTransition(trueState);
427 }
428
CheckMallocZero(CheckerContext & C,const CallExpr * CE) const429 void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,
430 const CallExpr *CE) const {
431 BasicAllocationCheck(C, CE, 1, 0, "malloc");
432 }
433
CheckReallocZero(CheckerContext & C,const CallExpr * CE) const434 void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,
435 const CallExpr *CE) const {
436 BasicAllocationCheck(C, CE, 2, 1, "realloc");
437 }
438
CheckReallocfZero(CheckerContext & C,const CallExpr * CE) const439 void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,
440 const CallExpr *CE) const {
441 BasicAllocationCheck(C, CE, 2, 1, "reallocf");
442 }
443
CheckAllocaZero(CheckerContext & C,const CallExpr * CE) const444 void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,
445 const CallExpr *CE) const {
446 BasicAllocationCheck(C, CE, 1, 0, "alloca");
447 }
448
CheckAllocaWithAlignZero(CheckerContext & C,const CallExpr * CE) const449 void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
450 CheckerContext &C,
451 const CallExpr *CE) const {
452 BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");
453 }
454
CheckVallocZero(CheckerContext & C,const CallExpr * CE) const455 void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,
456 const CallExpr *CE) const {
457 BasicAllocationCheck(C, CE, 1, 0, "valloc");
458 }
459
checkPreStmt(const CallExpr * CE,CheckerContext & C) const460 void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
461 CheckerContext &C) const {
462 const FunctionDecl *FD = C.getCalleeDecl(CE);
463 if (!FD || FD->getKind() != Decl::Function)
464 return;
465
466 // Don't treat functions in namespaces with the same name a Unix function
467 // as a call to the Unix function.
468 const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
469 if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx))
470 return;
471
472 StringRef FName = C.getCalleeName(FD);
473 if (FName.empty())
474 return;
475
476 if (FName == "calloc")
477 CheckCallocZero(C, CE);
478
479 else if (FName == "malloc")
480 CheckMallocZero(C, CE);
481
482 else if (FName == "realloc")
483 CheckReallocZero(C, CE);
484
485 else if (FName == "reallocf")
486 CheckReallocfZero(C, CE);
487
488 else if (FName == "alloca" || FName == "__builtin_alloca")
489 CheckAllocaZero(C, CE);
490
491 else if (FName == "__builtin_alloca_with_align")
492 CheckAllocaWithAlignZero(C, CE);
493
494 else if (FName == "valloc")
495 CheckVallocZero(C, CE);
496 }
497
498 //===----------------------------------------------------------------------===//
499 // Registration.
500 //===----------------------------------------------------------------------===//
501
502 #define REGISTER_CHECKER(CHECKERNAME) \
503 void ento::register##CHECKERNAME(CheckerManager &mgr) { \
504 mgr.registerChecker<CHECKERNAME>(); \
505 } \
506 \
507 bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) { \
508 return true; \
509 }
510
511 REGISTER_CHECKER(UnixAPIMisuseChecker)
512 REGISTER_CHECKER(UnixAPIPortabilityChecker)
513