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

source code of clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp