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