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