1*0b57cec5SDimitry Andric //= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-// 2*0b57cec5SDimitry Andric // 3*0b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4*0b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5*0b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6*0b57cec5SDimitry Andric // 7*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 8*0b57cec5SDimitry Andric // 9*0b57cec5SDimitry Andric // This defines UnixAPIChecker, which is an assortment of checks on calls 10*0b57cec5SDimitry Andric // to various, widely used UNIX/Posix functions. 11*0b57cec5SDimitry Andric // 12*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 13*0b57cec5SDimitry Andric 14*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 15*0b57cec5SDimitry Andric #include "clang/Basic/TargetInfo.h" 16*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 17*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h" 18*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h" 19*0b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 20*0b57cec5SDimitry Andric #include "llvm/ADT/Optional.h" 21*0b57cec5SDimitry Andric #include "llvm/ADT/STLExtras.h" 22*0b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h" 23*0b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 24*0b57cec5SDimitry Andric 25*0b57cec5SDimitry Andric using namespace clang; 26*0b57cec5SDimitry Andric using namespace ento; 27*0b57cec5SDimitry Andric 28*0b57cec5SDimitry Andric enum class OpenVariant { 29*0b57cec5SDimitry Andric /// The standard open() call: 30*0b57cec5SDimitry Andric /// int open(const char *path, int oflag, ...); 31*0b57cec5SDimitry Andric Open, 32*0b57cec5SDimitry Andric 33*0b57cec5SDimitry Andric /// The variant taking a directory file descriptor and a relative path: 34*0b57cec5SDimitry Andric /// int openat(int fd, const char *path, int oflag, ...); 35*0b57cec5SDimitry Andric OpenAt 36*0b57cec5SDimitry Andric }; 37*0b57cec5SDimitry Andric 38*0b57cec5SDimitry Andric namespace { 39*0b57cec5SDimitry Andric 40*0b57cec5SDimitry Andric class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > { 41*0b57cec5SDimitry Andric mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce; 42*0b57cec5SDimitry Andric mutable Optional<uint64_t> Val_O_CREAT; 43*0b57cec5SDimitry Andric 44*0b57cec5SDimitry Andric public: 45*0b57cec5SDimitry Andric DefaultBool CheckMisuse, CheckPortability; 46*0b57cec5SDimitry Andric 47*0b57cec5SDimitry Andric void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 48*0b57cec5SDimitry Andric 49*0b57cec5SDimitry Andric void CheckOpen(CheckerContext &C, const CallExpr *CE) const; 50*0b57cec5SDimitry Andric void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const; 51*0b57cec5SDimitry Andric void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; 52*0b57cec5SDimitry Andric 53*0b57cec5SDimitry Andric void CheckOpenVariant(CheckerContext &C, 54*0b57cec5SDimitry Andric const CallExpr *CE, OpenVariant Variant) const; 55*0b57cec5SDimitry Andric 56*0b57cec5SDimitry Andric void ReportOpenBug(CheckerContext &C, 57*0b57cec5SDimitry Andric ProgramStateRef State, 58*0b57cec5SDimitry Andric const char *Msg, 59*0b57cec5SDimitry Andric SourceRange SR) const; 60*0b57cec5SDimitry Andric 61*0b57cec5SDimitry Andric }; 62*0b57cec5SDimitry Andric 63*0b57cec5SDimitry Andric class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > { 64*0b57cec5SDimitry Andric public: 65*0b57cec5SDimitry Andric void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 66*0b57cec5SDimitry Andric 67*0b57cec5SDimitry Andric private: 68*0b57cec5SDimitry Andric mutable std::unique_ptr<BugType> BT_mallocZero; 69*0b57cec5SDimitry Andric 70*0b57cec5SDimitry Andric void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const; 71*0b57cec5SDimitry Andric void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; 72*0b57cec5SDimitry Andric void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const; 73*0b57cec5SDimitry Andric void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const; 74*0b57cec5SDimitry Andric void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const; 75*0b57cec5SDimitry Andric void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const; 76*0b57cec5SDimitry Andric void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const; 77*0b57cec5SDimitry Andric 78*0b57cec5SDimitry Andric bool ReportZeroByteAllocation(CheckerContext &C, 79*0b57cec5SDimitry Andric ProgramStateRef falseState, 80*0b57cec5SDimitry Andric const Expr *arg, 81*0b57cec5SDimitry Andric const char *fn_name) const; 82*0b57cec5SDimitry Andric void BasicAllocationCheck(CheckerContext &C, 83*0b57cec5SDimitry Andric const CallExpr *CE, 84*0b57cec5SDimitry Andric const unsigned numArgs, 85*0b57cec5SDimitry Andric const unsigned sizeArg, 86*0b57cec5SDimitry Andric const char *fn) const; 87*0b57cec5SDimitry Andric }; 88*0b57cec5SDimitry Andric 89*0b57cec5SDimitry Andric } //end anonymous namespace 90*0b57cec5SDimitry Andric 91*0b57cec5SDimitry Andric static void LazyInitialize(const CheckerBase *Checker, 92*0b57cec5SDimitry Andric std::unique_ptr<BugType> &BT, 93*0b57cec5SDimitry Andric const char *name) { 94*0b57cec5SDimitry Andric if (BT) 95*0b57cec5SDimitry Andric return; 96*0b57cec5SDimitry Andric BT.reset(new BugType(Checker, name, categories::UnixAPI)); 97*0b57cec5SDimitry Andric } 98*0b57cec5SDimitry Andric 99*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 100*0b57cec5SDimitry Andric // "open" (man 2 open) 101*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===/ 102*0b57cec5SDimitry Andric 103*0b57cec5SDimitry Andric void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE, 104*0b57cec5SDimitry Andric CheckerContext &C) const { 105*0b57cec5SDimitry Andric const FunctionDecl *FD = C.getCalleeDecl(CE); 106*0b57cec5SDimitry Andric if (!FD || FD->getKind() != Decl::Function) 107*0b57cec5SDimitry Andric return; 108*0b57cec5SDimitry Andric 109*0b57cec5SDimitry Andric // Don't treat functions in namespaces with the same name a Unix function 110*0b57cec5SDimitry Andric // as a call to the Unix function. 111*0b57cec5SDimitry Andric const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); 112*0b57cec5SDimitry Andric if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) 113*0b57cec5SDimitry Andric return; 114*0b57cec5SDimitry Andric 115*0b57cec5SDimitry Andric StringRef FName = C.getCalleeName(FD); 116*0b57cec5SDimitry Andric if (FName.empty()) 117*0b57cec5SDimitry Andric return; 118*0b57cec5SDimitry Andric 119*0b57cec5SDimitry Andric if (FName == "open") 120*0b57cec5SDimitry Andric CheckOpen(C, CE); 121*0b57cec5SDimitry Andric 122*0b57cec5SDimitry Andric else if (FName == "openat") 123*0b57cec5SDimitry Andric CheckOpenAt(C, CE); 124*0b57cec5SDimitry Andric 125*0b57cec5SDimitry Andric else if (FName == "pthread_once") 126*0b57cec5SDimitry Andric CheckPthreadOnce(C, CE); 127*0b57cec5SDimitry Andric } 128*0b57cec5SDimitry Andric void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C, 129*0b57cec5SDimitry Andric ProgramStateRef State, 130*0b57cec5SDimitry Andric const char *Msg, 131*0b57cec5SDimitry Andric SourceRange SR) const { 132*0b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(State); 133*0b57cec5SDimitry Andric if (!N) 134*0b57cec5SDimitry Andric return; 135*0b57cec5SDimitry Andric 136*0b57cec5SDimitry Andric LazyInitialize(this, BT_open, "Improper use of 'open'"); 137*0b57cec5SDimitry Andric 138*0b57cec5SDimitry Andric auto Report = llvm::make_unique<BugReport>(*BT_open, Msg, N); 139*0b57cec5SDimitry Andric Report->addRange(SR); 140*0b57cec5SDimitry Andric C.emitReport(std::move(Report)); 141*0b57cec5SDimitry Andric } 142*0b57cec5SDimitry Andric 143*0b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C, 144*0b57cec5SDimitry Andric const CallExpr *CE) const { 145*0b57cec5SDimitry Andric CheckOpenVariant(C, CE, OpenVariant::Open); 146*0b57cec5SDimitry Andric } 147*0b57cec5SDimitry Andric 148*0b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C, 149*0b57cec5SDimitry Andric const CallExpr *CE) const { 150*0b57cec5SDimitry Andric CheckOpenVariant(C, CE, OpenVariant::OpenAt); 151*0b57cec5SDimitry Andric } 152*0b57cec5SDimitry Andric 153*0b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C, 154*0b57cec5SDimitry Andric const CallExpr *CE, 155*0b57cec5SDimitry Andric OpenVariant Variant) const { 156*0b57cec5SDimitry Andric // The index of the argument taking the flags open flags (O_RDONLY, 157*0b57cec5SDimitry Andric // O_WRONLY, O_CREAT, etc.), 158*0b57cec5SDimitry Andric unsigned int FlagsArgIndex; 159*0b57cec5SDimitry Andric const char *VariantName; 160*0b57cec5SDimitry Andric switch (Variant) { 161*0b57cec5SDimitry Andric case OpenVariant::Open: 162*0b57cec5SDimitry Andric FlagsArgIndex = 1; 163*0b57cec5SDimitry Andric VariantName = "open"; 164*0b57cec5SDimitry Andric break; 165*0b57cec5SDimitry Andric case OpenVariant::OpenAt: 166*0b57cec5SDimitry Andric FlagsArgIndex = 2; 167*0b57cec5SDimitry Andric VariantName = "openat"; 168*0b57cec5SDimitry Andric break; 169*0b57cec5SDimitry Andric }; 170*0b57cec5SDimitry Andric 171*0b57cec5SDimitry Andric // All calls should at least provide arguments up to the 'flags' parameter. 172*0b57cec5SDimitry Andric unsigned int MinArgCount = FlagsArgIndex + 1; 173*0b57cec5SDimitry Andric 174*0b57cec5SDimitry Andric // If the flags has O_CREAT set then open/openat() require an additional 175*0b57cec5SDimitry Andric // argument specifying the file mode (permission bits) for the created file. 176*0b57cec5SDimitry Andric unsigned int CreateModeArgIndex = FlagsArgIndex + 1; 177*0b57cec5SDimitry Andric 178*0b57cec5SDimitry Andric // The create mode argument should be the last argument. 179*0b57cec5SDimitry Andric unsigned int MaxArgCount = CreateModeArgIndex + 1; 180*0b57cec5SDimitry Andric 181*0b57cec5SDimitry Andric ProgramStateRef state = C.getState(); 182*0b57cec5SDimitry Andric 183*0b57cec5SDimitry Andric if (CE->getNumArgs() < MinArgCount) { 184*0b57cec5SDimitry Andric // The frontend should issue a warning for this case, so this is a sanity 185*0b57cec5SDimitry Andric // check. 186*0b57cec5SDimitry Andric return; 187*0b57cec5SDimitry Andric } else if (CE->getNumArgs() == MaxArgCount) { 188*0b57cec5SDimitry Andric const Expr *Arg = CE->getArg(CreateModeArgIndex); 189*0b57cec5SDimitry Andric QualType QT = Arg->getType(); 190*0b57cec5SDimitry Andric if (!QT->isIntegerType()) { 191*0b57cec5SDimitry Andric SmallString<256> SBuf; 192*0b57cec5SDimitry Andric llvm::raw_svector_ostream OS(SBuf); 193*0b57cec5SDimitry Andric OS << "The " << CreateModeArgIndex + 1 194*0b57cec5SDimitry Andric << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) 195*0b57cec5SDimitry Andric << " argument to '" << VariantName << "' is not an integer"; 196*0b57cec5SDimitry Andric 197*0b57cec5SDimitry Andric ReportOpenBug(C, state, 198*0b57cec5SDimitry Andric SBuf.c_str(), 199*0b57cec5SDimitry Andric Arg->getSourceRange()); 200*0b57cec5SDimitry Andric return; 201*0b57cec5SDimitry Andric } 202*0b57cec5SDimitry Andric } else if (CE->getNumArgs() > MaxArgCount) { 203*0b57cec5SDimitry Andric SmallString<256> SBuf; 204*0b57cec5SDimitry Andric llvm::raw_svector_ostream OS(SBuf); 205*0b57cec5SDimitry Andric OS << "Call to '" << VariantName << "' with more than " << MaxArgCount 206*0b57cec5SDimitry Andric << " arguments"; 207*0b57cec5SDimitry Andric 208*0b57cec5SDimitry Andric ReportOpenBug(C, state, 209*0b57cec5SDimitry Andric SBuf.c_str(), 210*0b57cec5SDimitry Andric CE->getArg(MaxArgCount)->getSourceRange()); 211*0b57cec5SDimitry Andric return; 212*0b57cec5SDimitry Andric } 213*0b57cec5SDimitry Andric 214*0b57cec5SDimitry Andric // The definition of O_CREAT is platform specific. We need a better way 215*0b57cec5SDimitry Andric // of querying this information from the checking environment. 216*0b57cec5SDimitry Andric if (!Val_O_CREAT.hasValue()) { 217*0b57cec5SDimitry Andric if (C.getASTContext().getTargetInfo().getTriple().getVendor() 218*0b57cec5SDimitry Andric == llvm::Triple::Apple) 219*0b57cec5SDimitry Andric Val_O_CREAT = 0x0200; 220*0b57cec5SDimitry Andric else { 221*0b57cec5SDimitry Andric // FIXME: We need a more general way of getting the O_CREAT value. 222*0b57cec5SDimitry Andric // We could possibly grovel through the preprocessor state, but 223*0b57cec5SDimitry Andric // that would require passing the Preprocessor object to the ExprEngine. 224*0b57cec5SDimitry Andric // See also: MallocChecker.cpp / M_ZERO. 225*0b57cec5SDimitry Andric return; 226*0b57cec5SDimitry Andric } 227*0b57cec5SDimitry Andric } 228*0b57cec5SDimitry Andric 229*0b57cec5SDimitry Andric // Now check if oflags has O_CREAT set. 230*0b57cec5SDimitry Andric const Expr *oflagsEx = CE->getArg(FlagsArgIndex); 231*0b57cec5SDimitry Andric const SVal V = C.getSVal(oflagsEx); 232*0b57cec5SDimitry Andric if (!V.getAs<NonLoc>()) { 233*0b57cec5SDimitry Andric // The case where 'V' can be a location can only be due to a bad header, 234*0b57cec5SDimitry Andric // so in this case bail out. 235*0b57cec5SDimitry Andric return; 236*0b57cec5SDimitry Andric } 237*0b57cec5SDimitry Andric NonLoc oflags = V.castAs<NonLoc>(); 238*0b57cec5SDimitry Andric NonLoc ocreateFlag = C.getSValBuilder() 239*0b57cec5SDimitry Andric .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>(); 240*0b57cec5SDimitry Andric SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, 241*0b57cec5SDimitry Andric oflags, ocreateFlag, 242*0b57cec5SDimitry Andric oflagsEx->getType()); 243*0b57cec5SDimitry Andric if (maskedFlagsUC.isUnknownOrUndef()) 244*0b57cec5SDimitry Andric return; 245*0b57cec5SDimitry Andric DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>(); 246*0b57cec5SDimitry Andric 247*0b57cec5SDimitry Andric // Check if maskedFlags is non-zero. 248*0b57cec5SDimitry Andric ProgramStateRef trueState, falseState; 249*0b57cec5SDimitry Andric std::tie(trueState, falseState) = state->assume(maskedFlags); 250*0b57cec5SDimitry Andric 251*0b57cec5SDimitry Andric // Only emit an error if the value of 'maskedFlags' is properly 252*0b57cec5SDimitry Andric // constrained; 253*0b57cec5SDimitry Andric if (!(trueState && !falseState)) 254*0b57cec5SDimitry Andric return; 255*0b57cec5SDimitry Andric 256*0b57cec5SDimitry Andric if (CE->getNumArgs() < MaxArgCount) { 257*0b57cec5SDimitry Andric SmallString<256> SBuf; 258*0b57cec5SDimitry Andric llvm::raw_svector_ostream OS(SBuf); 259*0b57cec5SDimitry Andric OS << "Call to '" << VariantName << "' requires a " 260*0b57cec5SDimitry Andric << CreateModeArgIndex + 1 261*0b57cec5SDimitry Andric << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) 262*0b57cec5SDimitry Andric << " argument when the 'O_CREAT' flag is set"; 263*0b57cec5SDimitry Andric ReportOpenBug(C, trueState, 264*0b57cec5SDimitry Andric SBuf.c_str(), 265*0b57cec5SDimitry Andric oflagsEx->getSourceRange()); 266*0b57cec5SDimitry Andric } 267*0b57cec5SDimitry Andric } 268*0b57cec5SDimitry Andric 269*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 270*0b57cec5SDimitry Andric // pthread_once 271*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 272*0b57cec5SDimitry Andric 273*0b57cec5SDimitry Andric void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C, 274*0b57cec5SDimitry Andric const CallExpr *CE) const { 275*0b57cec5SDimitry Andric 276*0b57cec5SDimitry Andric // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. 277*0b57cec5SDimitry Andric // They can possibly be refactored. 278*0b57cec5SDimitry Andric 279*0b57cec5SDimitry Andric if (CE->getNumArgs() < 1) 280*0b57cec5SDimitry Andric return; 281*0b57cec5SDimitry Andric 282*0b57cec5SDimitry Andric // Check if the first argument is stack allocated. If so, issue a warning 283*0b57cec5SDimitry Andric // because that's likely to be bad news. 284*0b57cec5SDimitry Andric ProgramStateRef state = C.getState(); 285*0b57cec5SDimitry Andric const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion(); 286*0b57cec5SDimitry Andric if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) 287*0b57cec5SDimitry Andric return; 288*0b57cec5SDimitry Andric 289*0b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(state); 290*0b57cec5SDimitry Andric if (!N) 291*0b57cec5SDimitry Andric return; 292*0b57cec5SDimitry Andric 293*0b57cec5SDimitry Andric SmallString<256> S; 294*0b57cec5SDimitry Andric llvm::raw_svector_ostream os(S); 295*0b57cec5SDimitry Andric os << "Call to 'pthread_once' uses"; 296*0b57cec5SDimitry Andric if (const VarRegion *VR = dyn_cast<VarRegion>(R)) 297*0b57cec5SDimitry Andric os << " the local variable '" << VR->getDecl()->getName() << '\''; 298*0b57cec5SDimitry Andric else 299*0b57cec5SDimitry Andric os << " stack allocated memory"; 300*0b57cec5SDimitry Andric os << " for the \"control\" value. Using such transient memory for " 301*0b57cec5SDimitry Andric "the control value is potentially dangerous."; 302*0b57cec5SDimitry Andric if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) 303*0b57cec5SDimitry Andric os << " Perhaps you intended to declare the variable as 'static'?"; 304*0b57cec5SDimitry Andric 305*0b57cec5SDimitry Andric LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'"); 306*0b57cec5SDimitry Andric 307*0b57cec5SDimitry Andric auto report = llvm::make_unique<BugReport>(*BT_pthreadOnce, os.str(), N); 308*0b57cec5SDimitry Andric report->addRange(CE->getArg(0)->getSourceRange()); 309*0b57cec5SDimitry Andric C.emitReport(std::move(report)); 310*0b57cec5SDimitry Andric } 311*0b57cec5SDimitry Andric 312*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 313*0b57cec5SDimitry Andric // "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc" 314*0b57cec5SDimitry Andric // with allocation size 0 315*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 316*0b57cec5SDimitry Andric 317*0b57cec5SDimitry Andric // FIXME: Eventually these should be rolled into the MallocChecker, but right now 318*0b57cec5SDimitry Andric // they're more basic and valuable for widespread use. 319*0b57cec5SDimitry Andric 320*0b57cec5SDimitry Andric // Returns true if we try to do a zero byte allocation, false otherwise. 321*0b57cec5SDimitry Andric // Fills in trueState and falseState. 322*0b57cec5SDimitry Andric static bool IsZeroByteAllocation(ProgramStateRef state, 323*0b57cec5SDimitry Andric const SVal argVal, 324*0b57cec5SDimitry Andric ProgramStateRef *trueState, 325*0b57cec5SDimitry Andric ProgramStateRef *falseState) { 326*0b57cec5SDimitry Andric std::tie(*trueState, *falseState) = 327*0b57cec5SDimitry Andric state->assume(argVal.castAs<DefinedSVal>()); 328*0b57cec5SDimitry Andric 329*0b57cec5SDimitry Andric return (*falseState && !*trueState); 330*0b57cec5SDimitry Andric } 331*0b57cec5SDimitry Andric 332*0b57cec5SDimitry Andric // Generates an error report, indicating that the function whose name is given 333*0b57cec5SDimitry Andric // will perform a zero byte allocation. 334*0b57cec5SDimitry Andric // Returns false if an error occurred, true otherwise. 335*0b57cec5SDimitry Andric bool UnixAPIPortabilityChecker::ReportZeroByteAllocation( 336*0b57cec5SDimitry Andric CheckerContext &C, 337*0b57cec5SDimitry Andric ProgramStateRef falseState, 338*0b57cec5SDimitry Andric const Expr *arg, 339*0b57cec5SDimitry Andric const char *fn_name) const { 340*0b57cec5SDimitry Andric ExplodedNode *N = C.generateErrorNode(falseState); 341*0b57cec5SDimitry Andric if (!N) 342*0b57cec5SDimitry Andric return false; 343*0b57cec5SDimitry Andric 344*0b57cec5SDimitry Andric LazyInitialize(this, BT_mallocZero, 345*0b57cec5SDimitry Andric "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); 346*0b57cec5SDimitry Andric 347*0b57cec5SDimitry Andric SmallString<256> S; 348*0b57cec5SDimitry Andric llvm::raw_svector_ostream os(S); 349*0b57cec5SDimitry Andric os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; 350*0b57cec5SDimitry Andric auto report = llvm::make_unique<BugReport>(*BT_mallocZero, os.str(), N); 351*0b57cec5SDimitry Andric 352*0b57cec5SDimitry Andric report->addRange(arg->getSourceRange()); 353*0b57cec5SDimitry Andric bugreporter::trackExpressionValue(N, arg, *report); 354*0b57cec5SDimitry Andric C.emitReport(std::move(report)); 355*0b57cec5SDimitry Andric 356*0b57cec5SDimitry Andric return true; 357*0b57cec5SDimitry Andric } 358*0b57cec5SDimitry Andric 359*0b57cec5SDimitry Andric // Does a basic check for 0-sized allocations suitable for most of the below 360*0b57cec5SDimitry Andric // functions (modulo "calloc") 361*0b57cec5SDimitry Andric void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C, 362*0b57cec5SDimitry Andric const CallExpr *CE, 363*0b57cec5SDimitry Andric const unsigned numArgs, 364*0b57cec5SDimitry Andric const unsigned sizeArg, 365*0b57cec5SDimitry Andric const char *fn) const { 366*0b57cec5SDimitry Andric // Sanity check for the correct number of arguments 367*0b57cec5SDimitry Andric if (CE->getNumArgs() != numArgs) 368*0b57cec5SDimitry Andric return; 369*0b57cec5SDimitry Andric 370*0b57cec5SDimitry Andric // Check if the allocation size is 0. 371*0b57cec5SDimitry Andric ProgramStateRef state = C.getState(); 372*0b57cec5SDimitry Andric ProgramStateRef trueState = nullptr, falseState = nullptr; 373*0b57cec5SDimitry Andric const Expr *arg = CE->getArg(sizeArg); 374*0b57cec5SDimitry Andric SVal argVal = C.getSVal(arg); 375*0b57cec5SDimitry Andric 376*0b57cec5SDimitry Andric if (argVal.isUnknownOrUndef()) 377*0b57cec5SDimitry Andric return; 378*0b57cec5SDimitry Andric 379*0b57cec5SDimitry Andric // Is the value perfectly constrained to zero? 380*0b57cec5SDimitry Andric if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { 381*0b57cec5SDimitry Andric (void) ReportZeroByteAllocation(C, falseState, arg, fn); 382*0b57cec5SDimitry Andric return; 383*0b57cec5SDimitry Andric } 384*0b57cec5SDimitry Andric // Assume the value is non-zero going forward. 385*0b57cec5SDimitry Andric assert(trueState); 386*0b57cec5SDimitry Andric if (trueState != state) 387*0b57cec5SDimitry Andric C.addTransition(trueState); 388*0b57cec5SDimitry Andric } 389*0b57cec5SDimitry Andric 390*0b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C, 391*0b57cec5SDimitry Andric const CallExpr *CE) const { 392*0b57cec5SDimitry Andric unsigned int nArgs = CE->getNumArgs(); 393*0b57cec5SDimitry Andric if (nArgs != 2) 394*0b57cec5SDimitry Andric return; 395*0b57cec5SDimitry Andric 396*0b57cec5SDimitry Andric ProgramStateRef state = C.getState(); 397*0b57cec5SDimitry Andric ProgramStateRef trueState = nullptr, falseState = nullptr; 398*0b57cec5SDimitry Andric 399*0b57cec5SDimitry Andric unsigned int i; 400*0b57cec5SDimitry Andric for (i = 0; i < nArgs; i++) { 401*0b57cec5SDimitry Andric const Expr *arg = CE->getArg(i); 402*0b57cec5SDimitry Andric SVal argVal = C.getSVal(arg); 403*0b57cec5SDimitry Andric if (argVal.isUnknownOrUndef()) { 404*0b57cec5SDimitry Andric if (i == 0) 405*0b57cec5SDimitry Andric continue; 406*0b57cec5SDimitry Andric else 407*0b57cec5SDimitry Andric return; 408*0b57cec5SDimitry Andric } 409*0b57cec5SDimitry Andric 410*0b57cec5SDimitry Andric if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { 411*0b57cec5SDimitry Andric if (ReportZeroByteAllocation(C, falseState, arg, "calloc")) 412*0b57cec5SDimitry Andric return; 413*0b57cec5SDimitry Andric else if (i == 0) 414*0b57cec5SDimitry Andric continue; 415*0b57cec5SDimitry Andric else 416*0b57cec5SDimitry Andric return; 417*0b57cec5SDimitry Andric } 418*0b57cec5SDimitry Andric } 419*0b57cec5SDimitry Andric 420*0b57cec5SDimitry Andric // Assume the value is non-zero going forward. 421*0b57cec5SDimitry Andric assert(trueState); 422*0b57cec5SDimitry Andric if (trueState != state) 423*0b57cec5SDimitry Andric C.addTransition(trueState); 424*0b57cec5SDimitry Andric } 425*0b57cec5SDimitry Andric 426*0b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C, 427*0b57cec5SDimitry Andric const CallExpr *CE) const { 428*0b57cec5SDimitry Andric BasicAllocationCheck(C, CE, 1, 0, "malloc"); 429*0b57cec5SDimitry Andric } 430*0b57cec5SDimitry Andric 431*0b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C, 432*0b57cec5SDimitry Andric const CallExpr *CE) const { 433*0b57cec5SDimitry Andric BasicAllocationCheck(C, CE, 2, 1, "realloc"); 434*0b57cec5SDimitry Andric } 435*0b57cec5SDimitry Andric 436*0b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C, 437*0b57cec5SDimitry Andric const CallExpr *CE) const { 438*0b57cec5SDimitry Andric BasicAllocationCheck(C, CE, 2, 1, "reallocf"); 439*0b57cec5SDimitry Andric } 440*0b57cec5SDimitry Andric 441*0b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C, 442*0b57cec5SDimitry Andric const CallExpr *CE) const { 443*0b57cec5SDimitry Andric BasicAllocationCheck(C, CE, 1, 0, "alloca"); 444*0b57cec5SDimitry Andric } 445*0b57cec5SDimitry Andric 446*0b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero( 447*0b57cec5SDimitry Andric CheckerContext &C, 448*0b57cec5SDimitry Andric const CallExpr *CE) const { 449*0b57cec5SDimitry Andric BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align"); 450*0b57cec5SDimitry Andric } 451*0b57cec5SDimitry Andric 452*0b57cec5SDimitry Andric void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C, 453*0b57cec5SDimitry Andric const CallExpr *CE) const { 454*0b57cec5SDimitry Andric BasicAllocationCheck(C, CE, 1, 0, "valloc"); 455*0b57cec5SDimitry Andric } 456*0b57cec5SDimitry Andric 457*0b57cec5SDimitry Andric void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE, 458*0b57cec5SDimitry Andric CheckerContext &C) const { 459*0b57cec5SDimitry Andric const FunctionDecl *FD = C.getCalleeDecl(CE); 460*0b57cec5SDimitry Andric if (!FD || FD->getKind() != Decl::Function) 461*0b57cec5SDimitry Andric return; 462*0b57cec5SDimitry Andric 463*0b57cec5SDimitry Andric // Don't treat functions in namespaces with the same name a Unix function 464*0b57cec5SDimitry Andric // as a call to the Unix function. 465*0b57cec5SDimitry Andric const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); 466*0b57cec5SDimitry Andric if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) 467*0b57cec5SDimitry Andric return; 468*0b57cec5SDimitry Andric 469*0b57cec5SDimitry Andric StringRef FName = C.getCalleeName(FD); 470*0b57cec5SDimitry Andric if (FName.empty()) 471*0b57cec5SDimitry Andric return; 472*0b57cec5SDimitry Andric 473*0b57cec5SDimitry Andric if (FName == "calloc") 474*0b57cec5SDimitry Andric CheckCallocZero(C, CE); 475*0b57cec5SDimitry Andric 476*0b57cec5SDimitry Andric else if (FName == "malloc") 477*0b57cec5SDimitry Andric CheckMallocZero(C, CE); 478*0b57cec5SDimitry Andric 479*0b57cec5SDimitry Andric else if (FName == "realloc") 480*0b57cec5SDimitry Andric CheckReallocZero(C, CE); 481*0b57cec5SDimitry Andric 482*0b57cec5SDimitry Andric else if (FName == "reallocf") 483*0b57cec5SDimitry Andric CheckReallocfZero(C, CE); 484*0b57cec5SDimitry Andric 485*0b57cec5SDimitry Andric else if (FName == "alloca" || FName == "__builtin_alloca") 486*0b57cec5SDimitry Andric CheckAllocaZero(C, CE); 487*0b57cec5SDimitry Andric 488*0b57cec5SDimitry Andric else if (FName == "__builtin_alloca_with_align") 489*0b57cec5SDimitry Andric CheckAllocaWithAlignZero(C, CE); 490*0b57cec5SDimitry Andric 491*0b57cec5SDimitry Andric else if (FName == "valloc") 492*0b57cec5SDimitry Andric CheckVallocZero(C, CE); 493*0b57cec5SDimitry Andric } 494*0b57cec5SDimitry Andric 495*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 496*0b57cec5SDimitry Andric // Registration. 497*0b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 498*0b57cec5SDimitry Andric 499*0b57cec5SDimitry Andric #define REGISTER_CHECKER(CHECKERNAME) \ 500*0b57cec5SDimitry Andric void ento::register##CHECKERNAME(CheckerManager &mgr) { \ 501*0b57cec5SDimitry Andric mgr.registerChecker<CHECKERNAME>(); \ 502*0b57cec5SDimitry Andric } \ 503*0b57cec5SDimitry Andric \ 504*0b57cec5SDimitry Andric bool ento::shouldRegister##CHECKERNAME(const LangOptions &LO) { \ 505*0b57cec5SDimitry Andric return true; \ 506*0b57cec5SDimitry Andric } 507*0b57cec5SDimitry Andric 508*0b57cec5SDimitry Andric REGISTER_CHECKER(UnixAPIMisuseChecker) 509*0b57cec5SDimitry Andric REGISTER_CHECKER(UnixAPIPortabilityChecker) 510