xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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