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/CallEvent.h"
21 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
23 #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
24 #include "llvm/ADT/STLExtras.h"
25 #include "llvm/ADT/SmallString.h"
26 #include "llvm/ADT/StringExtras.h"
27 #include "llvm/Support/raw_ostream.h"
28 #include <optional>
29
30 using namespace clang;
31 using namespace ento;
32
33 enum class OpenVariant {
34 /// The standard open() call:
35 /// int open(const char *path, int oflag, ...);
36 Open,
37
38 /// The variant taking a directory file descriptor and a relative path:
39 /// int openat(int fd, const char *path, int oflag, ...);
40 OpenAt
41 };
42
43 namespace {
44
45 class UnixAPIMisuseChecker
46 : public Checker<check::PreCall, check::ASTDecl<TranslationUnitDecl>> {
47 const BugType BT_open{this, "Improper use of 'open'", categories::UnixAPI};
48 const BugType BT_getline{this, "Improper use of getdelim",
49 categories::UnixAPI};
50 const BugType BT_pthreadOnce{this, "Improper use of 'pthread_once'",
51 categories::UnixAPI};
52 const BugType BT_ArgumentNull{this, "NULL pointer", categories::UnixAPI};
53 mutable std::optional<uint64_t> Val_O_CREAT;
54
55 ProgramStateRef
56 EnsurePtrNotNull(SVal PtrVal, const Expr *PtrExpr, CheckerContext &C,
57 ProgramStateRef State, const StringRef PtrDescr,
58 std::optional<std::reference_wrapper<const BugType>> BT =
59 std::nullopt) const;
60
61 ProgramStateRef EnsureGetdelimBufferAndSizeCorrect(
62 SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
63 const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const;
64
65 public:
66 void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager &Mgr,
67 BugReporter &BR) const;
68
69 void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
70
71 void CheckOpen(CheckerContext &C, const CallEvent &Call) const;
72 void CheckOpenAt(CheckerContext &C, const CallEvent &Call) const;
73 void CheckGetDelim(CheckerContext &C, const CallEvent &Call) const;
74 void CheckPthreadOnce(CheckerContext &C, const CallEvent &Call) const;
75
76 void CheckOpenVariant(CheckerContext &C, const CallEvent &Call,
77 OpenVariant Variant) const;
78
79 void ReportOpenBug(CheckerContext &C, ProgramStateRef State, const char *Msg,
80 SourceRange SR) const;
81 };
82
83 class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
84 public:
85 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
86
87 private:
88 const BugType BT_mallocZero{
89 this, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)",
90 categories::UnixAPI};
91
92 void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
93 void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
94 void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
95 void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const;
96 void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
97 void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const;
98 void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
99
100 bool ReportZeroByteAllocation(CheckerContext &C,
101 ProgramStateRef falseState,
102 const Expr *arg,
103 const char *fn_name) const;
104 void BasicAllocationCheck(CheckerContext &C,
105 const CallExpr *CE,
106 const unsigned numArgs,
107 const unsigned sizeArg,
108 const char *fn) const;
109 };
110
111 } // end anonymous namespace
112
EnsurePtrNotNull(SVal PtrVal,const Expr * PtrExpr,CheckerContext & C,ProgramStateRef State,const StringRef PtrDescr,std::optional<std::reference_wrapper<const BugType>> BT) const113 ProgramStateRef UnixAPIMisuseChecker::EnsurePtrNotNull(
114 SVal PtrVal, const Expr *PtrExpr, CheckerContext &C, ProgramStateRef State,
115 const StringRef PtrDescr,
116 std::optional<std::reference_wrapper<const BugType>> BT) const {
117 const auto Ptr = PtrVal.getAs<DefinedSVal>();
118 if (!Ptr)
119 return State;
120
121 const auto [PtrNotNull, PtrNull] = State->assume(*Ptr);
122 if (!PtrNotNull && PtrNull) {
123 if (ExplodedNode *N = C.generateErrorNode(PtrNull)) {
124 auto R = std::make_unique<PathSensitiveBugReport>(
125 BT.value_or(std::cref(BT_ArgumentNull)),
126 (PtrDescr + " pointer might be NULL.").str(), N);
127 if (PtrExpr)
128 bugreporter::trackExpressionValue(N, PtrExpr, *R);
129 C.emitReport(std::move(R));
130 }
131 return nullptr;
132 }
133
134 return PtrNotNull;
135 }
136
checkASTDecl(const TranslationUnitDecl * TU,AnalysisManager & Mgr,BugReporter &) const137 void UnixAPIMisuseChecker::checkASTDecl(const TranslationUnitDecl *TU,
138 AnalysisManager &Mgr,
139 BugReporter &) const {
140 // The definition of O_CREAT is platform specific.
141 // Try to get the macro value from the preprocessor.
142 Val_O_CREAT = tryExpandAsInteger("O_CREAT", Mgr.getPreprocessor());
143 // If we failed, fall-back to known values.
144 if (!Val_O_CREAT) {
145 if (TU->getASTContext().getTargetInfo().getTriple().getVendor() ==
146 llvm::Triple::Apple)
147 Val_O_CREAT = 0x0200;
148 }
149 }
150
151 //===----------------------------------------------------------------------===//
152 // "open" (man 2 open)
153 //===----------------------------------------------------------------------===/
154
checkPreCall(const CallEvent & Call,CheckerContext & C) const155 void UnixAPIMisuseChecker::checkPreCall(const CallEvent &Call,
156 CheckerContext &C) const {
157 const FunctionDecl *FD = dyn_cast_if_present<FunctionDecl>(Call.getDecl());
158 if (!FD || FD->getKind() != Decl::Function)
159 return;
160
161 // Don't treat functions in namespaces with the same name a Unix function
162 // as a call to the Unix function.
163 const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
164 if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
165 return;
166
167 StringRef FName = C.getCalleeName(FD);
168 if (FName.empty())
169 return;
170
171 if (FName == "open")
172 CheckOpen(C, Call);
173
174 else if (FName == "openat")
175 CheckOpenAt(C, Call);
176
177 else if (FName == "pthread_once")
178 CheckPthreadOnce(C, Call);
179
180 else if (is_contained({"getdelim", "getline"}, FName))
181 CheckGetDelim(C, Call);
182 }
ReportOpenBug(CheckerContext & C,ProgramStateRef State,const char * Msg,SourceRange SR) const183 void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
184 ProgramStateRef State,
185 const char *Msg,
186 SourceRange SR) const {
187 ExplodedNode *N = C.generateErrorNode(State);
188 if (!N)
189 return;
190
191 auto Report = std::make_unique<PathSensitiveBugReport>(BT_open, Msg, N);
192 Report->addRange(SR);
193 C.emitReport(std::move(Report));
194 }
195
CheckOpen(CheckerContext & C,const CallEvent & Call) const196 void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,
197 const CallEvent &Call) const {
198 CheckOpenVariant(C, Call, OpenVariant::Open);
199 }
200
CheckOpenAt(CheckerContext & C,const CallEvent & Call) const201 void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,
202 const CallEvent &Call) const {
203 CheckOpenVariant(C, Call, OpenVariant::OpenAt);
204 }
205
CheckOpenVariant(CheckerContext & C,const CallEvent & Call,OpenVariant Variant) const206 void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
207 const CallEvent &Call,
208 OpenVariant Variant) const {
209 // The index of the argument taking the flags open flags (O_RDONLY,
210 // O_WRONLY, O_CREAT, etc.),
211 unsigned int FlagsArgIndex;
212 const char *VariantName;
213 switch (Variant) {
214 case OpenVariant::Open:
215 FlagsArgIndex = 1;
216 VariantName = "open";
217 break;
218 case OpenVariant::OpenAt:
219 FlagsArgIndex = 2;
220 VariantName = "openat";
221 break;
222 };
223
224 // All calls should at least provide arguments up to the 'flags' parameter.
225 unsigned int MinArgCount = FlagsArgIndex + 1;
226
227 // If the flags has O_CREAT set then open/openat() require an additional
228 // argument specifying the file mode (permission bits) for the created file.
229 unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
230
231 // The create mode argument should be the last argument.
232 unsigned int MaxArgCount = CreateModeArgIndex + 1;
233
234 ProgramStateRef state = C.getState();
235
236 if (Call.getNumArgs() < MinArgCount) {
237 // The frontend should issue a warning for this case. Just return.
238 return;
239 } else if (Call.getNumArgs() == MaxArgCount) {
240 const Expr *Arg = Call.getArgExpr(CreateModeArgIndex);
241 QualType QT = Arg->getType();
242 if (!QT->isIntegerType()) {
243 SmallString<256> SBuf;
244 llvm::raw_svector_ostream OS(SBuf);
245 OS << "The " << CreateModeArgIndex + 1
246 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
247 << " argument to '" << VariantName << "' is not an integer";
248
249 ReportOpenBug(C, state,
250 SBuf.c_str(),
251 Arg->getSourceRange());
252 return;
253 }
254 } else if (Call.getNumArgs() > MaxArgCount) {
255 SmallString<256> SBuf;
256 llvm::raw_svector_ostream OS(SBuf);
257 OS << "Call to '" << VariantName << "' with more than " << MaxArgCount
258 << " arguments";
259
260 ReportOpenBug(C, state, SBuf.c_str(),
261 Call.getArgExpr(MaxArgCount)->getSourceRange());
262 return;
263 }
264
265 if (!Val_O_CREAT) {
266 return;
267 }
268
269 // Now check if oflags has O_CREAT set.
270 const Expr *oflagsEx = Call.getArgExpr(FlagsArgIndex);
271 const SVal V = Call.getArgSVal(FlagsArgIndex);
272 if (!isa<NonLoc>(V)) {
273 // The case where 'V' can be a location can only be due to a bad header,
274 // so in this case bail out.
275 return;
276 }
277 NonLoc oflags = V.castAs<NonLoc>();
278 NonLoc ocreateFlag = C.getSValBuilder()
279 .makeIntVal(*Val_O_CREAT, oflagsEx->getType())
280 .castAs<NonLoc>();
281 SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
282 oflags, ocreateFlag,
283 oflagsEx->getType());
284 if (maskedFlagsUC.isUnknownOrUndef())
285 return;
286 DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
287
288 // Check if maskedFlags is non-zero.
289 ProgramStateRef trueState, falseState;
290 std::tie(trueState, falseState) = state->assume(maskedFlags);
291
292 // Only emit an error if the value of 'maskedFlags' is properly
293 // constrained;
294 if (!(trueState && !falseState))
295 return;
296
297 if (Call.getNumArgs() < MaxArgCount) {
298 SmallString<256> SBuf;
299 llvm::raw_svector_ostream OS(SBuf);
300 OS << "Call to '" << VariantName << "' requires a "
301 << CreateModeArgIndex + 1
302 << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
303 << " argument when the 'O_CREAT' flag is set";
304 ReportOpenBug(C, trueState,
305 SBuf.c_str(),
306 oflagsEx->getSourceRange());
307 }
308 }
309
310 //===----------------------------------------------------------------------===//
311 // getdelim and getline
312 //===----------------------------------------------------------------------===//
313
EnsureGetdelimBufferAndSizeCorrect(SVal LinePtrPtrSVal,SVal SizePtrSVal,const Expr * LinePtrPtrExpr,const Expr * SizePtrExpr,CheckerContext & C,ProgramStateRef State) const314 ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(
315 SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
316 const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const {
317 static constexpr llvm::StringLiteral SizeGreaterThanBufferSize =
318 "The buffer from the first argument is smaller than the size "
319 "specified by the second parameter";
320 static constexpr llvm::StringLiteral SizeUndef =
321 "The buffer from the first argument is not NULL, but the size specified "
322 "by the second parameter is undefined.";
323
324 auto EmitBugReport = [this, &C, SizePtrExpr, LinePtrPtrExpr](
325 ProgramStateRef BugState, StringRef ErrMsg) {
326 if (ExplodedNode *N = C.generateErrorNode(BugState)) {
327 auto R = std::make_unique<PathSensitiveBugReport>(BT_getline, ErrMsg, N);
328 bugreporter::trackExpressionValue(N, SizePtrExpr, *R);
329 bugreporter::trackExpressionValue(N, LinePtrPtrExpr, *R);
330 C.emitReport(std::move(R));
331 }
332 };
333
334 // We have a pointer to a pointer to the buffer, and a pointer to the size.
335 // We want what they point at.
336 auto LinePtrSVal = getPointeeVal(LinePtrPtrSVal, State)->getAs<DefinedSVal>();
337 auto NSVal = getPointeeVal(SizePtrSVal, State);
338 if (!LinePtrSVal || !NSVal || NSVal->isUnknown())
339 return nullptr;
340
341 assert(LinePtrPtrExpr && SizePtrExpr);
342
343 const auto [LinePtrNotNull, LinePtrNull] = State->assume(*LinePtrSVal);
344 if (LinePtrNotNull && !LinePtrNull) {
345 // If `*lineptr` is not null, but `*n` is undefined, there is UB.
346 if (NSVal->isUndef()) {
347 EmitBugReport(LinePtrNotNull, SizeUndef);
348 return nullptr;
349 }
350
351 // If it is defined, and known, its size must be less than or equal to
352 // the buffer size.
353 auto NDefSVal = NSVal->getAs<DefinedSVal>();
354 auto &SVB = C.getSValBuilder();
355 auto LineBufSize =
356 getDynamicExtent(LinePtrNotNull, LinePtrSVal->getAsRegion(), SVB);
357 auto LineBufSizeGtN = SVB.evalBinOp(LinePtrNotNull, BO_GE, LineBufSize,
358 *NDefSVal, SVB.getConditionType())
359 .getAs<DefinedOrUnknownSVal>();
360 if (!LineBufSizeGtN)
361 return LinePtrNotNull;
362 if (auto LineBufSizeOk = LinePtrNotNull->assume(*LineBufSizeGtN, true))
363 return LineBufSizeOk;
364
365 EmitBugReport(LinePtrNotNull, SizeGreaterThanBufferSize);
366 return nullptr;
367 }
368 return State;
369 }
370
CheckGetDelim(CheckerContext & C,const CallEvent & Call) const371 void UnixAPIMisuseChecker::CheckGetDelim(CheckerContext &C,
372 const CallEvent &Call) const {
373 ProgramStateRef State = C.getState();
374
375 // The parameter `n` must not be NULL.
376 SVal SizePtrSval = Call.getArgSVal(1);
377 State = EnsurePtrNotNull(SizePtrSval, Call.getArgExpr(1), C, State, "Size");
378 if (!State)
379 return;
380
381 // The parameter `lineptr` must not be NULL.
382 SVal LinePtrPtrSVal = Call.getArgSVal(0);
383 State =
384 EnsurePtrNotNull(LinePtrPtrSVal, Call.getArgExpr(0), C, State, "Line");
385 if (!State)
386 return;
387
388 State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,
389 Call.getArgExpr(0),
390 Call.getArgExpr(1), C, State);
391 if (!State)
392 return;
393
394 C.addTransition(State);
395 }
396
397 //===----------------------------------------------------------------------===//
398 // pthread_once
399 //===----------------------------------------------------------------------===//
400
CheckPthreadOnce(CheckerContext & C,const CallEvent & Call) const401 void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
402 const CallEvent &Call) const {
403
404 // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
405 // They can possibly be refactored.
406
407 if (Call.getNumArgs() < 1)
408 return;
409
410 // Check if the first argument is stack allocated. If so, issue a warning
411 // because that's likely to be bad news.
412 ProgramStateRef state = C.getState();
413 const MemRegion *R = Call.getArgSVal(0).getAsRegion();
414 if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
415 return;
416
417 ExplodedNode *N = C.generateErrorNode(state);
418 if (!N)
419 return;
420
421 SmallString<256> S;
422 llvm::raw_svector_ostream os(S);
423 os << "Call to 'pthread_once' uses";
424 if (const VarRegion *VR = dyn_cast<VarRegion>(R))
425 os << " the local variable '" << VR->getDecl()->getName() << '\'';
426 else
427 os << " stack allocated memory";
428 os << " for the \"control\" value. Using such transient memory for "
429 "the control value is potentially dangerous.";
430 if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
431 os << " Perhaps you intended to declare the variable as 'static'?";
432
433 auto report =
434 std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N);
435 report->addRange(Call.getArgExpr(0)->getSourceRange());
436 C.emitReport(std::move(report));
437 }
438
439 //===----------------------------------------------------------------------===//
440 // "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
441 // with allocation size 0
442 //===----------------------------------------------------------------------===//
443
444 // FIXME: Eventually these should be rolled into the MallocChecker, but right now
445 // they're more basic and valuable for widespread use.
446
447 // Returns true if we try to do a zero byte allocation, false otherwise.
448 // Fills in trueState and falseState.
IsZeroByteAllocation(ProgramStateRef state,const SVal argVal,ProgramStateRef * trueState,ProgramStateRef * falseState)449 static bool IsZeroByteAllocation(ProgramStateRef state,
450 const SVal argVal,
451 ProgramStateRef *trueState,
452 ProgramStateRef *falseState) {
453 std::tie(*trueState, *falseState) =
454 state->assume(argVal.castAs<DefinedSVal>());
455
456 return (*falseState && !*trueState);
457 }
458
459 // Generates an error report, indicating that the function whose name is given
460 // will perform a zero byte allocation.
461 // Returns false if an error occurred, true otherwise.
ReportZeroByteAllocation(CheckerContext & C,ProgramStateRef falseState,const Expr * arg,const char * fn_name) const462 bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
463 CheckerContext &C,
464 ProgramStateRef falseState,
465 const Expr *arg,
466 const char *fn_name) const {
467 ExplodedNode *N = C.generateErrorNode(falseState);
468 if (!N)
469 return false;
470
471 SmallString<256> S;
472 llvm::raw_svector_ostream os(S);
473 os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
474 auto report =
475 std::make_unique<PathSensitiveBugReport>(BT_mallocZero, os.str(), N);
476
477 report->addRange(arg->getSourceRange());
478 bugreporter::trackExpressionValue(N, arg, *report);
479 C.emitReport(std::move(report));
480
481 return true;
482 }
483
484 // Does a basic check for 0-sized allocations suitable for most of the below
485 // functions (modulo "calloc")
BasicAllocationCheck(CheckerContext & C,const CallExpr * CE,const unsigned numArgs,const unsigned sizeArg,const char * fn) const486 void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,
487 const CallExpr *CE,
488 const unsigned numArgs,
489 const unsigned sizeArg,
490 const char *fn) const {
491 // Check for the correct number of arguments.
492 if (CE->getNumArgs() != numArgs)
493 return;
494
495 // Check if the allocation size is 0.
496 ProgramStateRef state = C.getState();
497 ProgramStateRef trueState = nullptr, falseState = nullptr;
498 const Expr *arg = CE->getArg(sizeArg);
499 SVal argVal = C.getSVal(arg);
500
501 if (argVal.isUnknownOrUndef())
502 return;
503
504 // Is the value perfectly constrained to zero?
505 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
506 (void) ReportZeroByteAllocation(C, falseState, arg, fn);
507 return;
508 }
509 // Assume the value is non-zero going forward.
510 assert(trueState);
511 if (trueState != state)
512 C.addTransition(trueState);
513 }
514
CheckCallocZero(CheckerContext & C,const CallExpr * CE) const515 void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,
516 const CallExpr *CE) const {
517 unsigned int nArgs = CE->getNumArgs();
518 if (nArgs != 2)
519 return;
520
521 ProgramStateRef state = C.getState();
522 ProgramStateRef trueState = nullptr, falseState = nullptr;
523
524 unsigned int i;
525 for (i = 0; i < nArgs; i++) {
526 const Expr *arg = CE->getArg(i);
527 SVal argVal = C.getSVal(arg);
528 if (argVal.isUnknownOrUndef()) {
529 if (i == 0)
530 continue;
531 else
532 return;
533 }
534
535 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
536 if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
537 return;
538 else if (i == 0)
539 continue;
540 else
541 return;
542 }
543 }
544
545 // Assume the value is non-zero going forward.
546 assert(trueState);
547 if (trueState != state)
548 C.addTransition(trueState);
549 }
550
CheckMallocZero(CheckerContext & C,const CallExpr * CE) const551 void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,
552 const CallExpr *CE) const {
553 BasicAllocationCheck(C, CE, 1, 0, "malloc");
554 }
555
CheckReallocZero(CheckerContext & C,const CallExpr * CE) const556 void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,
557 const CallExpr *CE) const {
558 BasicAllocationCheck(C, CE, 2, 1, "realloc");
559 }
560
CheckReallocfZero(CheckerContext & C,const CallExpr * CE) const561 void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,
562 const CallExpr *CE) const {
563 BasicAllocationCheck(C, CE, 2, 1, "reallocf");
564 }
565
CheckAllocaZero(CheckerContext & C,const CallExpr * CE) const566 void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,
567 const CallExpr *CE) const {
568 BasicAllocationCheck(C, CE, 1, 0, "alloca");
569 }
570
CheckAllocaWithAlignZero(CheckerContext & C,const CallExpr * CE) const571 void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
572 CheckerContext &C,
573 const CallExpr *CE) const {
574 BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");
575 }
576
CheckVallocZero(CheckerContext & C,const CallExpr * CE) const577 void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,
578 const CallExpr *CE) const {
579 BasicAllocationCheck(C, CE, 1, 0, "valloc");
580 }
581
checkPreStmt(const CallExpr * CE,CheckerContext & C) const582 void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
583 CheckerContext &C) const {
584 const FunctionDecl *FD = C.getCalleeDecl(CE);
585 if (!FD || FD->getKind() != Decl::Function)
586 return;
587
588 // Don't treat functions in namespaces with the same name a Unix function
589 // as a call to the Unix function.
590 const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
591 if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
592 return;
593
594 StringRef FName = C.getCalleeName(FD);
595 if (FName.empty())
596 return;
597
598 if (FName == "calloc")
599 CheckCallocZero(C, CE);
600
601 else if (FName == "malloc")
602 CheckMallocZero(C, CE);
603
604 else if (FName == "realloc")
605 CheckReallocZero(C, CE);
606
607 else if (FName == "reallocf")
608 CheckReallocfZero(C, CE);
609
610 else if (FName == "alloca" || FName == "__builtin_alloca")
611 CheckAllocaZero(C, CE);
612
613 else if (FName == "__builtin_alloca_with_align")
614 CheckAllocaWithAlignZero(C, CE);
615
616 else if (FName == "valloc")
617 CheckVallocZero(C, CE);
618 }
619
620 //===----------------------------------------------------------------------===//
621 // Registration.
622 //===----------------------------------------------------------------------===//
623
624 #define REGISTER_CHECKER(CHECKERNAME) \
625 void ento::register##CHECKERNAME(CheckerManager &mgr) { \
626 mgr.registerChecker<CHECKERNAME>(); \
627 } \
628 \
629 bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) { \
630 return true; \
631 }
632
633 REGISTER_CHECKER(UnixAPIMisuseChecker)
634 REGISTER_CHECKER(UnixAPIPortabilityChecker)
635