xref: /freebsd/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp (revision 647cbc5de815c5651677bf8582797f716ec7b48d)
10b57cec5SDimitry Andric //= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This defines UnixAPIChecker, which is an assortment of checks on calls
100b57cec5SDimitry Andric // to various, widely used UNIX/Posix functions.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
130b57cec5SDimitry Andric 
140b57cec5SDimitry Andric #include "clang/Basic/TargetInfo.h"
15*647cbc5dSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17*647cbc5dSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
210b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
220b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
235ffd83dbSDimitry Andric #include "llvm/ADT/StringExtras.h"
240b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
25bdd1243dSDimitry Andric #include <optional>
260b57cec5SDimitry Andric 
270b57cec5SDimitry Andric using namespace clang;
280b57cec5SDimitry Andric using namespace ento;
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric enum class OpenVariant {
310b57cec5SDimitry Andric   /// The standard open() call:
320b57cec5SDimitry Andric   ///    int open(const char *path, int oflag, ...);
330b57cec5SDimitry Andric   Open,
340b57cec5SDimitry Andric 
350b57cec5SDimitry Andric   /// The variant taking a directory file descriptor and a relative path:
360b57cec5SDimitry Andric   ///    int openat(int fd, const char *path, int oflag, ...);
370b57cec5SDimitry Andric   OpenAt
380b57cec5SDimitry Andric };
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric namespace {
410b57cec5SDimitry Andric 
420b57cec5SDimitry Andric class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > {
43*647cbc5dSDimitry Andric   const BugType BT_open{this, "Improper use of 'open'", categories::UnixAPI};
44*647cbc5dSDimitry Andric   const BugType BT_pthreadOnce{this, "Improper use of 'pthread_once'",
45*647cbc5dSDimitry Andric                                categories::UnixAPI};
46bdd1243dSDimitry Andric   mutable std::optional<uint64_t> Val_O_CREAT;
470b57cec5SDimitry Andric 
480b57cec5SDimitry Andric public:
490b57cec5SDimitry Andric   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
500b57cec5SDimitry Andric 
510b57cec5SDimitry Andric   void CheckOpen(CheckerContext &C, const CallExpr *CE) const;
520b57cec5SDimitry Andric   void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const;
530b57cec5SDimitry Andric   void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const;
540b57cec5SDimitry Andric 
550b57cec5SDimitry Andric   void CheckOpenVariant(CheckerContext &C,
560b57cec5SDimitry Andric                         const CallExpr *CE, OpenVariant Variant) const;
570b57cec5SDimitry Andric 
580b57cec5SDimitry Andric   void ReportOpenBug(CheckerContext &C,
590b57cec5SDimitry Andric                      ProgramStateRef State,
600b57cec5SDimitry Andric                      const char *Msg,
610b57cec5SDimitry Andric                      SourceRange SR) const;
620b57cec5SDimitry Andric 
630b57cec5SDimitry Andric };
640b57cec5SDimitry Andric 
650b57cec5SDimitry Andric class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
660b57cec5SDimitry Andric public:
670b57cec5SDimitry Andric   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
680b57cec5SDimitry Andric 
690b57cec5SDimitry Andric private:
70*647cbc5dSDimitry Andric   const BugType BT_mallocZero{
71*647cbc5dSDimitry Andric       this, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)",
72*647cbc5dSDimitry Andric       categories::UnixAPI};
730b57cec5SDimitry Andric 
740b57cec5SDimitry Andric   void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
750b57cec5SDimitry Andric   void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
760b57cec5SDimitry Andric   void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
770b57cec5SDimitry Andric   void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const;
780b57cec5SDimitry Andric   void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
790b57cec5SDimitry Andric   void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const;
800b57cec5SDimitry Andric   void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
810b57cec5SDimitry Andric 
820b57cec5SDimitry Andric   bool ReportZeroByteAllocation(CheckerContext &C,
830b57cec5SDimitry Andric                                 ProgramStateRef falseState,
840b57cec5SDimitry Andric                                 const Expr *arg,
850b57cec5SDimitry Andric                                 const char *fn_name) const;
860b57cec5SDimitry Andric   void BasicAllocationCheck(CheckerContext &C,
870b57cec5SDimitry Andric                             const CallExpr *CE,
880b57cec5SDimitry Andric                             const unsigned numArgs,
890b57cec5SDimitry Andric                             const unsigned sizeArg,
900b57cec5SDimitry Andric                             const char *fn) const;
910b57cec5SDimitry Andric };
920b57cec5SDimitry Andric 
930b57cec5SDimitry Andric } //end anonymous namespace
940b57cec5SDimitry Andric 
950b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
960b57cec5SDimitry Andric // "open" (man 2 open)
970b57cec5SDimitry Andric //===----------------------------------------------------------------------===/
980b57cec5SDimitry Andric 
990b57cec5SDimitry Andric void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE,
1000b57cec5SDimitry Andric                                         CheckerContext &C) const {
1010b57cec5SDimitry Andric   const FunctionDecl *FD = C.getCalleeDecl(CE);
1020b57cec5SDimitry Andric   if (!FD || FD->getKind() != Decl::Function)
1030b57cec5SDimitry Andric     return;
1040b57cec5SDimitry Andric 
1050b57cec5SDimitry Andric   // Don't treat functions in namespaces with the same name a Unix function
1060b57cec5SDimitry Andric   // as a call to the Unix function.
1070b57cec5SDimitry Andric   const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
108349cc55cSDimitry Andric   if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
1090b57cec5SDimitry Andric     return;
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric   StringRef FName = C.getCalleeName(FD);
1120b57cec5SDimitry Andric   if (FName.empty())
1130b57cec5SDimitry Andric     return;
1140b57cec5SDimitry Andric 
1150b57cec5SDimitry Andric   if (FName == "open")
1160b57cec5SDimitry Andric     CheckOpen(C, CE);
1170b57cec5SDimitry Andric 
1180b57cec5SDimitry Andric   else if (FName == "openat")
1190b57cec5SDimitry Andric     CheckOpenAt(C, CE);
1200b57cec5SDimitry Andric 
1210b57cec5SDimitry Andric   else if (FName == "pthread_once")
1220b57cec5SDimitry Andric     CheckPthreadOnce(C, CE);
1230b57cec5SDimitry Andric }
1240b57cec5SDimitry Andric void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
1250b57cec5SDimitry Andric                                          ProgramStateRef State,
1260b57cec5SDimitry Andric                                          const char *Msg,
1270b57cec5SDimitry Andric                                          SourceRange SR) const {
1280b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode(State);
1290b57cec5SDimitry Andric   if (!N)
1300b57cec5SDimitry Andric     return;
1310b57cec5SDimitry Andric 
132*647cbc5dSDimitry Andric   auto Report = std::make_unique<PathSensitiveBugReport>(BT_open, Msg, N);
1330b57cec5SDimitry Andric   Report->addRange(SR);
1340b57cec5SDimitry Andric   C.emitReport(std::move(Report));
1350b57cec5SDimitry Andric }
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,
1380b57cec5SDimitry Andric                                      const CallExpr *CE) const {
1390b57cec5SDimitry Andric   CheckOpenVariant(C, CE, OpenVariant::Open);
1400b57cec5SDimitry Andric }
1410b57cec5SDimitry Andric 
1420b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,
1430b57cec5SDimitry Andric                                        const CallExpr *CE) const {
1440b57cec5SDimitry Andric   CheckOpenVariant(C, CE, OpenVariant::OpenAt);
1450b57cec5SDimitry Andric }
1460b57cec5SDimitry Andric 
1470b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
1480b57cec5SDimitry Andric                                             const CallExpr *CE,
1490b57cec5SDimitry Andric                                             OpenVariant Variant) const {
1500b57cec5SDimitry Andric   // The index of the argument taking the flags open flags (O_RDONLY,
1510b57cec5SDimitry Andric   // O_WRONLY, O_CREAT, etc.),
1520b57cec5SDimitry Andric   unsigned int FlagsArgIndex;
1530b57cec5SDimitry Andric   const char *VariantName;
1540b57cec5SDimitry Andric   switch (Variant) {
1550b57cec5SDimitry Andric   case OpenVariant::Open:
1560b57cec5SDimitry Andric     FlagsArgIndex = 1;
1570b57cec5SDimitry Andric     VariantName = "open";
1580b57cec5SDimitry Andric     break;
1590b57cec5SDimitry Andric   case OpenVariant::OpenAt:
1600b57cec5SDimitry Andric     FlagsArgIndex = 2;
1610b57cec5SDimitry Andric     VariantName = "openat";
1620b57cec5SDimitry Andric     break;
1630b57cec5SDimitry Andric   };
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric   // All calls should at least provide arguments up to the 'flags' parameter.
1660b57cec5SDimitry Andric   unsigned int MinArgCount = FlagsArgIndex + 1;
1670b57cec5SDimitry Andric 
1680b57cec5SDimitry Andric   // If the flags has O_CREAT set then open/openat() require an additional
1690b57cec5SDimitry Andric   // argument specifying the file mode (permission bits) for the created file.
1700b57cec5SDimitry Andric   unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
1710b57cec5SDimitry Andric 
1720b57cec5SDimitry Andric   // The create mode argument should be the last argument.
1730b57cec5SDimitry Andric   unsigned int MaxArgCount = CreateModeArgIndex + 1;
1740b57cec5SDimitry Andric 
1750b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
1760b57cec5SDimitry Andric 
1770b57cec5SDimitry Andric   if (CE->getNumArgs() < MinArgCount) {
1785e801ac6SDimitry Andric     // The frontend should issue a warning for this case. Just return.
1790b57cec5SDimitry Andric     return;
1800b57cec5SDimitry Andric   } else if (CE->getNumArgs() == MaxArgCount) {
1810b57cec5SDimitry Andric     const Expr *Arg = CE->getArg(CreateModeArgIndex);
1820b57cec5SDimitry Andric     QualType QT = Arg->getType();
1830b57cec5SDimitry Andric     if (!QT->isIntegerType()) {
1840b57cec5SDimitry Andric       SmallString<256> SBuf;
1850b57cec5SDimitry Andric       llvm::raw_svector_ostream OS(SBuf);
1860b57cec5SDimitry Andric       OS << "The " << CreateModeArgIndex + 1
1870b57cec5SDimitry Andric          << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
1880b57cec5SDimitry Andric          << " argument to '" << VariantName << "' is not an integer";
1890b57cec5SDimitry Andric 
1900b57cec5SDimitry Andric       ReportOpenBug(C, state,
1910b57cec5SDimitry Andric                     SBuf.c_str(),
1920b57cec5SDimitry Andric                     Arg->getSourceRange());
1930b57cec5SDimitry Andric       return;
1940b57cec5SDimitry Andric     }
1950b57cec5SDimitry Andric   } else if (CE->getNumArgs() > MaxArgCount) {
1960b57cec5SDimitry Andric     SmallString<256> SBuf;
1970b57cec5SDimitry Andric     llvm::raw_svector_ostream OS(SBuf);
1980b57cec5SDimitry Andric     OS << "Call to '" << VariantName << "' with more than " << MaxArgCount
1990b57cec5SDimitry Andric        << " arguments";
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric     ReportOpenBug(C, state,
2020b57cec5SDimitry Andric                   SBuf.c_str(),
2030b57cec5SDimitry Andric                   CE->getArg(MaxArgCount)->getSourceRange());
2040b57cec5SDimitry Andric     return;
2050b57cec5SDimitry Andric   }
2060b57cec5SDimitry Andric 
2070b57cec5SDimitry Andric   // The definition of O_CREAT is platform specific.  We need a better way
2080b57cec5SDimitry Andric   // of querying this information from the checking environment.
20981ad6265SDimitry Andric   if (!Val_O_CREAT) {
2100b57cec5SDimitry Andric     if (C.getASTContext().getTargetInfo().getTriple().getVendor()
2110b57cec5SDimitry Andric                                                       == llvm::Triple::Apple)
2120b57cec5SDimitry Andric       Val_O_CREAT = 0x0200;
2130b57cec5SDimitry Andric     else {
2140b57cec5SDimitry Andric       // FIXME: We need a more general way of getting the O_CREAT value.
2150b57cec5SDimitry Andric       // We could possibly grovel through the preprocessor state, but
2160b57cec5SDimitry Andric       // that would require passing the Preprocessor object to the ExprEngine.
2170b57cec5SDimitry Andric       // See also: MallocChecker.cpp / M_ZERO.
2180b57cec5SDimitry Andric       return;
2190b57cec5SDimitry Andric     }
2200b57cec5SDimitry Andric   }
2210b57cec5SDimitry Andric 
2220b57cec5SDimitry Andric   // Now check if oflags has O_CREAT set.
2230b57cec5SDimitry Andric   const Expr *oflagsEx = CE->getArg(FlagsArgIndex);
2240b57cec5SDimitry Andric   const SVal V = C.getSVal(oflagsEx);
22581ad6265SDimitry Andric   if (!isa<NonLoc>(V)) {
2260b57cec5SDimitry Andric     // The case where 'V' can be a location can only be due to a bad header,
2270b57cec5SDimitry Andric     // so in this case bail out.
2280b57cec5SDimitry Andric     return;
2290b57cec5SDimitry Andric   }
2300b57cec5SDimitry Andric   NonLoc oflags = V.castAs<NonLoc>();
2310b57cec5SDimitry Andric   NonLoc ocreateFlag = C.getSValBuilder()
232bdd1243dSDimitry Andric                            .makeIntVal(*Val_O_CREAT, oflagsEx->getType())
233753f127fSDimitry Andric                            .castAs<NonLoc>();
2340b57cec5SDimitry Andric   SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
2350b57cec5SDimitry Andric                                                       oflags, ocreateFlag,
2360b57cec5SDimitry Andric                                                       oflagsEx->getType());
2370b57cec5SDimitry Andric   if (maskedFlagsUC.isUnknownOrUndef())
2380b57cec5SDimitry Andric     return;
2390b57cec5SDimitry Andric   DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
2400b57cec5SDimitry Andric 
2410b57cec5SDimitry Andric   // Check if maskedFlags is non-zero.
2420b57cec5SDimitry Andric   ProgramStateRef trueState, falseState;
2430b57cec5SDimitry Andric   std::tie(trueState, falseState) = state->assume(maskedFlags);
2440b57cec5SDimitry Andric 
2450b57cec5SDimitry Andric   // Only emit an error if the value of 'maskedFlags' is properly
2460b57cec5SDimitry Andric   // constrained;
2470b57cec5SDimitry Andric   if (!(trueState && !falseState))
2480b57cec5SDimitry Andric     return;
2490b57cec5SDimitry Andric 
2500b57cec5SDimitry Andric   if (CE->getNumArgs() < MaxArgCount) {
2510b57cec5SDimitry Andric     SmallString<256> SBuf;
2520b57cec5SDimitry Andric     llvm::raw_svector_ostream OS(SBuf);
2530b57cec5SDimitry Andric     OS << "Call to '" << VariantName << "' requires a "
2540b57cec5SDimitry Andric        << CreateModeArgIndex + 1
2550b57cec5SDimitry Andric        << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
2560b57cec5SDimitry Andric        << " argument when the 'O_CREAT' flag is set";
2570b57cec5SDimitry Andric     ReportOpenBug(C, trueState,
2580b57cec5SDimitry Andric                   SBuf.c_str(),
2590b57cec5SDimitry Andric                   oflagsEx->getSourceRange());
2600b57cec5SDimitry Andric   }
2610b57cec5SDimitry Andric }
2620b57cec5SDimitry Andric 
2630b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
2640b57cec5SDimitry Andric // pthread_once
2650b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
2660b57cec5SDimitry Andric 
2670b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
2680b57cec5SDimitry Andric                                       const CallExpr *CE) const {
2690b57cec5SDimitry Andric 
2700b57cec5SDimitry Andric   // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
2710b57cec5SDimitry Andric   // They can possibly be refactored.
2720b57cec5SDimitry Andric 
2730b57cec5SDimitry Andric   if (CE->getNumArgs() < 1)
2740b57cec5SDimitry Andric     return;
2750b57cec5SDimitry Andric 
2760b57cec5SDimitry Andric   // Check if the first argument is stack allocated.  If so, issue a warning
2770b57cec5SDimitry Andric   // because that's likely to be bad news.
2780b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
2790b57cec5SDimitry Andric   const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
2800b57cec5SDimitry Andric   if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
2810b57cec5SDimitry Andric     return;
2820b57cec5SDimitry Andric 
2830b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode(state);
2840b57cec5SDimitry Andric   if (!N)
2850b57cec5SDimitry Andric     return;
2860b57cec5SDimitry Andric 
2870b57cec5SDimitry Andric   SmallString<256> S;
2880b57cec5SDimitry Andric   llvm::raw_svector_ostream os(S);
2890b57cec5SDimitry Andric   os << "Call to 'pthread_once' uses";
2900b57cec5SDimitry Andric   if (const VarRegion *VR = dyn_cast<VarRegion>(R))
2910b57cec5SDimitry Andric     os << " the local variable '" << VR->getDecl()->getName() << '\'';
2920b57cec5SDimitry Andric   else
2930b57cec5SDimitry Andric     os << " stack allocated memory";
2940b57cec5SDimitry Andric   os << " for the \"control\" value.  Using such transient memory for "
2950b57cec5SDimitry Andric   "the control value is potentially dangerous.";
2960b57cec5SDimitry Andric   if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
2970b57cec5SDimitry Andric     os << "  Perhaps you intended to declare the variable as 'static'?";
2980b57cec5SDimitry Andric 
299a7dea167SDimitry Andric   auto report =
300*647cbc5dSDimitry Andric       std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N);
3010b57cec5SDimitry Andric   report->addRange(CE->getArg(0)->getSourceRange());
3020b57cec5SDimitry Andric   C.emitReport(std::move(report));
3030b57cec5SDimitry Andric }
3040b57cec5SDimitry Andric 
3050b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3060b57cec5SDimitry Andric // "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
3070b57cec5SDimitry Andric // with allocation size 0
3080b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
3090b57cec5SDimitry Andric 
3100b57cec5SDimitry Andric // FIXME: Eventually these should be rolled into the MallocChecker, but right now
3110b57cec5SDimitry Andric // they're more basic and valuable for widespread use.
3120b57cec5SDimitry Andric 
3130b57cec5SDimitry Andric // Returns true if we try to do a zero byte allocation, false otherwise.
3140b57cec5SDimitry Andric // Fills in trueState and falseState.
3150b57cec5SDimitry Andric static bool IsZeroByteAllocation(ProgramStateRef state,
3160b57cec5SDimitry Andric                                  const SVal argVal,
3170b57cec5SDimitry Andric                                  ProgramStateRef *trueState,
3180b57cec5SDimitry Andric                                  ProgramStateRef *falseState) {
3190b57cec5SDimitry Andric   std::tie(*trueState, *falseState) =
3200b57cec5SDimitry Andric     state->assume(argVal.castAs<DefinedSVal>());
3210b57cec5SDimitry Andric 
3220b57cec5SDimitry Andric   return (*falseState && !*trueState);
3230b57cec5SDimitry Andric }
3240b57cec5SDimitry Andric 
3250b57cec5SDimitry Andric // Generates an error report, indicating that the function whose name is given
3260b57cec5SDimitry Andric // will perform a zero byte allocation.
3270b57cec5SDimitry Andric // Returns false if an error occurred, true otherwise.
3280b57cec5SDimitry Andric bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
3290b57cec5SDimitry Andric                                                     CheckerContext &C,
3300b57cec5SDimitry Andric                                                     ProgramStateRef falseState,
3310b57cec5SDimitry Andric                                                     const Expr *arg,
3320b57cec5SDimitry Andric                                                     const char *fn_name) const {
3330b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode(falseState);
3340b57cec5SDimitry Andric   if (!N)
3350b57cec5SDimitry Andric     return false;
3360b57cec5SDimitry Andric 
3370b57cec5SDimitry Andric   SmallString<256> S;
3380b57cec5SDimitry Andric   llvm::raw_svector_ostream os(S);
3390b57cec5SDimitry Andric   os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
340a7dea167SDimitry Andric   auto report =
341*647cbc5dSDimitry Andric       std::make_unique<PathSensitiveBugReport>(BT_mallocZero, os.str(), N);
3420b57cec5SDimitry Andric 
3430b57cec5SDimitry Andric   report->addRange(arg->getSourceRange());
3440b57cec5SDimitry Andric   bugreporter::trackExpressionValue(N, arg, *report);
3450b57cec5SDimitry Andric   C.emitReport(std::move(report));
3460b57cec5SDimitry Andric 
3470b57cec5SDimitry Andric   return true;
3480b57cec5SDimitry Andric }
3490b57cec5SDimitry Andric 
3500b57cec5SDimitry Andric // Does a basic check for 0-sized allocations suitable for most of the below
3510b57cec5SDimitry Andric // functions (modulo "calloc")
3520b57cec5SDimitry Andric void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,
3530b57cec5SDimitry Andric                                                      const CallExpr *CE,
3540b57cec5SDimitry Andric                                                      const unsigned numArgs,
3550b57cec5SDimitry Andric                                                      const unsigned sizeArg,
3560b57cec5SDimitry Andric                                                      const char *fn) const {
3575e801ac6SDimitry Andric   // Check for the correct number of arguments.
3580b57cec5SDimitry Andric   if (CE->getNumArgs() != numArgs)
3590b57cec5SDimitry Andric     return;
3600b57cec5SDimitry Andric 
3610b57cec5SDimitry Andric   // Check if the allocation size is 0.
3620b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
3630b57cec5SDimitry Andric   ProgramStateRef trueState = nullptr, falseState = nullptr;
3640b57cec5SDimitry Andric   const Expr *arg = CE->getArg(sizeArg);
3650b57cec5SDimitry Andric   SVal argVal = C.getSVal(arg);
3660b57cec5SDimitry Andric 
3670b57cec5SDimitry Andric   if (argVal.isUnknownOrUndef())
3680b57cec5SDimitry Andric     return;
3690b57cec5SDimitry Andric 
3700b57cec5SDimitry Andric   // Is the value perfectly constrained to zero?
3710b57cec5SDimitry Andric   if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
3720b57cec5SDimitry Andric     (void) ReportZeroByteAllocation(C, falseState, arg, fn);
3730b57cec5SDimitry Andric     return;
3740b57cec5SDimitry Andric   }
3750b57cec5SDimitry Andric   // Assume the value is non-zero going forward.
3760b57cec5SDimitry Andric   assert(trueState);
3770b57cec5SDimitry Andric   if (trueState != state)
3780b57cec5SDimitry Andric     C.addTransition(trueState);
3790b57cec5SDimitry Andric }
3800b57cec5SDimitry Andric 
3810b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,
3820b57cec5SDimitry Andric                                                 const CallExpr *CE) const {
3830b57cec5SDimitry Andric   unsigned int nArgs = CE->getNumArgs();
3840b57cec5SDimitry Andric   if (nArgs != 2)
3850b57cec5SDimitry Andric     return;
3860b57cec5SDimitry Andric 
3870b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
3880b57cec5SDimitry Andric   ProgramStateRef trueState = nullptr, falseState = nullptr;
3890b57cec5SDimitry Andric 
3900b57cec5SDimitry Andric   unsigned int i;
3910b57cec5SDimitry Andric   for (i = 0; i < nArgs; i++) {
3920b57cec5SDimitry Andric     const Expr *arg = CE->getArg(i);
3930b57cec5SDimitry Andric     SVal argVal = C.getSVal(arg);
3940b57cec5SDimitry Andric     if (argVal.isUnknownOrUndef()) {
3950b57cec5SDimitry Andric       if (i == 0)
3960b57cec5SDimitry Andric         continue;
3970b57cec5SDimitry Andric       else
3980b57cec5SDimitry Andric         return;
3990b57cec5SDimitry Andric     }
4000b57cec5SDimitry Andric 
4010b57cec5SDimitry Andric     if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
4020b57cec5SDimitry Andric       if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
4030b57cec5SDimitry Andric         return;
4040b57cec5SDimitry Andric       else if (i == 0)
4050b57cec5SDimitry Andric         continue;
4060b57cec5SDimitry Andric       else
4070b57cec5SDimitry Andric         return;
4080b57cec5SDimitry Andric     }
4090b57cec5SDimitry Andric   }
4100b57cec5SDimitry Andric 
4110b57cec5SDimitry Andric   // Assume the value is non-zero going forward.
4120b57cec5SDimitry Andric   assert(trueState);
4130b57cec5SDimitry Andric   if (trueState != state)
4140b57cec5SDimitry Andric     C.addTransition(trueState);
4150b57cec5SDimitry Andric }
4160b57cec5SDimitry Andric 
4170b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,
4180b57cec5SDimitry Andric                                                 const CallExpr *CE) const {
4190b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 1, 0, "malloc");
4200b57cec5SDimitry Andric }
4210b57cec5SDimitry Andric 
4220b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,
4230b57cec5SDimitry Andric                                                  const CallExpr *CE) const {
4240b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 2, 1, "realloc");
4250b57cec5SDimitry Andric }
4260b57cec5SDimitry Andric 
4270b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,
4280b57cec5SDimitry Andric                                                   const CallExpr *CE) const {
4290b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 2, 1, "reallocf");
4300b57cec5SDimitry Andric }
4310b57cec5SDimitry Andric 
4320b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,
4330b57cec5SDimitry Andric                                                 const CallExpr *CE) const {
4340b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 1, 0, "alloca");
4350b57cec5SDimitry Andric }
4360b57cec5SDimitry Andric 
4370b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
4380b57cec5SDimitry Andric                                                      CheckerContext &C,
4390b57cec5SDimitry Andric                                                      const CallExpr *CE) const {
4400b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");
4410b57cec5SDimitry Andric }
4420b57cec5SDimitry Andric 
4430b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,
4440b57cec5SDimitry Andric                                                 const CallExpr *CE) const {
4450b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 1, 0, "valloc");
4460b57cec5SDimitry Andric }
4470b57cec5SDimitry Andric 
4480b57cec5SDimitry Andric void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
4490b57cec5SDimitry Andric                                              CheckerContext &C) const {
4500b57cec5SDimitry Andric   const FunctionDecl *FD = C.getCalleeDecl(CE);
4510b57cec5SDimitry Andric   if (!FD || FD->getKind() != Decl::Function)
4520b57cec5SDimitry Andric     return;
4530b57cec5SDimitry Andric 
4540b57cec5SDimitry Andric   // Don't treat functions in namespaces with the same name a Unix function
4550b57cec5SDimitry Andric   // as a call to the Unix function.
4560b57cec5SDimitry Andric   const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
457349cc55cSDimitry Andric   if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
4580b57cec5SDimitry Andric     return;
4590b57cec5SDimitry Andric 
4600b57cec5SDimitry Andric   StringRef FName = C.getCalleeName(FD);
4610b57cec5SDimitry Andric   if (FName.empty())
4620b57cec5SDimitry Andric     return;
4630b57cec5SDimitry Andric 
4640b57cec5SDimitry Andric   if (FName == "calloc")
4650b57cec5SDimitry Andric     CheckCallocZero(C, CE);
4660b57cec5SDimitry Andric 
4670b57cec5SDimitry Andric   else if (FName == "malloc")
4680b57cec5SDimitry Andric     CheckMallocZero(C, CE);
4690b57cec5SDimitry Andric 
4700b57cec5SDimitry Andric   else if (FName == "realloc")
4710b57cec5SDimitry Andric     CheckReallocZero(C, CE);
4720b57cec5SDimitry Andric 
4730b57cec5SDimitry Andric   else if (FName == "reallocf")
4740b57cec5SDimitry Andric     CheckReallocfZero(C, CE);
4750b57cec5SDimitry Andric 
4760b57cec5SDimitry Andric   else if (FName == "alloca" || FName ==  "__builtin_alloca")
4770b57cec5SDimitry Andric     CheckAllocaZero(C, CE);
4780b57cec5SDimitry Andric 
4790b57cec5SDimitry Andric   else if (FName == "__builtin_alloca_with_align")
4800b57cec5SDimitry Andric     CheckAllocaWithAlignZero(C, CE);
4810b57cec5SDimitry Andric 
4820b57cec5SDimitry Andric   else if (FName == "valloc")
4830b57cec5SDimitry Andric     CheckVallocZero(C, CE);
4840b57cec5SDimitry Andric }
4850b57cec5SDimitry Andric 
4860b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4870b57cec5SDimitry Andric // Registration.
4880b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4890b57cec5SDimitry Andric 
4900b57cec5SDimitry Andric #define REGISTER_CHECKER(CHECKERNAME)                                          \
4910b57cec5SDimitry Andric   void ento::register##CHECKERNAME(CheckerManager &mgr) {                      \
4920b57cec5SDimitry Andric     mgr.registerChecker<CHECKERNAME>();                                        \
4930b57cec5SDimitry Andric   }                                                                            \
4940b57cec5SDimitry Andric                                                                                \
4955ffd83dbSDimitry Andric   bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) {          \
4960b57cec5SDimitry Andric     return true;                                                               \
4970b57cec5SDimitry Andric   }
4980b57cec5SDimitry Andric 
4990b57cec5SDimitry Andric REGISTER_CHECKER(UnixAPIMisuseChecker)
5000b57cec5SDimitry Andric REGISTER_CHECKER(UnixAPIPortabilityChecker)
501