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