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