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
29 using namespace clang;
30 using namespace ento;
31
32 enum 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
getCreateFlagValue(const ASTContext & Ctx,const Preprocessor & PP)42 static std::optional<int> getCreateFlagValue(const ASTContext &Ctx,
43 const Preprocessor &PP) {
44 std::optional<int> MacroVal = tryExpandAsInteger("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
54 namespace {
55
56 class 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
75 public:
UnixAPIMisuseChecker(const ASTContext & Ctx,const Preprocessor & PP)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 CheckGetDelimOrGetline(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
96 class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
97 public:
98 void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
99
100 private:
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
EnsurePtrNotNull(SVal PtrVal,const Expr * PtrExpr,CheckerContext & C,ProgramStateRef State,const StringRef PtrDescr,std::optional<std::reference_wrapper<const BugType>> BT) const126 ProgramStateRef 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 || !PtrExpr->getType()->isPointerType())
132 return State;
133
134 const auto [PtrNotNull, PtrNull] = State->assume(*Ptr);
135 if (!PtrNotNull && PtrNull) {
136 if (ExplodedNode *N = C.generateErrorNode(PtrNull)) {
137 auto R = std::make_unique<PathSensitiveBugReport>(
138 BT.value_or(std::cref(BT_ArgumentNull)),
139 (PtrDescr + " pointer might be NULL.").str(), N);
140 if (PtrExpr)
141 bugreporter::trackExpressionValue(N, PtrExpr, *R);
142 C.emitReport(std::move(R));
143 }
144 return nullptr;
145 }
146
147 return PtrNotNull;
148 }
149
150 //===----------------------------------------------------------------------===//
151 // "open" (man 2 open)
152 //===----------------------------------------------------------------------===/
153
checkPreCall(const CallEvent & Call,CheckerContext & C) const154 void UnixAPIMisuseChecker::checkPreCall(const CallEvent &Call,
155 CheckerContext &C) const {
156 const FunctionDecl *FD = dyn_cast_if_present<FunctionDecl>(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>(NamespaceCtx))
164 return;
165
166 StringRef FName = C.getCalleeName(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({"getdelim", "getline"}, FName))
180 CheckGetDelimOrGetline(C, Call);
181 }
ReportOpenBug(CheckerContext & C,ProgramStateRef State,const char * Msg,SourceRange SR) const182 void 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>(BT_open, Msg, N);
191 Report->addRange(SR);
192 C.emitReport(std::move(Report));
193 }
194
CheckOpen(CheckerContext & C,const CallEvent & Call) const195 void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,
196 const CallEvent &Call) const {
197 CheckOpenVariant(C, Call, OpenVariant::Open);
198 }
199
CheckOpenAt(CheckerContext & C,const CallEvent & Call) const200 void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,
201 const CallEvent &Call) const {
202 CheckOpenVariant(C, Call, OpenVariant::OpenAt);
203 }
204
CheckOpenVariant(CheckerContext & C,const CallEvent & Call,OpenVariant Variant) const205 void 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 // The frontend should issue a warning for this case. Just return.
227 if (Call.getNumArgs() < MinArgCount)
228 return;
229
230 // If the flags has O_CREAT set then open/openat() require an additional
231 // argument specifying the file mode (permission bits) for the created file.
232 unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
233
234 // The create mode argument should be the last argument.
235 unsigned int MaxArgCount = CreateModeArgIndex + 1;
236
237 ProgramStateRef state = C.getState();
238 if (Call.getNumArgs() == MaxArgCount) {
239 const Expr *Arg = Call.getArgExpr(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(CreateModeArgIndex + 1)
246 << " argument to '" << VariantName << "' is not an integer";
247
248 ReportOpenBug(C, state,
249 SBuf.c_str(),
250 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, SBuf.c_str(),
260 Call.getArgExpr(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(FlagsArgIndex);
270 const SVal V = Call.getArgSVal(FlagsArgIndex);
271 if (!isa<NonLoc>(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(Val_O_CREAT.value(), oflagsEx->getType())
279 .castAs<NonLoc>();
280 SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
281 oflags, ocreateFlag,
282 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(trueState, falseState) = state->assume(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(CreateModeArgIndex + 1)
302 << " argument when the 'O_CREAT' flag is set";
303 ReportOpenBug(C, trueState,
304 SBuf.c_str(),
305 oflagsEx->getSourceRange());
306 }
307 }
308
309 //===----------------------------------------------------------------------===//
310 // getdelim and getline
311 //===----------------------------------------------------------------------===//
312
EnsureGetdelimBufferAndSizeCorrect(SVal LinePtrPtrSVal,SVal SizePtrSVal,const Expr * LinePtrPtrExpr,const Expr * SizePtrExpr,CheckerContext & C,ProgramStateRef State) const313 ProgramStateRef 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(BugState)) {
326 auto R = std::make_unique<PathSensitiveBugReport>(BT_getline, ErrMsg, N);
327 bugreporter::trackExpressionValue(N, SizePtrExpr, *R);
328 bugreporter::trackExpressionValue(N, LinePtrPtrExpr, *R);
329 C.emitReport(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 const auto LinePtrValOpt = getPointeeVal(LinePtrPtrSVal, State);
336 if (!LinePtrValOpt)
337 return nullptr;
338
339 const auto LinePtrSVal = LinePtrValOpt->getAs<DefinedSVal>();
340 const auto NSVal = getPointeeVal(SizePtrSVal, State);
341 if (!LinePtrSVal || !NSVal || NSVal->isUnknown())
342 return nullptr;
343
344 assert(LinePtrPtrExpr && SizePtrExpr);
345
346 const auto [LinePtrNotNull, LinePtrNull] = State->assume(*LinePtrSVal);
347 if (LinePtrNotNull && !LinePtrNull) {
348 // If `*lineptr` is not null, but `*n` is undefined, there is UB.
349 if (NSVal->isUndef()) {
350 EmitBugReport(LinePtrNotNull, SizeUndef);
351 return nullptr;
352 }
353
354 // If it is defined, and known, its size must be less than or equal to
355 // the buffer size.
356 auto NDefSVal = NSVal->getAs<DefinedSVal>();
357 if (!NDefSVal)
358 return LinePtrNotNull;
359
360 auto &SVB = C.getSValBuilder();
361
362 const MemRegion *LinePtrRegion = LinePtrSVal->getAsRegion();
363 if (!LinePtrRegion)
364 return LinePtrNotNull;
365
366 auto LineBufSize = getDynamicExtent(LinePtrNotNull, LinePtrRegion, SVB);
367 auto LineBufSizeGtN = SVB.evalBinOp(LinePtrNotNull, BO_GE, LineBufSize,
368 *NDefSVal, SVB.getConditionType())
369 .getAs<DefinedOrUnknownSVal>();
370 if (!LineBufSizeGtN)
371 return LinePtrNotNull;
372 if (auto LineBufSizeOk = LinePtrNotNull->assume(*LineBufSizeGtN, true))
373 return LineBufSizeOk;
374
375 EmitBugReport(LinePtrNotNull, SizeGreaterThanBufferSize);
376 return nullptr;
377 }
378 return State;
379 }
380
CheckGetDelimOrGetline(CheckerContext & C,const CallEvent & Call) const381 void UnixAPIMisuseChecker::CheckGetDelimOrGetline(CheckerContext &C,
382 const CallEvent &Call) const {
383 if (Call.getNumArgs() < 2)
384 return;
385
386 ProgramStateRef State = C.getState();
387
388 // The parameter `n` must not be NULL.
389 SVal SizePtrSval = Call.getArgSVal(1);
390 State = EnsurePtrNotNull(SizePtrSval, Call.getArgExpr(1), C, State, "Size");
391 if (!State)
392 return;
393
394 // The parameter `lineptr` must not be NULL.
395 SVal LinePtrPtrSVal = Call.getArgSVal(0);
396 State =
397 EnsurePtrNotNull(LinePtrPtrSVal, Call.getArgExpr(0), C, State, "Line");
398 if (!State)
399 return;
400
401 State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,
402 Call.getArgExpr(0),
403 Call.getArgExpr(1), C, State);
404 if (!State)
405 return;
406
407 C.addTransition(State);
408 }
409
410 //===----------------------------------------------------------------------===//
411 // pthread_once
412 //===----------------------------------------------------------------------===//
413
CheckPthreadOnce(CheckerContext & C,const CallEvent & Call) const414 void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
415 const CallEvent &Call) const {
416
417 // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
418 // They can possibly be refactored.
419
420 if (Call.getNumArgs() < 1)
421 return;
422
423 // Check if the first argument is stack allocated. If so, issue a warning
424 // because that's likely to be bad news.
425 ProgramStateRef state = C.getState();
426 const MemRegion *R = Call.getArgSVal(0).getAsRegion();
427 if (!R || !R->hasMemorySpace<StackSpaceRegion>(state))
428 return;
429
430 ExplodedNode *N = C.generateErrorNode(state);
431 if (!N)
432 return;
433
434 SmallString<256> S;
435 llvm::raw_svector_ostream os(S);
436 os << "Call to 'pthread_once' uses";
437 if (const VarRegion *VR = dyn_cast<VarRegion>(R))
438 os << " the local variable '" << VR->getDecl()->getName() << '\'';
439 else
440 os << " stack allocated memory";
441 os << " for the \"control\" value. Using such transient memory for "
442 "the control value is potentially dangerous.";
443 if (isa<VarRegion>(R) && R->hasMemorySpace<StackLocalsSpaceRegion>(state))
444 os << " Perhaps you intended to declare the variable as 'static'?";
445
446 auto report =
447 std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N);
448 report->addRange(Call.getArgExpr(0)->getSourceRange());
449 C.emitReport(std::move(report));
450 }
451
452 //===----------------------------------------------------------------------===//
453 // "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
454 // with allocation size 0
455 //===----------------------------------------------------------------------===//
456
457 // FIXME: Eventually these should be rolled into the MallocChecker, but right now
458 // they're more basic and valuable for widespread use.
459
460 // Returns true if we try to do a zero byte allocation, false otherwise.
461 // Fills in trueState and falseState.
IsZeroByteAllocation(ProgramStateRef state,const SVal argVal,ProgramStateRef * trueState,ProgramStateRef * falseState)462 static bool IsZeroByteAllocation(ProgramStateRef state,
463 const SVal argVal,
464 ProgramStateRef *trueState,
465 ProgramStateRef *falseState) {
466 std::tie(*trueState, *falseState) =
467 state->assume(argVal.castAs<DefinedSVal>());
468
469 return (*falseState && !*trueState);
470 }
471
472 // Generates an error report, indicating that the function whose name is given
473 // will perform a zero byte allocation.
474 // Returns false if an error occurred, true otherwise.
ReportZeroByteAllocation(CheckerContext & C,ProgramStateRef falseState,const Expr * arg,const char * fn_name) const475 bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
476 CheckerContext &C,
477 ProgramStateRef falseState,
478 const Expr *arg,
479 const char *fn_name) const {
480 ExplodedNode *N = C.generateErrorNode(falseState);
481 if (!N)
482 return false;
483
484 SmallString<256> S;
485 llvm::raw_svector_ostream os(S);
486 os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
487 auto report =
488 std::make_unique<PathSensitiveBugReport>(BT_mallocZero, os.str(), N);
489
490 report->addRange(arg->getSourceRange());
491 bugreporter::trackExpressionValue(N, arg, *report);
492 C.emitReport(std::move(report));
493
494 return true;
495 }
496
497 // Does a basic check for 0-sized allocations suitable for most of the below
498 // functions (modulo "calloc")
BasicAllocationCheck(CheckerContext & C,const CallExpr * CE,const unsigned numArgs,const unsigned sizeArg,const char * fn) const499 void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,
500 const CallExpr *CE,
501 const unsigned numArgs,
502 const unsigned sizeArg,
503 const char *fn) const {
504 // Check for the correct number of arguments.
505 if (CE->getNumArgs() != numArgs)
506 return;
507
508 // Check if the allocation size is 0.
509 ProgramStateRef state = C.getState();
510 ProgramStateRef trueState = nullptr, falseState = nullptr;
511 const Expr *arg = CE->getArg(sizeArg);
512 SVal argVal = C.getSVal(arg);
513
514 if (argVal.isUnknownOrUndef())
515 return;
516
517 // Is the value perfectly constrained to zero?
518 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
519 (void) ReportZeroByteAllocation(C, falseState, arg, fn);
520 return;
521 }
522 // Assume the value is non-zero going forward.
523 assert(trueState);
524 if (trueState != state)
525 C.addTransition(trueState);
526 }
527
CheckCallocZero(CheckerContext & C,const CallExpr * CE) const528 void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,
529 const CallExpr *CE) const {
530 unsigned int nArgs = CE->getNumArgs();
531 if (nArgs != 2)
532 return;
533
534 ProgramStateRef state = C.getState();
535 ProgramStateRef trueState = nullptr, falseState = nullptr;
536
537 unsigned int i;
538 for (i = 0; i < nArgs; i++) {
539 const Expr *arg = CE->getArg(i);
540 SVal argVal = C.getSVal(arg);
541 if (argVal.isUnknownOrUndef()) {
542 if (i == 0)
543 continue;
544 return;
545 }
546
547 if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
548 if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
549 return;
550 if (i == 0)
551 continue;
552 return;
553 }
554 }
555
556 // Assume the value is non-zero going forward.
557 assert(trueState);
558 if (trueState != state)
559 C.addTransition(trueState);
560 }
561
CheckMallocZero(CheckerContext & C,const CallExpr * CE) const562 void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,
563 const CallExpr *CE) const {
564 BasicAllocationCheck(C, CE, 1, 0, "malloc");
565 }
566
CheckReallocZero(CheckerContext & C,const CallExpr * CE) const567 void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,
568 const CallExpr *CE) const {
569 BasicAllocationCheck(C, CE, 2, 1, "realloc");
570 }
571
CheckReallocfZero(CheckerContext & C,const CallExpr * CE) const572 void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,
573 const CallExpr *CE) const {
574 BasicAllocationCheck(C, CE, 2, 1, "reallocf");
575 }
576
CheckAllocaZero(CheckerContext & C,const CallExpr * CE) const577 void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,
578 const CallExpr *CE) const {
579 BasicAllocationCheck(C, CE, 1, 0, "alloca");
580 }
581
CheckAllocaWithAlignZero(CheckerContext & C,const CallExpr * CE) const582 void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
583 CheckerContext &C,
584 const CallExpr *CE) const {
585 BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");
586 }
587
CheckVallocZero(CheckerContext & C,const CallExpr * CE) const588 void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,
589 const CallExpr *CE) const {
590 BasicAllocationCheck(C, CE, 1, 0, "valloc");
591 }
592
checkPreStmt(const CallExpr * CE,CheckerContext & C) const593 void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
594 CheckerContext &C) const {
595 const FunctionDecl *FD = C.getCalleeDecl(CE);
596 if (!FD || FD->getKind() != Decl::Function)
597 return;
598
599 // Don't treat functions in namespaces with the same name a Unix function
600 // as a call to the Unix function.
601 const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
602 if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
603 return;
604
605 StringRef FName = C.getCalleeName(FD);
606 if (FName.empty())
607 return;
608
609 if (FName == "calloc")
610 CheckCallocZero(C, CE);
611
612 else if (FName == "malloc")
613 CheckMallocZero(C, CE);
614
615 else if (FName == "realloc")
616 CheckReallocZero(C, CE);
617
618 else if (FName == "reallocf")
619 CheckReallocfZero(C, CE);
620
621 else if (FName == "alloca" || FName == "__builtin_alloca")
622 CheckAllocaZero(C, CE);
623
624 else if (FName == "__builtin_alloca_with_align")
625 CheckAllocaWithAlignZero(C, CE);
626
627 else if (FName == "valloc")
628 CheckVallocZero(C, CE);
629 }
630
631 //===----------------------------------------------------------------------===//
632 // Registration.
633 //===----------------------------------------------------------------------===//
634
registerUnixAPIMisuseChecker(CheckerManager & Mgr)635 void ento::registerUnixAPIMisuseChecker(CheckerManager &Mgr) {
636 Mgr.registerChecker<UnixAPIMisuseChecker>(Mgr.getASTContext(),
637 Mgr.getPreprocessor());
638 }
shouldRegisterUnixAPIMisuseChecker(const CheckerManager & Mgr)639 bool ento::shouldRegisterUnixAPIMisuseChecker(const CheckerManager &Mgr) {
640 return true;
641 }
642
registerUnixAPIPortabilityChecker(CheckerManager & Mgr)643 void ento::registerUnixAPIPortabilityChecker(CheckerManager &Mgr) {
644 Mgr.registerChecker<UnixAPIPortabilityChecker>();
645 }
shouldRegisterUnixAPIPortabilityChecker(const CheckerManager & Mgr)646 bool ento::shouldRegisterUnixAPIPortabilityChecker(const CheckerManager &Mgr) {
647 return true;
648 }
649