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