xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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"
15647cbc5dSDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
160b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17647cbc5dSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
180b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
190b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
20*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
210b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
22*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
23*0fca6ea1SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
240b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h"
250b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
265ffd83dbSDimitry Andric #include "llvm/ADT/StringExtras.h"
270b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
28bdd1243dSDimitry Andric #include <optional>
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric using namespace clang;
310b57cec5SDimitry Andric using namespace ento;
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric enum class OpenVariant {
340b57cec5SDimitry Andric   /// The standard open() call:
350b57cec5SDimitry Andric   ///    int open(const char *path, int oflag, ...);
360b57cec5SDimitry Andric   Open,
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric   /// The variant taking a directory file descriptor and a relative path:
390b57cec5SDimitry Andric   ///    int openat(int fd, const char *path, int oflag, ...);
400b57cec5SDimitry Andric   OpenAt
410b57cec5SDimitry Andric };
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric namespace {
440b57cec5SDimitry Andric 
45*0fca6ea1SDimitry Andric class UnixAPIMisuseChecker
46*0fca6ea1SDimitry Andric     : public Checker<check::PreCall, check::ASTDecl<TranslationUnitDecl>> {
47647cbc5dSDimitry Andric   const BugType BT_open{this, "Improper use of 'open'", categories::UnixAPI};
48*0fca6ea1SDimitry Andric   const BugType BT_getline{this, "Improper use of getdelim",
49*0fca6ea1SDimitry Andric                            categories::UnixAPI};
50647cbc5dSDimitry Andric   const BugType BT_pthreadOnce{this, "Improper use of 'pthread_once'",
51647cbc5dSDimitry Andric                                categories::UnixAPI};
52*0fca6ea1SDimitry Andric   const BugType BT_ArgumentNull{this, "NULL pointer", categories::UnixAPI};
53bdd1243dSDimitry Andric   mutable std::optional<uint64_t> Val_O_CREAT;
540b57cec5SDimitry Andric 
55*0fca6ea1SDimitry Andric   ProgramStateRef
56*0fca6ea1SDimitry Andric   EnsurePtrNotNull(SVal PtrVal, const Expr *PtrExpr, CheckerContext &C,
57*0fca6ea1SDimitry Andric                    ProgramStateRef State, const StringRef PtrDescr,
58*0fca6ea1SDimitry Andric                    std::optional<std::reference_wrapper<const BugType>> BT =
59*0fca6ea1SDimitry Andric                        std::nullopt) const;
60*0fca6ea1SDimitry Andric 
61*0fca6ea1SDimitry Andric   ProgramStateRef EnsureGetdelimBufferAndSizeCorrect(
62*0fca6ea1SDimitry Andric       SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
63*0fca6ea1SDimitry Andric       const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const;
64*0fca6ea1SDimitry Andric 
650b57cec5SDimitry Andric public:
66*0fca6ea1SDimitry Andric   void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager &Mgr,
67*0fca6ea1SDimitry Andric                     BugReporter &BR) const;
680b57cec5SDimitry Andric 
69*0fca6ea1SDimitry Andric   void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
700b57cec5SDimitry Andric 
71*0fca6ea1SDimitry Andric   void CheckOpen(CheckerContext &C, const CallEvent &Call) const;
72*0fca6ea1SDimitry Andric   void CheckOpenAt(CheckerContext &C, const CallEvent &Call) const;
73*0fca6ea1SDimitry Andric   void CheckGetDelim(CheckerContext &C, const CallEvent &Call) const;
74*0fca6ea1SDimitry Andric   void CheckPthreadOnce(CheckerContext &C, const CallEvent &Call) const;
750b57cec5SDimitry Andric 
76*0fca6ea1SDimitry Andric   void CheckOpenVariant(CheckerContext &C, const CallEvent &Call,
77*0fca6ea1SDimitry Andric                         OpenVariant Variant) const;
78*0fca6ea1SDimitry Andric 
79*0fca6ea1SDimitry Andric   void ReportOpenBug(CheckerContext &C, ProgramStateRef State, const char *Msg,
800b57cec5SDimitry Andric                      SourceRange SR) const;
810b57cec5SDimitry Andric };
820b57cec5SDimitry Andric 
830b57cec5SDimitry Andric class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
840b57cec5SDimitry Andric public:
850b57cec5SDimitry Andric   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
860b57cec5SDimitry Andric 
870b57cec5SDimitry Andric private:
88647cbc5dSDimitry Andric   const BugType BT_mallocZero{
89647cbc5dSDimitry Andric       this, "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)",
90647cbc5dSDimitry Andric       categories::UnixAPI};
910b57cec5SDimitry Andric 
920b57cec5SDimitry Andric   void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
930b57cec5SDimitry Andric   void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
940b57cec5SDimitry Andric   void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
950b57cec5SDimitry Andric   void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const;
960b57cec5SDimitry Andric   void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
970b57cec5SDimitry Andric   void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const;
980b57cec5SDimitry Andric   void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
990b57cec5SDimitry Andric 
1000b57cec5SDimitry Andric   bool ReportZeroByteAllocation(CheckerContext &C,
1010b57cec5SDimitry Andric                                 ProgramStateRef falseState,
1020b57cec5SDimitry Andric                                 const Expr *arg,
1030b57cec5SDimitry Andric                                 const char *fn_name) const;
1040b57cec5SDimitry Andric   void BasicAllocationCheck(CheckerContext &C,
1050b57cec5SDimitry Andric                             const CallExpr *CE,
1060b57cec5SDimitry Andric                             const unsigned numArgs,
1070b57cec5SDimitry Andric                             const unsigned sizeArg,
1080b57cec5SDimitry Andric                             const char *fn) const;
1090b57cec5SDimitry Andric };
1100b57cec5SDimitry Andric 
1110b57cec5SDimitry Andric } // end anonymous namespace
1120b57cec5SDimitry Andric 
113*0fca6ea1SDimitry Andric ProgramStateRef UnixAPIMisuseChecker::EnsurePtrNotNull(
114*0fca6ea1SDimitry Andric     SVal PtrVal, const Expr *PtrExpr, CheckerContext &C, ProgramStateRef State,
115*0fca6ea1SDimitry Andric     const StringRef PtrDescr,
116*0fca6ea1SDimitry Andric     std::optional<std::reference_wrapper<const BugType>> BT) const {
117*0fca6ea1SDimitry Andric   const auto Ptr = PtrVal.getAs<DefinedSVal>();
118*0fca6ea1SDimitry Andric   if (!Ptr)
119*0fca6ea1SDimitry Andric     return State;
120*0fca6ea1SDimitry Andric 
121*0fca6ea1SDimitry Andric   const auto [PtrNotNull, PtrNull] = State->assume(*Ptr);
122*0fca6ea1SDimitry Andric   if (!PtrNotNull && PtrNull) {
123*0fca6ea1SDimitry Andric     if (ExplodedNode *N = C.generateErrorNode(PtrNull)) {
124*0fca6ea1SDimitry Andric       auto R = std::make_unique<PathSensitiveBugReport>(
125*0fca6ea1SDimitry Andric           BT.value_or(std::cref(BT_ArgumentNull)),
126*0fca6ea1SDimitry Andric           (PtrDescr + " pointer might be NULL.").str(), N);
127*0fca6ea1SDimitry Andric       if (PtrExpr)
128*0fca6ea1SDimitry Andric         bugreporter::trackExpressionValue(N, PtrExpr, *R);
129*0fca6ea1SDimitry Andric       C.emitReport(std::move(R));
130*0fca6ea1SDimitry Andric     }
131*0fca6ea1SDimitry Andric     return nullptr;
132*0fca6ea1SDimitry Andric   }
133*0fca6ea1SDimitry Andric 
134*0fca6ea1SDimitry Andric   return PtrNotNull;
135*0fca6ea1SDimitry Andric }
136*0fca6ea1SDimitry Andric 
137*0fca6ea1SDimitry Andric void UnixAPIMisuseChecker::checkASTDecl(const TranslationUnitDecl *TU,
138*0fca6ea1SDimitry Andric                                         AnalysisManager &Mgr,
139*0fca6ea1SDimitry Andric                                         BugReporter &) const {
140*0fca6ea1SDimitry Andric   // The definition of O_CREAT is platform specific.
141*0fca6ea1SDimitry Andric   // Try to get the macro value from the preprocessor.
142*0fca6ea1SDimitry Andric   Val_O_CREAT = tryExpandAsInteger("O_CREAT", Mgr.getPreprocessor());
143*0fca6ea1SDimitry Andric   // If we failed, fall-back to known values.
144*0fca6ea1SDimitry Andric   if (!Val_O_CREAT) {
145*0fca6ea1SDimitry Andric     if (TU->getASTContext().getTargetInfo().getTriple().getVendor() ==
146*0fca6ea1SDimitry Andric         llvm::Triple::Apple)
147*0fca6ea1SDimitry Andric       Val_O_CREAT = 0x0200;
148*0fca6ea1SDimitry Andric   }
149*0fca6ea1SDimitry Andric }
150*0fca6ea1SDimitry Andric 
1510b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1520b57cec5SDimitry Andric // "open" (man 2 open)
1530b57cec5SDimitry Andric //===----------------------------------------------------------------------===/
1540b57cec5SDimitry Andric 
155*0fca6ea1SDimitry Andric void UnixAPIMisuseChecker::checkPreCall(const CallEvent &Call,
1560b57cec5SDimitry Andric                                         CheckerContext &C) const {
157*0fca6ea1SDimitry Andric   const FunctionDecl *FD = dyn_cast_if_present<FunctionDecl>(Call.getDecl());
1580b57cec5SDimitry Andric   if (!FD || FD->getKind() != Decl::Function)
1590b57cec5SDimitry Andric     return;
1600b57cec5SDimitry Andric 
1610b57cec5SDimitry Andric   // Don't treat functions in namespaces with the same name a Unix function
1620b57cec5SDimitry Andric   // as a call to the Unix function.
1630b57cec5SDimitry Andric   const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
164349cc55cSDimitry Andric   if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
1650b57cec5SDimitry Andric     return;
1660b57cec5SDimitry Andric 
1670b57cec5SDimitry Andric   StringRef FName = C.getCalleeName(FD);
1680b57cec5SDimitry Andric   if (FName.empty())
1690b57cec5SDimitry Andric     return;
1700b57cec5SDimitry Andric 
1710b57cec5SDimitry Andric   if (FName == "open")
172*0fca6ea1SDimitry Andric     CheckOpen(C, Call);
1730b57cec5SDimitry Andric 
1740b57cec5SDimitry Andric   else if (FName == "openat")
175*0fca6ea1SDimitry Andric     CheckOpenAt(C, Call);
1760b57cec5SDimitry Andric 
1770b57cec5SDimitry Andric   else if (FName == "pthread_once")
178*0fca6ea1SDimitry Andric     CheckPthreadOnce(C, Call);
179*0fca6ea1SDimitry Andric 
180*0fca6ea1SDimitry Andric   else if (is_contained({"getdelim", "getline"}, FName))
181*0fca6ea1SDimitry Andric     CheckGetDelim(C, Call);
1820b57cec5SDimitry Andric }
1830b57cec5SDimitry Andric void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
1840b57cec5SDimitry Andric                                          ProgramStateRef State,
1850b57cec5SDimitry Andric                                          const char *Msg,
1860b57cec5SDimitry Andric                                          SourceRange SR) const {
1870b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode(State);
1880b57cec5SDimitry Andric   if (!N)
1890b57cec5SDimitry Andric     return;
1900b57cec5SDimitry Andric 
191647cbc5dSDimitry Andric   auto Report = std::make_unique<PathSensitiveBugReport>(BT_open, Msg, N);
1920b57cec5SDimitry Andric   Report->addRange(SR);
1930b57cec5SDimitry Andric   C.emitReport(std::move(Report));
1940b57cec5SDimitry Andric }
1950b57cec5SDimitry Andric 
1960b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,
197*0fca6ea1SDimitry Andric                                      const CallEvent &Call) const {
198*0fca6ea1SDimitry Andric   CheckOpenVariant(C, Call, OpenVariant::Open);
1990b57cec5SDimitry Andric }
2000b57cec5SDimitry Andric 
2010b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,
202*0fca6ea1SDimitry Andric                                        const CallEvent &Call) const {
203*0fca6ea1SDimitry Andric   CheckOpenVariant(C, Call, OpenVariant::OpenAt);
2040b57cec5SDimitry Andric }
2050b57cec5SDimitry Andric 
2060b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
207*0fca6ea1SDimitry Andric                                             const CallEvent &Call,
2080b57cec5SDimitry Andric                                             OpenVariant Variant) const {
2090b57cec5SDimitry Andric   // The index of the argument taking the flags open flags (O_RDONLY,
2100b57cec5SDimitry Andric   // O_WRONLY, O_CREAT, etc.),
2110b57cec5SDimitry Andric   unsigned int FlagsArgIndex;
2120b57cec5SDimitry Andric   const char *VariantName;
2130b57cec5SDimitry Andric   switch (Variant) {
2140b57cec5SDimitry Andric   case OpenVariant::Open:
2150b57cec5SDimitry Andric     FlagsArgIndex = 1;
2160b57cec5SDimitry Andric     VariantName = "open";
2170b57cec5SDimitry Andric     break;
2180b57cec5SDimitry Andric   case OpenVariant::OpenAt:
2190b57cec5SDimitry Andric     FlagsArgIndex = 2;
2200b57cec5SDimitry Andric     VariantName = "openat";
2210b57cec5SDimitry Andric     break;
2220b57cec5SDimitry Andric   };
2230b57cec5SDimitry Andric 
2240b57cec5SDimitry Andric   // All calls should at least provide arguments up to the 'flags' parameter.
2250b57cec5SDimitry Andric   unsigned int MinArgCount = FlagsArgIndex + 1;
2260b57cec5SDimitry Andric 
2270b57cec5SDimitry Andric   // If the flags has O_CREAT set then open/openat() require an additional
2280b57cec5SDimitry Andric   // argument specifying the file mode (permission bits) for the created file.
2290b57cec5SDimitry Andric   unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
2300b57cec5SDimitry Andric 
2310b57cec5SDimitry Andric   // The create mode argument should be the last argument.
2320b57cec5SDimitry Andric   unsigned int MaxArgCount = CreateModeArgIndex + 1;
2330b57cec5SDimitry Andric 
2340b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
2350b57cec5SDimitry Andric 
236*0fca6ea1SDimitry Andric   if (Call.getNumArgs() < MinArgCount) {
2375e801ac6SDimitry Andric     // The frontend should issue a warning for this case. Just return.
2380b57cec5SDimitry Andric     return;
239*0fca6ea1SDimitry Andric   } else if (Call.getNumArgs() == MaxArgCount) {
240*0fca6ea1SDimitry Andric     const Expr *Arg = Call.getArgExpr(CreateModeArgIndex);
2410b57cec5SDimitry Andric     QualType QT = Arg->getType();
2420b57cec5SDimitry Andric     if (!QT->isIntegerType()) {
2430b57cec5SDimitry Andric       SmallString<256> SBuf;
2440b57cec5SDimitry Andric       llvm::raw_svector_ostream OS(SBuf);
2450b57cec5SDimitry Andric       OS << "The " << CreateModeArgIndex + 1
2460b57cec5SDimitry Andric          << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
2470b57cec5SDimitry Andric          << " argument to '" << VariantName << "' is not an integer";
2480b57cec5SDimitry Andric 
2490b57cec5SDimitry Andric       ReportOpenBug(C, state,
2500b57cec5SDimitry Andric                     SBuf.c_str(),
2510b57cec5SDimitry Andric                     Arg->getSourceRange());
2520b57cec5SDimitry Andric       return;
2530b57cec5SDimitry Andric     }
254*0fca6ea1SDimitry Andric   } else if (Call.getNumArgs() > MaxArgCount) {
2550b57cec5SDimitry Andric     SmallString<256> SBuf;
2560b57cec5SDimitry Andric     llvm::raw_svector_ostream OS(SBuf);
2570b57cec5SDimitry Andric     OS << "Call to '" << VariantName << "' with more than " << MaxArgCount
2580b57cec5SDimitry Andric        << " arguments";
2590b57cec5SDimitry Andric 
260*0fca6ea1SDimitry Andric     ReportOpenBug(C, state, SBuf.c_str(),
261*0fca6ea1SDimitry Andric                   Call.getArgExpr(MaxArgCount)->getSourceRange());
2620b57cec5SDimitry Andric     return;
2630b57cec5SDimitry Andric   }
2640b57cec5SDimitry Andric 
26581ad6265SDimitry Andric   if (!Val_O_CREAT) {
2660b57cec5SDimitry Andric     return;
2670b57cec5SDimitry Andric   }
2680b57cec5SDimitry Andric 
2690b57cec5SDimitry Andric   // Now check if oflags has O_CREAT set.
270*0fca6ea1SDimitry Andric   const Expr *oflagsEx = Call.getArgExpr(FlagsArgIndex);
271*0fca6ea1SDimitry Andric   const SVal V = Call.getArgSVal(FlagsArgIndex);
27281ad6265SDimitry Andric   if (!isa<NonLoc>(V)) {
2730b57cec5SDimitry Andric     // The case where 'V' can be a location can only be due to a bad header,
2740b57cec5SDimitry Andric     // so in this case bail out.
2750b57cec5SDimitry Andric     return;
2760b57cec5SDimitry Andric   }
2770b57cec5SDimitry Andric   NonLoc oflags = V.castAs<NonLoc>();
2780b57cec5SDimitry Andric   NonLoc ocreateFlag = C.getSValBuilder()
279bdd1243dSDimitry Andric                            .makeIntVal(*Val_O_CREAT, oflagsEx->getType())
280753f127fSDimitry Andric                            .castAs<NonLoc>();
2810b57cec5SDimitry Andric   SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
2820b57cec5SDimitry Andric                                                       oflags, ocreateFlag,
2830b57cec5SDimitry Andric                                                       oflagsEx->getType());
2840b57cec5SDimitry Andric   if (maskedFlagsUC.isUnknownOrUndef())
2850b57cec5SDimitry Andric     return;
2860b57cec5SDimitry Andric   DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
2870b57cec5SDimitry Andric 
2880b57cec5SDimitry Andric   // Check if maskedFlags is non-zero.
2890b57cec5SDimitry Andric   ProgramStateRef trueState, falseState;
2900b57cec5SDimitry Andric   std::tie(trueState, falseState) = state->assume(maskedFlags);
2910b57cec5SDimitry Andric 
2920b57cec5SDimitry Andric   // Only emit an error if the value of 'maskedFlags' is properly
2930b57cec5SDimitry Andric   // constrained;
2940b57cec5SDimitry Andric   if (!(trueState && !falseState))
2950b57cec5SDimitry Andric     return;
2960b57cec5SDimitry Andric 
297*0fca6ea1SDimitry Andric   if (Call.getNumArgs() < MaxArgCount) {
2980b57cec5SDimitry Andric     SmallString<256> SBuf;
2990b57cec5SDimitry Andric     llvm::raw_svector_ostream OS(SBuf);
3000b57cec5SDimitry Andric     OS << "Call to '" << VariantName << "' requires a "
3010b57cec5SDimitry Andric        << CreateModeArgIndex + 1
3020b57cec5SDimitry Andric        << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
3030b57cec5SDimitry Andric        << " argument when the 'O_CREAT' flag is set";
3040b57cec5SDimitry Andric     ReportOpenBug(C, trueState,
3050b57cec5SDimitry Andric                   SBuf.c_str(),
3060b57cec5SDimitry Andric                   oflagsEx->getSourceRange());
3070b57cec5SDimitry Andric   }
3080b57cec5SDimitry Andric }
3090b57cec5SDimitry Andric 
3100b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
311*0fca6ea1SDimitry Andric // getdelim and getline
312*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
313*0fca6ea1SDimitry Andric 
314*0fca6ea1SDimitry Andric ProgramStateRef UnixAPIMisuseChecker::EnsureGetdelimBufferAndSizeCorrect(
315*0fca6ea1SDimitry Andric     SVal LinePtrPtrSVal, SVal SizePtrSVal, const Expr *LinePtrPtrExpr,
316*0fca6ea1SDimitry Andric     const Expr *SizePtrExpr, CheckerContext &C, ProgramStateRef State) const {
317*0fca6ea1SDimitry Andric   static constexpr llvm::StringLiteral SizeGreaterThanBufferSize =
318*0fca6ea1SDimitry Andric       "The buffer from the first argument is smaller than the size "
319*0fca6ea1SDimitry Andric       "specified by the second parameter";
320*0fca6ea1SDimitry Andric   static constexpr llvm::StringLiteral SizeUndef =
321*0fca6ea1SDimitry Andric       "The buffer from the first argument is not NULL, but the size specified "
322*0fca6ea1SDimitry Andric       "by the second parameter is undefined.";
323*0fca6ea1SDimitry Andric 
324*0fca6ea1SDimitry Andric   auto EmitBugReport = [this, &C, SizePtrExpr, LinePtrPtrExpr](
325*0fca6ea1SDimitry Andric                            ProgramStateRef BugState, StringRef ErrMsg) {
326*0fca6ea1SDimitry Andric     if (ExplodedNode *N = C.generateErrorNode(BugState)) {
327*0fca6ea1SDimitry Andric       auto R = std::make_unique<PathSensitiveBugReport>(BT_getline, ErrMsg, N);
328*0fca6ea1SDimitry Andric       bugreporter::trackExpressionValue(N, SizePtrExpr, *R);
329*0fca6ea1SDimitry Andric       bugreporter::trackExpressionValue(N, LinePtrPtrExpr, *R);
330*0fca6ea1SDimitry Andric       C.emitReport(std::move(R));
331*0fca6ea1SDimitry Andric     }
332*0fca6ea1SDimitry Andric   };
333*0fca6ea1SDimitry Andric 
334*0fca6ea1SDimitry Andric   // We have a pointer to a pointer to the buffer, and a pointer to the size.
335*0fca6ea1SDimitry Andric   // We want what they point at.
336*0fca6ea1SDimitry Andric   auto LinePtrSVal = getPointeeVal(LinePtrPtrSVal, State)->getAs<DefinedSVal>();
337*0fca6ea1SDimitry Andric   auto NSVal = getPointeeVal(SizePtrSVal, State);
338*0fca6ea1SDimitry Andric   if (!LinePtrSVal || !NSVal || NSVal->isUnknown())
339*0fca6ea1SDimitry Andric     return nullptr;
340*0fca6ea1SDimitry Andric 
341*0fca6ea1SDimitry Andric   assert(LinePtrPtrExpr && SizePtrExpr);
342*0fca6ea1SDimitry Andric 
343*0fca6ea1SDimitry Andric   const auto [LinePtrNotNull, LinePtrNull] = State->assume(*LinePtrSVal);
344*0fca6ea1SDimitry Andric   if (LinePtrNotNull && !LinePtrNull) {
345*0fca6ea1SDimitry Andric     // If `*lineptr` is not null, but `*n` is undefined, there is UB.
346*0fca6ea1SDimitry Andric     if (NSVal->isUndef()) {
347*0fca6ea1SDimitry Andric       EmitBugReport(LinePtrNotNull, SizeUndef);
348*0fca6ea1SDimitry Andric       return nullptr;
349*0fca6ea1SDimitry Andric     }
350*0fca6ea1SDimitry Andric 
351*0fca6ea1SDimitry Andric     // If it is defined, and known, its size must be less than or equal to
352*0fca6ea1SDimitry Andric     // the buffer size.
353*0fca6ea1SDimitry Andric     auto NDefSVal = NSVal->getAs<DefinedSVal>();
354*0fca6ea1SDimitry Andric     auto &SVB = C.getSValBuilder();
355*0fca6ea1SDimitry Andric     auto LineBufSize =
356*0fca6ea1SDimitry Andric         getDynamicExtent(LinePtrNotNull, LinePtrSVal->getAsRegion(), SVB);
357*0fca6ea1SDimitry Andric     auto LineBufSizeGtN = SVB.evalBinOp(LinePtrNotNull, BO_GE, LineBufSize,
358*0fca6ea1SDimitry Andric                                         *NDefSVal, SVB.getConditionType())
359*0fca6ea1SDimitry Andric                               .getAs<DefinedOrUnknownSVal>();
360*0fca6ea1SDimitry Andric     if (!LineBufSizeGtN)
361*0fca6ea1SDimitry Andric       return LinePtrNotNull;
362*0fca6ea1SDimitry Andric     if (auto LineBufSizeOk = LinePtrNotNull->assume(*LineBufSizeGtN, true))
363*0fca6ea1SDimitry Andric       return LineBufSizeOk;
364*0fca6ea1SDimitry Andric 
365*0fca6ea1SDimitry Andric     EmitBugReport(LinePtrNotNull, SizeGreaterThanBufferSize);
366*0fca6ea1SDimitry Andric     return nullptr;
367*0fca6ea1SDimitry Andric   }
368*0fca6ea1SDimitry Andric   return State;
369*0fca6ea1SDimitry Andric }
370*0fca6ea1SDimitry Andric 
371*0fca6ea1SDimitry Andric void UnixAPIMisuseChecker::CheckGetDelim(CheckerContext &C,
372*0fca6ea1SDimitry Andric                                          const CallEvent &Call) const {
373*0fca6ea1SDimitry Andric   ProgramStateRef State = C.getState();
374*0fca6ea1SDimitry Andric 
375*0fca6ea1SDimitry Andric   // The parameter `n` must not be NULL.
376*0fca6ea1SDimitry Andric   SVal SizePtrSval = Call.getArgSVal(1);
377*0fca6ea1SDimitry Andric   State = EnsurePtrNotNull(SizePtrSval, Call.getArgExpr(1), C, State, "Size");
378*0fca6ea1SDimitry Andric   if (!State)
379*0fca6ea1SDimitry Andric     return;
380*0fca6ea1SDimitry Andric 
381*0fca6ea1SDimitry Andric   // The parameter `lineptr` must not be NULL.
382*0fca6ea1SDimitry Andric   SVal LinePtrPtrSVal = Call.getArgSVal(0);
383*0fca6ea1SDimitry Andric   State =
384*0fca6ea1SDimitry Andric       EnsurePtrNotNull(LinePtrPtrSVal, Call.getArgExpr(0), C, State, "Line");
385*0fca6ea1SDimitry Andric   if (!State)
386*0fca6ea1SDimitry Andric     return;
387*0fca6ea1SDimitry Andric 
388*0fca6ea1SDimitry Andric   State = EnsureGetdelimBufferAndSizeCorrect(LinePtrPtrSVal, SizePtrSval,
389*0fca6ea1SDimitry Andric                                              Call.getArgExpr(0),
390*0fca6ea1SDimitry Andric                                              Call.getArgExpr(1), C, State);
391*0fca6ea1SDimitry Andric   if (!State)
392*0fca6ea1SDimitry Andric     return;
393*0fca6ea1SDimitry Andric 
394*0fca6ea1SDimitry Andric   C.addTransition(State);
395*0fca6ea1SDimitry Andric }
396*0fca6ea1SDimitry Andric 
397*0fca6ea1SDimitry Andric //===----------------------------------------------------------------------===//
3980b57cec5SDimitry Andric // pthread_once
3990b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4000b57cec5SDimitry Andric 
4010b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
402*0fca6ea1SDimitry Andric                                             const CallEvent &Call) const {
4030b57cec5SDimitry Andric 
4040b57cec5SDimitry Andric   // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
4050b57cec5SDimitry Andric   // They can possibly be refactored.
4060b57cec5SDimitry Andric 
407*0fca6ea1SDimitry Andric   if (Call.getNumArgs() < 1)
4080b57cec5SDimitry Andric     return;
4090b57cec5SDimitry Andric 
4100b57cec5SDimitry Andric   // Check if the first argument is stack allocated.  If so, issue a warning
4110b57cec5SDimitry Andric   // because that's likely to be bad news.
4120b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
413*0fca6ea1SDimitry Andric   const MemRegion *R = Call.getArgSVal(0).getAsRegion();
4140b57cec5SDimitry Andric   if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
4150b57cec5SDimitry Andric     return;
4160b57cec5SDimitry Andric 
4170b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode(state);
4180b57cec5SDimitry Andric   if (!N)
4190b57cec5SDimitry Andric     return;
4200b57cec5SDimitry Andric 
4210b57cec5SDimitry Andric   SmallString<256> S;
4220b57cec5SDimitry Andric   llvm::raw_svector_ostream os(S);
4230b57cec5SDimitry Andric   os << "Call to 'pthread_once' uses";
4240b57cec5SDimitry Andric   if (const VarRegion *VR = dyn_cast<VarRegion>(R))
4250b57cec5SDimitry Andric     os << " the local variable '" << VR->getDecl()->getName() << '\'';
4260b57cec5SDimitry Andric   else
4270b57cec5SDimitry Andric     os << " stack allocated memory";
4280b57cec5SDimitry Andric   os << " for the \"control\" value.  Using such transient memory for "
4290b57cec5SDimitry Andric   "the control value is potentially dangerous.";
4300b57cec5SDimitry Andric   if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
4310b57cec5SDimitry Andric     os << "  Perhaps you intended to declare the variable as 'static'?";
4320b57cec5SDimitry Andric 
433a7dea167SDimitry Andric   auto report =
434647cbc5dSDimitry Andric       std::make_unique<PathSensitiveBugReport>(BT_pthreadOnce, os.str(), N);
435*0fca6ea1SDimitry Andric   report->addRange(Call.getArgExpr(0)->getSourceRange());
4360b57cec5SDimitry Andric   C.emitReport(std::move(report));
4370b57cec5SDimitry Andric }
4380b57cec5SDimitry Andric 
4390b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4400b57cec5SDimitry Andric // "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
4410b57cec5SDimitry Andric // with allocation size 0
4420b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
4430b57cec5SDimitry Andric 
4440b57cec5SDimitry Andric // FIXME: Eventually these should be rolled into the MallocChecker, but right now
4450b57cec5SDimitry Andric // they're more basic and valuable for widespread use.
4460b57cec5SDimitry Andric 
4470b57cec5SDimitry Andric // Returns true if we try to do a zero byte allocation, false otherwise.
4480b57cec5SDimitry Andric // Fills in trueState and falseState.
4490b57cec5SDimitry Andric static bool IsZeroByteAllocation(ProgramStateRef state,
4500b57cec5SDimitry Andric                                  const SVal argVal,
4510b57cec5SDimitry Andric                                  ProgramStateRef *trueState,
4520b57cec5SDimitry Andric                                  ProgramStateRef *falseState) {
4530b57cec5SDimitry Andric   std::tie(*trueState, *falseState) =
4540b57cec5SDimitry Andric     state->assume(argVal.castAs<DefinedSVal>());
4550b57cec5SDimitry Andric 
4560b57cec5SDimitry Andric   return (*falseState && !*trueState);
4570b57cec5SDimitry Andric }
4580b57cec5SDimitry Andric 
4590b57cec5SDimitry Andric // Generates an error report, indicating that the function whose name is given
4600b57cec5SDimitry Andric // will perform a zero byte allocation.
4610b57cec5SDimitry Andric // Returns false if an error occurred, true otherwise.
4620b57cec5SDimitry Andric bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
4630b57cec5SDimitry Andric                                                     CheckerContext &C,
4640b57cec5SDimitry Andric                                                     ProgramStateRef falseState,
4650b57cec5SDimitry Andric                                                     const Expr *arg,
4660b57cec5SDimitry Andric                                                     const char *fn_name) const {
4670b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode(falseState);
4680b57cec5SDimitry Andric   if (!N)
4690b57cec5SDimitry Andric     return false;
4700b57cec5SDimitry Andric 
4710b57cec5SDimitry Andric   SmallString<256> S;
4720b57cec5SDimitry Andric   llvm::raw_svector_ostream os(S);
4730b57cec5SDimitry Andric   os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
474a7dea167SDimitry Andric   auto report =
475647cbc5dSDimitry Andric       std::make_unique<PathSensitiveBugReport>(BT_mallocZero, os.str(), N);
4760b57cec5SDimitry Andric 
4770b57cec5SDimitry Andric   report->addRange(arg->getSourceRange());
4780b57cec5SDimitry Andric   bugreporter::trackExpressionValue(N, arg, *report);
4790b57cec5SDimitry Andric   C.emitReport(std::move(report));
4800b57cec5SDimitry Andric 
4810b57cec5SDimitry Andric   return true;
4820b57cec5SDimitry Andric }
4830b57cec5SDimitry Andric 
4840b57cec5SDimitry Andric // Does a basic check for 0-sized allocations suitable for most of the below
4850b57cec5SDimitry Andric // functions (modulo "calloc")
4860b57cec5SDimitry Andric void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,
4870b57cec5SDimitry Andric                                                      const CallExpr *CE,
4880b57cec5SDimitry Andric                                                      const unsigned numArgs,
4890b57cec5SDimitry Andric                                                      const unsigned sizeArg,
4900b57cec5SDimitry Andric                                                      const char *fn) const {
4915e801ac6SDimitry Andric   // Check for the correct number of arguments.
4920b57cec5SDimitry Andric   if (CE->getNumArgs() != numArgs)
4930b57cec5SDimitry Andric     return;
4940b57cec5SDimitry Andric 
4950b57cec5SDimitry Andric   // Check if the allocation size is 0.
4960b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
4970b57cec5SDimitry Andric   ProgramStateRef trueState = nullptr, falseState = nullptr;
4980b57cec5SDimitry Andric   const Expr *arg = CE->getArg(sizeArg);
4990b57cec5SDimitry Andric   SVal argVal = C.getSVal(arg);
5000b57cec5SDimitry Andric 
5010b57cec5SDimitry Andric   if (argVal.isUnknownOrUndef())
5020b57cec5SDimitry Andric     return;
5030b57cec5SDimitry Andric 
5040b57cec5SDimitry Andric   // Is the value perfectly constrained to zero?
5050b57cec5SDimitry Andric   if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
5060b57cec5SDimitry Andric     (void) ReportZeroByteAllocation(C, falseState, arg, fn);
5070b57cec5SDimitry Andric     return;
5080b57cec5SDimitry Andric   }
5090b57cec5SDimitry Andric   // Assume the value is non-zero going forward.
5100b57cec5SDimitry Andric   assert(trueState);
5110b57cec5SDimitry Andric   if (trueState != state)
5120b57cec5SDimitry Andric     C.addTransition(trueState);
5130b57cec5SDimitry Andric }
5140b57cec5SDimitry Andric 
5150b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,
5160b57cec5SDimitry Andric                                                 const CallExpr *CE) const {
5170b57cec5SDimitry Andric   unsigned int nArgs = CE->getNumArgs();
5180b57cec5SDimitry Andric   if (nArgs != 2)
5190b57cec5SDimitry Andric     return;
5200b57cec5SDimitry Andric 
5210b57cec5SDimitry Andric   ProgramStateRef state = C.getState();
5220b57cec5SDimitry Andric   ProgramStateRef trueState = nullptr, falseState = nullptr;
5230b57cec5SDimitry Andric 
5240b57cec5SDimitry Andric   unsigned int i;
5250b57cec5SDimitry Andric   for (i = 0; i < nArgs; i++) {
5260b57cec5SDimitry Andric     const Expr *arg = CE->getArg(i);
5270b57cec5SDimitry Andric     SVal argVal = C.getSVal(arg);
5280b57cec5SDimitry Andric     if (argVal.isUnknownOrUndef()) {
5290b57cec5SDimitry Andric       if (i == 0)
5300b57cec5SDimitry Andric         continue;
5310b57cec5SDimitry Andric       else
5320b57cec5SDimitry Andric         return;
5330b57cec5SDimitry Andric     }
5340b57cec5SDimitry Andric 
5350b57cec5SDimitry Andric     if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
5360b57cec5SDimitry Andric       if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
5370b57cec5SDimitry Andric         return;
5380b57cec5SDimitry Andric       else if (i == 0)
5390b57cec5SDimitry Andric         continue;
5400b57cec5SDimitry Andric       else
5410b57cec5SDimitry Andric         return;
5420b57cec5SDimitry Andric     }
5430b57cec5SDimitry Andric   }
5440b57cec5SDimitry Andric 
5450b57cec5SDimitry Andric   // Assume the value is non-zero going forward.
5460b57cec5SDimitry Andric   assert(trueState);
5470b57cec5SDimitry Andric   if (trueState != state)
5480b57cec5SDimitry Andric     C.addTransition(trueState);
5490b57cec5SDimitry Andric }
5500b57cec5SDimitry Andric 
5510b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,
5520b57cec5SDimitry Andric                                                 const CallExpr *CE) const {
5530b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 1, 0, "malloc");
5540b57cec5SDimitry Andric }
5550b57cec5SDimitry Andric 
5560b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,
5570b57cec5SDimitry Andric                                                  const CallExpr *CE) const {
5580b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 2, 1, "realloc");
5590b57cec5SDimitry Andric }
5600b57cec5SDimitry Andric 
5610b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,
5620b57cec5SDimitry Andric                                                   const CallExpr *CE) const {
5630b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 2, 1, "reallocf");
5640b57cec5SDimitry Andric }
5650b57cec5SDimitry Andric 
5660b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,
5670b57cec5SDimitry Andric                                                 const CallExpr *CE) const {
5680b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 1, 0, "alloca");
5690b57cec5SDimitry Andric }
5700b57cec5SDimitry Andric 
5710b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
5720b57cec5SDimitry Andric                                                      CheckerContext &C,
5730b57cec5SDimitry Andric                                                      const CallExpr *CE) const {
5740b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");
5750b57cec5SDimitry Andric }
5760b57cec5SDimitry Andric 
5770b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,
5780b57cec5SDimitry Andric                                                 const CallExpr *CE) const {
5790b57cec5SDimitry Andric   BasicAllocationCheck(C, CE, 1, 0, "valloc");
5800b57cec5SDimitry Andric }
5810b57cec5SDimitry Andric 
5820b57cec5SDimitry Andric void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
5830b57cec5SDimitry Andric                                              CheckerContext &C) const {
5840b57cec5SDimitry Andric   const FunctionDecl *FD = C.getCalleeDecl(CE);
5850b57cec5SDimitry Andric   if (!FD || FD->getKind() != Decl::Function)
5860b57cec5SDimitry Andric     return;
5870b57cec5SDimitry Andric 
5880b57cec5SDimitry Andric   // Don't treat functions in namespaces with the same name a Unix function
5890b57cec5SDimitry Andric   // as a call to the Unix function.
5900b57cec5SDimitry Andric   const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
591349cc55cSDimitry Andric   if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
5920b57cec5SDimitry Andric     return;
5930b57cec5SDimitry Andric 
5940b57cec5SDimitry Andric   StringRef FName = C.getCalleeName(FD);
5950b57cec5SDimitry Andric   if (FName.empty())
5960b57cec5SDimitry Andric     return;
5970b57cec5SDimitry Andric 
5980b57cec5SDimitry Andric   if (FName == "calloc")
5990b57cec5SDimitry Andric     CheckCallocZero(C, CE);
6000b57cec5SDimitry Andric 
6010b57cec5SDimitry Andric   else if (FName == "malloc")
6020b57cec5SDimitry Andric     CheckMallocZero(C, CE);
6030b57cec5SDimitry Andric 
6040b57cec5SDimitry Andric   else if (FName == "realloc")
6050b57cec5SDimitry Andric     CheckReallocZero(C, CE);
6060b57cec5SDimitry Andric 
6070b57cec5SDimitry Andric   else if (FName == "reallocf")
6080b57cec5SDimitry Andric     CheckReallocfZero(C, CE);
6090b57cec5SDimitry Andric 
6100b57cec5SDimitry Andric   else if (FName == "alloca" || FName ==  "__builtin_alloca")
6110b57cec5SDimitry Andric     CheckAllocaZero(C, CE);
6120b57cec5SDimitry Andric 
6130b57cec5SDimitry Andric   else if (FName == "__builtin_alloca_with_align")
6140b57cec5SDimitry Andric     CheckAllocaWithAlignZero(C, CE);
6150b57cec5SDimitry Andric 
6160b57cec5SDimitry Andric   else if (FName == "valloc")
6170b57cec5SDimitry Andric     CheckVallocZero(C, CE);
6180b57cec5SDimitry Andric }
6190b57cec5SDimitry Andric 
6200b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
6210b57cec5SDimitry Andric // Registration.
6220b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
6230b57cec5SDimitry Andric 
6240b57cec5SDimitry Andric #define REGISTER_CHECKER(CHECKERNAME)                                          \
6250b57cec5SDimitry Andric   void ento::register##CHECKERNAME(CheckerManager &mgr) {                      \
6260b57cec5SDimitry Andric     mgr.registerChecker<CHECKERNAME>();                                        \
6270b57cec5SDimitry Andric   }                                                                            \
6280b57cec5SDimitry Andric                                                                                \
6295ffd83dbSDimitry Andric   bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) {          \
6300b57cec5SDimitry Andric     return true;                                                               \
6310b57cec5SDimitry Andric   }
6320b57cec5SDimitry Andric 
6330b57cec5SDimitry Andric REGISTER_CHECKER(UnixAPIMisuseChecker)
6340b57cec5SDimitry Andric REGISTER_CHECKER(UnixAPIPortabilityChecker)
635