1e5dd7070Spatrick //=== StdLibraryFunctionsChecker.cpp - Model standard functions -*- C++ -*-===// 2e5dd7070Spatrick // 3e5dd7070Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e5dd7070Spatrick // See https://llvm.org/LICENSE.txt for license information. 5e5dd7070Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e5dd7070Spatrick // 7e5dd7070Spatrick //===----------------------------------------------------------------------===// 8e5dd7070Spatrick // 9e5dd7070Spatrick // This checker improves modeling of a few simple library functions. 10e5dd7070Spatrick // 11*ec727ea7Spatrick // This checker provides a specification format - `Summary' - and 12e5dd7070Spatrick // contains descriptions of some library functions in this format. Each 13e5dd7070Spatrick // specification contains a list of branches for splitting the program state 14e5dd7070Spatrick // upon call, and range constraints on argument and return-value symbols that 15e5dd7070Spatrick // are satisfied on each branch. This spec can be expanded to include more 16e5dd7070Spatrick // items, like external effects of the function. 17e5dd7070Spatrick // 18e5dd7070Spatrick // The main difference between this approach and the body farms technique is 19e5dd7070Spatrick // in more explicit control over how many branches are produced. For example, 20e5dd7070Spatrick // consider standard C function `ispunct(int x)', which returns a non-zero value 21e5dd7070Spatrick // iff `x' is a punctuation character, that is, when `x' is in range 22e5dd7070Spatrick // ['!', '/'] [':', '@'] U ['[', '\`'] U ['{', '~']. 23*ec727ea7Spatrick // `Summary' provides only two branches for this function. However, 24e5dd7070Spatrick // any attempt to describe this range with if-statements in the body farm 25e5dd7070Spatrick // would result in many more branches. Because each branch needs to be analyzed 26e5dd7070Spatrick // independently, this significantly reduces performance. Additionally, 27e5dd7070Spatrick // once we consider a branch on which `x' is in range, say, ['!', '/'], 28e5dd7070Spatrick // we assume that such branch is an important separate path through the program, 29e5dd7070Spatrick // which may lead to false positives because considering this particular path 30e5dd7070Spatrick // was not consciously intended, and therefore it might have been unreachable. 31e5dd7070Spatrick // 32*ec727ea7Spatrick // This checker uses eval::Call for modeling pure functions (functions without 33*ec727ea7Spatrick // side effets), for which their `Summary' is a precise model. This avoids 34*ec727ea7Spatrick // unnecessary invalidation passes. Conflicts with other checkers are unlikely 35*ec727ea7Spatrick // because if the function has no other effects, other checkers would probably 36*ec727ea7Spatrick // never want to improve upon the modeling done by this checker. 37e5dd7070Spatrick // 38*ec727ea7Spatrick // Non-pure functions, for which only partial improvement over the default 39e5dd7070Spatrick // behavior is expected, are modeled via check::PostCall, non-intrusively. 40e5dd7070Spatrick // 41e5dd7070Spatrick // The following standard C functions are currently supported: 42e5dd7070Spatrick // 43e5dd7070Spatrick // fgetc getline isdigit isupper 44e5dd7070Spatrick // fread isalnum isgraph isxdigit 45e5dd7070Spatrick // fwrite isalpha islower read 46e5dd7070Spatrick // getc isascii isprint write 47e5dd7070Spatrick // getchar isblank ispunct 48e5dd7070Spatrick // getdelim iscntrl isspace 49e5dd7070Spatrick // 50e5dd7070Spatrick //===----------------------------------------------------------------------===// 51e5dd7070Spatrick 52e5dd7070Spatrick #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 53*ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 54e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/Checker.h" 55e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/CheckerManager.h" 56e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 57e5dd7070Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 58*ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" 59*ec727ea7Spatrick #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h" 60e5dd7070Spatrick 61e5dd7070Spatrick using namespace clang; 62e5dd7070Spatrick using namespace clang::ento; 63e5dd7070Spatrick 64e5dd7070Spatrick namespace { 65*ec727ea7Spatrick class StdLibraryFunctionsChecker 66*ec727ea7Spatrick : public Checker<check::PreCall, check::PostCall, eval::Call> { 67*ec727ea7Spatrick 68*ec727ea7Spatrick class Summary; 69e5dd7070Spatrick 70e5dd7070Spatrick /// Specify how much the analyzer engine should entrust modeling this function 71e5dd7070Spatrick /// to us. If he doesn't, he performs additional invalidations. 72*ec727ea7Spatrick enum InvalidationKind { NoEvalCall, EvalCallAsPure }; 73e5dd7070Spatrick 74e5dd7070Spatrick // The universal integral type to use in value range descriptions. 75e5dd7070Spatrick // Unsigned to make sure overflows are well-defined. 76*ec727ea7Spatrick typedef uint64_t RangeInt; 77e5dd7070Spatrick 78e5dd7070Spatrick /// Normally, describes a single range constraint, eg. {{0, 1}, {3, 4}} is 79e5dd7070Spatrick /// a non-negative integer, which less than 5 and not equal to 2. For 80e5dd7070Spatrick /// `ComparesToArgument', holds information about how exactly to compare to 81e5dd7070Spatrick /// the argument. 82*ec727ea7Spatrick typedef std::vector<std::pair<RangeInt, RangeInt>> IntRangeVector; 83e5dd7070Spatrick 84e5dd7070Spatrick /// A reference to an argument or return value by its number. 85e5dd7070Spatrick /// ArgNo in CallExpr and CallEvent is defined as Unsigned, but 86e5dd7070Spatrick /// obviously uint32_t should be enough for all practical purposes. 87*ec727ea7Spatrick typedef uint32_t ArgNo; 88*ec727ea7Spatrick static const ArgNo Ret; 89e5dd7070Spatrick 90*ec727ea7Spatrick class ValueConstraint; 91*ec727ea7Spatrick 92*ec727ea7Spatrick // Pointer to the ValueConstraint. We need a copyable, polymorphic and 93*ec727ea7Spatrick // default initialize able type (vector needs that). A raw pointer was good, 94*ec727ea7Spatrick // however, we cannot default initialize that. unique_ptr makes the Summary 95*ec727ea7Spatrick // class non-copyable, therefore not an option. Releasing the copyability 96*ec727ea7Spatrick // requirement would render the initialization of the Summary map infeasible. 97*ec727ea7Spatrick using ValueConstraintPtr = std::shared_ptr<ValueConstraint>; 98*ec727ea7Spatrick 99*ec727ea7Spatrick /// Polymorphic base class that represents a constraint on a given argument 100*ec727ea7Spatrick /// (or return value) of a function. Derived classes implement different kind 101*ec727ea7Spatrick /// of constraints, e.g range constraints or correlation between two 102*ec727ea7Spatrick /// arguments. 103*ec727ea7Spatrick class ValueConstraint { 104*ec727ea7Spatrick public: 105*ec727ea7Spatrick ValueConstraint(ArgNo ArgN) : ArgN(ArgN) {} 106*ec727ea7Spatrick virtual ~ValueConstraint() {} 107*ec727ea7Spatrick /// Apply the effects of the constraint on the given program state. If null 108*ec727ea7Spatrick /// is returned then the constraint is not feasible. 109*ec727ea7Spatrick virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, 110*ec727ea7Spatrick const Summary &Summary, 111*ec727ea7Spatrick CheckerContext &C) const = 0; 112*ec727ea7Spatrick virtual ValueConstraintPtr negate() const { 113*ec727ea7Spatrick llvm_unreachable("Not implemented"); 114*ec727ea7Spatrick }; 115*ec727ea7Spatrick 116*ec727ea7Spatrick // Check whether the constraint is malformed or not. It is malformed if the 117*ec727ea7Spatrick // specified argument has a mismatch with the given FunctionDecl (e.g. the 118*ec727ea7Spatrick // arg number is out-of-range of the function's argument list). 119*ec727ea7Spatrick bool checkValidity(const FunctionDecl *FD) const { 120*ec727ea7Spatrick const bool ValidArg = ArgN == Ret || ArgN < FD->getNumParams(); 121*ec727ea7Spatrick assert(ValidArg && "Arg out of range!"); 122*ec727ea7Spatrick if (!ValidArg) 123*ec727ea7Spatrick return false; 124*ec727ea7Spatrick // Subclasses may further refine the validation. 125*ec727ea7Spatrick return checkSpecificValidity(FD); 126*ec727ea7Spatrick } 127*ec727ea7Spatrick ArgNo getArgNo() const { return ArgN; } 128*ec727ea7Spatrick 129*ec727ea7Spatrick protected: 130*ec727ea7Spatrick ArgNo ArgN; // Argument to which we apply the constraint. 131*ec727ea7Spatrick 132*ec727ea7Spatrick /// Do polymorphic sanity check on the constraint. 133*ec727ea7Spatrick virtual bool checkSpecificValidity(const FunctionDecl *FD) const { 134*ec727ea7Spatrick return true; 135*ec727ea7Spatrick } 136*ec727ea7Spatrick }; 137*ec727ea7Spatrick 138*ec727ea7Spatrick /// Given a range, should the argument stay inside or outside this range? 139*ec727ea7Spatrick enum RangeKind { OutOfRange, WithinRange }; 140*ec727ea7Spatrick 141*ec727ea7Spatrick /// Encapsulates a single range on a single symbol within a branch. 142*ec727ea7Spatrick class RangeConstraint : public ValueConstraint { 143*ec727ea7Spatrick RangeKind Kind; // Kind of range definition. 144*ec727ea7Spatrick IntRangeVector Args; // Polymorphic arguments. 145e5dd7070Spatrick 146e5dd7070Spatrick public: 147*ec727ea7Spatrick RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Args) 148*ec727ea7Spatrick : ValueConstraint(ArgN), Kind(Kind), Args(Args) {} 149e5dd7070Spatrick 150*ec727ea7Spatrick const IntRangeVector &getRanges() const { 151e5dd7070Spatrick return Args; 152e5dd7070Spatrick } 153e5dd7070Spatrick 154e5dd7070Spatrick private: 155*ec727ea7Spatrick ProgramStateRef applyAsOutOfRange(ProgramStateRef State, 156*ec727ea7Spatrick const CallEvent &Call, 157*ec727ea7Spatrick const Summary &Summary) const; 158*ec727ea7Spatrick ProgramStateRef applyAsWithinRange(ProgramStateRef State, 159*ec727ea7Spatrick const CallEvent &Call, 160*ec727ea7Spatrick const Summary &Summary) const; 161e5dd7070Spatrick public: 162e5dd7070Spatrick ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, 163*ec727ea7Spatrick const Summary &Summary, 164*ec727ea7Spatrick CheckerContext &C) const override { 165e5dd7070Spatrick switch (Kind) { 166e5dd7070Spatrick case OutOfRange: 167e5dd7070Spatrick return applyAsOutOfRange(State, Call, Summary); 168e5dd7070Spatrick case WithinRange: 169e5dd7070Spatrick return applyAsWithinRange(State, Call, Summary); 170e5dd7070Spatrick } 171*ec727ea7Spatrick llvm_unreachable("Unknown range kind!"); 172*ec727ea7Spatrick } 173*ec727ea7Spatrick 174*ec727ea7Spatrick ValueConstraintPtr negate() const override { 175*ec727ea7Spatrick RangeConstraint Tmp(*this); 176*ec727ea7Spatrick switch (Kind) { 177*ec727ea7Spatrick case OutOfRange: 178*ec727ea7Spatrick Tmp.Kind = WithinRange; 179*ec727ea7Spatrick break; 180*ec727ea7Spatrick case WithinRange: 181*ec727ea7Spatrick Tmp.Kind = OutOfRange; 182*ec727ea7Spatrick break; 183*ec727ea7Spatrick } 184*ec727ea7Spatrick return std::make_shared<RangeConstraint>(Tmp); 185*ec727ea7Spatrick } 186*ec727ea7Spatrick 187*ec727ea7Spatrick bool checkSpecificValidity(const FunctionDecl *FD) const override { 188*ec727ea7Spatrick const bool ValidArg = 189*ec727ea7Spatrick getArgType(FD, ArgN)->isIntegralType(FD->getASTContext()); 190*ec727ea7Spatrick assert(ValidArg && 191*ec727ea7Spatrick "This constraint should be applied on an integral type"); 192*ec727ea7Spatrick return ValidArg; 193e5dd7070Spatrick } 194e5dd7070Spatrick }; 195e5dd7070Spatrick 196*ec727ea7Spatrick class ComparisonConstraint : public ValueConstraint { 197*ec727ea7Spatrick BinaryOperator::Opcode Opcode; 198*ec727ea7Spatrick ArgNo OtherArgN; 199e5dd7070Spatrick 200e5dd7070Spatrick public: 201*ec727ea7Spatrick ComparisonConstraint(ArgNo ArgN, BinaryOperator::Opcode Opcode, 202*ec727ea7Spatrick ArgNo OtherArgN) 203*ec727ea7Spatrick : ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {} 204*ec727ea7Spatrick ArgNo getOtherArgNo() const { return OtherArgN; } 205*ec727ea7Spatrick BinaryOperator::Opcode getOpcode() const { return Opcode; } 206*ec727ea7Spatrick ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, 207*ec727ea7Spatrick const Summary &Summary, 208*ec727ea7Spatrick CheckerContext &C) const override; 209*ec727ea7Spatrick }; 210*ec727ea7Spatrick 211*ec727ea7Spatrick class NotNullConstraint : public ValueConstraint { 212*ec727ea7Spatrick using ValueConstraint::ValueConstraint; 213*ec727ea7Spatrick // This variable has a role when we negate the constraint. 214*ec727ea7Spatrick bool CannotBeNull = true; 215*ec727ea7Spatrick 216*ec727ea7Spatrick public: 217*ec727ea7Spatrick ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, 218*ec727ea7Spatrick const Summary &Summary, 219*ec727ea7Spatrick CheckerContext &C) const override { 220*ec727ea7Spatrick SVal V = getArgSVal(Call, getArgNo()); 221*ec727ea7Spatrick if (V.isUndef()) 222*ec727ea7Spatrick return State; 223*ec727ea7Spatrick 224*ec727ea7Spatrick DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>(); 225*ec727ea7Spatrick if (!L.getAs<Loc>()) 226*ec727ea7Spatrick return State; 227*ec727ea7Spatrick 228*ec727ea7Spatrick return State->assume(L, CannotBeNull); 229*ec727ea7Spatrick } 230*ec727ea7Spatrick 231*ec727ea7Spatrick ValueConstraintPtr negate() const override { 232*ec727ea7Spatrick NotNullConstraint Tmp(*this); 233*ec727ea7Spatrick Tmp.CannotBeNull = !this->CannotBeNull; 234*ec727ea7Spatrick return std::make_shared<NotNullConstraint>(Tmp); 235*ec727ea7Spatrick } 236*ec727ea7Spatrick 237*ec727ea7Spatrick bool checkSpecificValidity(const FunctionDecl *FD) const override { 238*ec727ea7Spatrick const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); 239*ec727ea7Spatrick assert(ValidArg && 240*ec727ea7Spatrick "This constraint should be applied only on a pointer type"); 241*ec727ea7Spatrick return ValidArg; 242*ec727ea7Spatrick } 243*ec727ea7Spatrick }; 244*ec727ea7Spatrick 245*ec727ea7Spatrick // Represents a buffer argument with an additional size argument. 246*ec727ea7Spatrick // E.g. the first two arguments here: 247*ec727ea7Spatrick // ctime_s(char *buffer, rsize_t bufsz, const time_t *time); 248*ec727ea7Spatrick // Another example: 249*ec727ea7Spatrick // size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 250*ec727ea7Spatrick // // Here, ptr is the buffer, and its minimum size is `size * nmemb`. 251*ec727ea7Spatrick class BufferSizeConstraint : public ValueConstraint { 252*ec727ea7Spatrick // The argument which holds the size of the buffer. 253*ec727ea7Spatrick ArgNo SizeArgN; 254*ec727ea7Spatrick // The argument which is a multiplier to size. This is set in case of 255*ec727ea7Spatrick // `fread` like functions where the size is computed as a multiplication of 256*ec727ea7Spatrick // two arguments. 257*ec727ea7Spatrick llvm::Optional<ArgNo> SizeMultiplierArgN; 258*ec727ea7Spatrick // The operator we use in apply. This is negated in negate(). 259*ec727ea7Spatrick BinaryOperator::Opcode Op = BO_LE; 260*ec727ea7Spatrick 261*ec727ea7Spatrick public: 262*ec727ea7Spatrick BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize) 263*ec727ea7Spatrick : ValueConstraint(Buffer), SizeArgN(BufSize) {} 264*ec727ea7Spatrick 265*ec727ea7Spatrick BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize, ArgNo BufSizeMultiplier) 266*ec727ea7Spatrick : ValueConstraint(Buffer), SizeArgN(BufSize), 267*ec727ea7Spatrick SizeMultiplierArgN(BufSizeMultiplier) {} 268*ec727ea7Spatrick 269*ec727ea7Spatrick ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, 270*ec727ea7Spatrick const Summary &Summary, 271*ec727ea7Spatrick CheckerContext &C) const override { 272*ec727ea7Spatrick SValBuilder &SvalBuilder = C.getSValBuilder(); 273*ec727ea7Spatrick // The buffer argument. 274*ec727ea7Spatrick SVal BufV = getArgSVal(Call, getArgNo()); 275*ec727ea7Spatrick // The size argument. 276*ec727ea7Spatrick SVal SizeV = getArgSVal(Call, SizeArgN); 277*ec727ea7Spatrick // Multiply with another argument if given. 278*ec727ea7Spatrick if (SizeMultiplierArgN) { 279*ec727ea7Spatrick SVal SizeMulV = getArgSVal(Call, *SizeMultiplierArgN); 280*ec727ea7Spatrick SizeV = SvalBuilder.evalBinOp(State, BO_Mul, SizeV, SizeMulV, 281*ec727ea7Spatrick Summary.getArgType(SizeArgN)); 282*ec727ea7Spatrick } 283*ec727ea7Spatrick // The dynamic size of the buffer argument, got from the analyzer engine. 284*ec727ea7Spatrick SVal BufDynSize = getDynamicSizeWithOffset(State, BufV); 285*ec727ea7Spatrick 286*ec727ea7Spatrick SVal Feasible = SvalBuilder.evalBinOp(State, Op, SizeV, BufDynSize, 287*ec727ea7Spatrick SvalBuilder.getContext().BoolTy); 288*ec727ea7Spatrick if (auto F = Feasible.getAs<DefinedOrUnknownSVal>()) 289*ec727ea7Spatrick return State->assume(*F, true); 290*ec727ea7Spatrick 291*ec727ea7Spatrick // We can get here only if the size argument or the dynamic size is 292*ec727ea7Spatrick // undefined. But the dynamic size should never be undefined, only 293*ec727ea7Spatrick // unknown. So, here, the size of the argument is undefined, i.e. we 294*ec727ea7Spatrick // cannot apply the constraint. Actually, other checkers like 295*ec727ea7Spatrick // CallAndMessage should catch this situation earlier, because we call a 296*ec727ea7Spatrick // function with an uninitialized argument. 297*ec727ea7Spatrick llvm_unreachable("Size argument or the dynamic size is Undefined"); 298*ec727ea7Spatrick } 299*ec727ea7Spatrick 300*ec727ea7Spatrick ValueConstraintPtr negate() const override { 301*ec727ea7Spatrick BufferSizeConstraint Tmp(*this); 302*ec727ea7Spatrick Tmp.Op = BinaryOperator::negateComparisonOp(Op); 303*ec727ea7Spatrick return std::make_shared<BufferSizeConstraint>(Tmp); 304*ec727ea7Spatrick } 305*ec727ea7Spatrick }; 306*ec727ea7Spatrick 307*ec727ea7Spatrick /// The complete list of constraints that defines a single branch. 308*ec727ea7Spatrick typedef std::vector<ValueConstraintPtr> ConstraintSet; 309*ec727ea7Spatrick 310*ec727ea7Spatrick using ArgTypes = std::vector<QualType>; 311*ec727ea7Spatrick 312*ec727ea7Spatrick // A placeholder type, we use it whenever we do not care about the concrete 313*ec727ea7Spatrick // type in a Signature. 314*ec727ea7Spatrick const QualType Irrelevant{}; 315*ec727ea7Spatrick bool static isIrrelevant(QualType T) { return T.isNull(); } 316*ec727ea7Spatrick 317*ec727ea7Spatrick // The signature of a function we want to describe with a summary. This is a 318*ec727ea7Spatrick // concessive signature, meaning there may be irrelevant types in the 319*ec727ea7Spatrick // signature which we do not check against a function with concrete types. 320*ec727ea7Spatrick struct Signature { 321*ec727ea7Spatrick const ArgTypes ArgTys; 322*ec727ea7Spatrick const QualType RetTy; 323*ec727ea7Spatrick Signature(ArgTypes ArgTys, QualType RetTy) : ArgTys(ArgTys), RetTy(RetTy) { 324*ec727ea7Spatrick assertRetTypeSuitableForSignature(RetTy); 325*ec727ea7Spatrick for (size_t I = 0, E = ArgTys.size(); I != E; ++I) { 326*ec727ea7Spatrick QualType ArgTy = ArgTys[I]; 327*ec727ea7Spatrick assertArgTypeSuitableForSignature(ArgTy); 328*ec727ea7Spatrick } 329*ec727ea7Spatrick } 330*ec727ea7Spatrick bool matches(const FunctionDecl *FD) const; 331*ec727ea7Spatrick 332*ec727ea7Spatrick private: 333*ec727ea7Spatrick static void assertArgTypeSuitableForSignature(QualType T) { 334*ec727ea7Spatrick assert((T.isNull() || !T->isVoidType()) && 335*ec727ea7Spatrick "We should have no void types in the spec"); 336*ec727ea7Spatrick assert((T.isNull() || T.isCanonical()) && 337*ec727ea7Spatrick "We should only have canonical types in the spec"); 338*ec727ea7Spatrick } 339*ec727ea7Spatrick static void assertRetTypeSuitableForSignature(QualType T) { 340*ec727ea7Spatrick assert((T.isNull() || T.isCanonical()) && 341*ec727ea7Spatrick "We should only have canonical types in the spec"); 342*ec727ea7Spatrick } 343*ec727ea7Spatrick }; 344*ec727ea7Spatrick 345*ec727ea7Spatrick static QualType getArgType(const FunctionDecl *FD, ArgNo ArgN) { 346*ec727ea7Spatrick assert(FD && "Function must be set"); 347*ec727ea7Spatrick QualType T = (ArgN == Ret) 348*ec727ea7Spatrick ? FD->getReturnType().getCanonicalType() 349*ec727ea7Spatrick : FD->getParamDecl(ArgN)->getType().getCanonicalType(); 350e5dd7070Spatrick return T; 351e5dd7070Spatrick } 352e5dd7070Spatrick 353*ec727ea7Spatrick using Cases = std::vector<ConstraintSet>; 354e5dd7070Spatrick 355*ec727ea7Spatrick /// A summary includes information about 356*ec727ea7Spatrick /// * function prototype (signature) 357*ec727ea7Spatrick /// * approach to invalidation, 358*ec727ea7Spatrick /// * a list of branches - a list of list of ranges - 359*ec727ea7Spatrick /// A branch represents a path in the exploded graph of a function (which 360*ec727ea7Spatrick /// is a tree). So, a branch is a series of assumptions. In other words, 361*ec727ea7Spatrick /// branches represent split states and additional assumptions on top of 362*ec727ea7Spatrick /// the splitting assumption. 363*ec727ea7Spatrick /// For example, consider the branches in `isalpha(x)` 364*ec727ea7Spatrick /// Branch 1) 365*ec727ea7Spatrick /// x is in range ['A', 'Z'] or in ['a', 'z'] 366*ec727ea7Spatrick /// then the return value is not 0. (I.e. out-of-range [0, 0]) 367*ec727ea7Spatrick /// Branch 2) 368*ec727ea7Spatrick /// x is out-of-range ['A', 'Z'] and out-of-range ['a', 'z'] 369*ec727ea7Spatrick /// then the return value is 0. 370*ec727ea7Spatrick /// * a list of argument constraints, that must be true on every branch. 371*ec727ea7Spatrick /// If these constraints are not satisfied that means a fatal error 372*ec727ea7Spatrick /// usually resulting in undefined behaviour. 373*ec727ea7Spatrick /// 374*ec727ea7Spatrick /// Application of a summary: 375*ec727ea7Spatrick /// The signature and argument constraints together contain information 376*ec727ea7Spatrick /// about which functions are handled by the summary. The signature can use 377*ec727ea7Spatrick /// "wildcards", i.e. Irrelevant types. Irrelevant type of a parameter in 378*ec727ea7Spatrick /// a signature means that type is not compared to the type of the parameter 379*ec727ea7Spatrick /// in the found FunctionDecl. Argument constraints may specify additional 380*ec727ea7Spatrick /// rules for the given parameter's type, those rules are checked once the 381*ec727ea7Spatrick /// signature is matched. 382*ec727ea7Spatrick class Summary { 383*ec727ea7Spatrick const Signature Sign; 384*ec727ea7Spatrick const InvalidationKind InvalidationKd; 385*ec727ea7Spatrick Cases CaseConstraints; 386*ec727ea7Spatrick ConstraintSet ArgConstraints; 387*ec727ea7Spatrick 388*ec727ea7Spatrick // The function to which the summary applies. This is set after lookup and 389*ec727ea7Spatrick // match to the signature. 390*ec727ea7Spatrick const FunctionDecl *FD = nullptr; 391*ec727ea7Spatrick 392*ec727ea7Spatrick public: 393*ec727ea7Spatrick Summary(ArgTypes ArgTys, QualType RetTy, InvalidationKind InvalidationKd) 394*ec727ea7Spatrick : Sign(ArgTys, RetTy), InvalidationKd(InvalidationKd) {} 395*ec727ea7Spatrick 396*ec727ea7Spatrick Summary &Case(ConstraintSet&& CS) { 397*ec727ea7Spatrick CaseConstraints.push_back(std::move(CS)); 398*ec727ea7Spatrick return *this; 399*ec727ea7Spatrick } 400*ec727ea7Spatrick Summary &ArgConstraint(ValueConstraintPtr VC) { 401*ec727ea7Spatrick ArgConstraints.push_back(VC); 402*ec727ea7Spatrick return *this; 403*ec727ea7Spatrick } 404*ec727ea7Spatrick 405*ec727ea7Spatrick InvalidationKind getInvalidationKd() const { return InvalidationKd; } 406*ec727ea7Spatrick const Cases &getCaseConstraints() const { return CaseConstraints; } 407*ec727ea7Spatrick const ConstraintSet &getArgConstraints() const { return ArgConstraints; } 408*ec727ea7Spatrick 409*ec727ea7Spatrick QualType getArgType(ArgNo ArgN) const { 410*ec727ea7Spatrick return StdLibraryFunctionsChecker::getArgType(FD, ArgN); 411*ec727ea7Spatrick } 412*ec727ea7Spatrick 413*ec727ea7Spatrick // Returns true if the summary should be applied to the given function. 414*ec727ea7Spatrick // And if yes then store the function declaration. 415*ec727ea7Spatrick bool matchesAndSet(const FunctionDecl *FD) { 416*ec727ea7Spatrick bool Result = Sign.matches(FD) && validateByConstraints(FD); 417*ec727ea7Spatrick if (Result) { 418*ec727ea7Spatrick assert(!this->FD && "FD must not be set more than once"); 419*ec727ea7Spatrick this->FD = FD; 420*ec727ea7Spatrick } 421*ec727ea7Spatrick return Result; 422*ec727ea7Spatrick } 423*ec727ea7Spatrick 424*ec727ea7Spatrick private: 425*ec727ea7Spatrick // Once we know the exact type of the function then do sanity check on all 426*ec727ea7Spatrick // the given constraints. 427*ec727ea7Spatrick bool validateByConstraints(const FunctionDecl *FD) const { 428*ec727ea7Spatrick for (const ConstraintSet &Case : CaseConstraints) 429*ec727ea7Spatrick for (const ValueConstraintPtr &Constraint : Case) 430*ec727ea7Spatrick if (!Constraint->checkValidity(FD)) 431*ec727ea7Spatrick return false; 432*ec727ea7Spatrick for (const ValueConstraintPtr &Constraint : ArgConstraints) 433*ec727ea7Spatrick if (!Constraint->checkValidity(FD)) 434*ec727ea7Spatrick return false; 435*ec727ea7Spatrick return true; 436*ec727ea7Spatrick } 437*ec727ea7Spatrick }; 438e5dd7070Spatrick 439e5dd7070Spatrick // The map of all functions supported by the checker. It is initialized 440e5dd7070Spatrick // lazily, and it doesn't change after initialization. 441*ec727ea7Spatrick using FunctionSummaryMapType = llvm::DenseMap<const FunctionDecl *, Summary>; 442*ec727ea7Spatrick mutable FunctionSummaryMapType FunctionSummaryMap; 443e5dd7070Spatrick 444*ec727ea7Spatrick mutable std::unique_ptr<BugType> BT_InvalidArg; 445*ec727ea7Spatrick 446*ec727ea7Spatrick static SVal getArgSVal(const CallEvent &Call, ArgNo ArgN) { 447*ec727ea7Spatrick return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgN); 448e5dd7070Spatrick } 449e5dd7070Spatrick 450e5dd7070Spatrick public: 451*ec727ea7Spatrick void checkPreCall(const CallEvent &Call, CheckerContext &C) const; 452e5dd7070Spatrick void checkPostCall(const CallEvent &Call, CheckerContext &C) const; 453e5dd7070Spatrick bool evalCall(const CallEvent &Call, CheckerContext &C) const; 454e5dd7070Spatrick 455*ec727ea7Spatrick enum CheckKind { 456*ec727ea7Spatrick CK_StdCLibraryFunctionArgsChecker, 457*ec727ea7Spatrick CK_StdCLibraryFunctionsTesterChecker, 458*ec727ea7Spatrick CK_NumCheckKinds 459*ec727ea7Spatrick }; 460*ec727ea7Spatrick DefaultBool ChecksEnabled[CK_NumCheckKinds]; 461*ec727ea7Spatrick CheckerNameRef CheckNames[CK_NumCheckKinds]; 462*ec727ea7Spatrick 463*ec727ea7Spatrick bool DisplayLoadedSummaries = false; 464*ec727ea7Spatrick bool ModelPOSIX = false; 465*ec727ea7Spatrick 466e5dd7070Spatrick private: 467*ec727ea7Spatrick Optional<Summary> findFunctionSummary(const FunctionDecl *FD, 468*ec727ea7Spatrick CheckerContext &C) const; 469*ec727ea7Spatrick Optional<Summary> findFunctionSummary(const CallEvent &Call, 470e5dd7070Spatrick CheckerContext &C) const; 471e5dd7070Spatrick 472*ec727ea7Spatrick void initFunctionSummaries(CheckerContext &C) const; 473*ec727ea7Spatrick 474*ec727ea7Spatrick void reportBug(const CallEvent &Call, ExplodedNode *N, 475*ec727ea7Spatrick CheckerContext &C) const { 476*ec727ea7Spatrick if (!ChecksEnabled[CK_StdCLibraryFunctionArgsChecker]) 477*ec727ea7Spatrick return; 478*ec727ea7Spatrick // TODO Add detailed diagnostic. 479*ec727ea7Spatrick StringRef Msg = "Function argument constraint is not satisfied"; 480*ec727ea7Spatrick if (!BT_InvalidArg) 481*ec727ea7Spatrick BT_InvalidArg = std::make_unique<BugType>( 482*ec727ea7Spatrick CheckNames[CK_StdCLibraryFunctionArgsChecker], 483*ec727ea7Spatrick "Unsatisfied argument constraints", categories::LogicError); 484*ec727ea7Spatrick auto R = std::make_unique<PathSensitiveBugReport>(*BT_InvalidArg, Msg, N); 485*ec727ea7Spatrick bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *R); 486*ec727ea7Spatrick C.emitReport(std::move(R)); 487*ec727ea7Spatrick } 488e5dd7070Spatrick }; 489*ec727ea7Spatrick 490*ec727ea7Spatrick const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret = 491*ec727ea7Spatrick std::numeric_limits<ArgNo>::max(); 492*ec727ea7Spatrick 493e5dd7070Spatrick } // end of anonymous namespace 494e5dd7070Spatrick 495*ec727ea7Spatrick ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsOutOfRange( 496e5dd7070Spatrick ProgramStateRef State, const CallEvent &Call, 497*ec727ea7Spatrick const Summary &Summary) const { 498e5dd7070Spatrick 499e5dd7070Spatrick ProgramStateManager &Mgr = State->getStateManager(); 500e5dd7070Spatrick SValBuilder &SVB = Mgr.getSValBuilder(); 501e5dd7070Spatrick BasicValueFactory &BVF = SVB.getBasicValueFactory(); 502e5dd7070Spatrick ConstraintManager &CM = Mgr.getConstraintManager(); 503*ec727ea7Spatrick QualType T = Summary.getArgType(getArgNo()); 504e5dd7070Spatrick SVal V = getArgSVal(Call, getArgNo()); 505e5dd7070Spatrick 506e5dd7070Spatrick if (auto N = V.getAs<NonLoc>()) { 507*ec727ea7Spatrick const IntRangeVector &R = getRanges(); 508e5dd7070Spatrick size_t E = R.size(); 509e5dd7070Spatrick for (size_t I = 0; I != E; ++I) { 510e5dd7070Spatrick const llvm::APSInt &Min = BVF.getValue(R[I].first, T); 511e5dd7070Spatrick const llvm::APSInt &Max = BVF.getValue(R[I].second, T); 512e5dd7070Spatrick assert(Min <= Max); 513e5dd7070Spatrick State = CM.assumeInclusiveRange(State, *N, Min, Max, false); 514e5dd7070Spatrick if (!State) 515e5dd7070Spatrick break; 516e5dd7070Spatrick } 517e5dd7070Spatrick } 518e5dd7070Spatrick 519e5dd7070Spatrick return State; 520e5dd7070Spatrick } 521e5dd7070Spatrick 522*ec727ea7Spatrick ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsWithinRange( 523e5dd7070Spatrick ProgramStateRef State, const CallEvent &Call, 524*ec727ea7Spatrick const Summary &Summary) const { 525e5dd7070Spatrick 526e5dd7070Spatrick ProgramStateManager &Mgr = State->getStateManager(); 527e5dd7070Spatrick SValBuilder &SVB = Mgr.getSValBuilder(); 528e5dd7070Spatrick BasicValueFactory &BVF = SVB.getBasicValueFactory(); 529e5dd7070Spatrick ConstraintManager &CM = Mgr.getConstraintManager(); 530*ec727ea7Spatrick QualType T = Summary.getArgType(getArgNo()); 531e5dd7070Spatrick SVal V = getArgSVal(Call, getArgNo()); 532e5dd7070Spatrick 533e5dd7070Spatrick // "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R". 534e5dd7070Spatrick // We cut off [T_MIN, min(R) - 1] and [max(R) + 1, T_MAX] if necessary, 535e5dd7070Spatrick // and then cut away all holes in R one by one. 536*ec727ea7Spatrick // 537*ec727ea7Spatrick // E.g. consider a range list R as [A, B] and [C, D] 538*ec727ea7Spatrick // -------+--------+------------------+------------+-----------> 539*ec727ea7Spatrick // A B C D 540*ec727ea7Spatrick // Then we assume that the value is not in [-inf, A - 1], 541*ec727ea7Spatrick // then not in [D + 1, +inf], then not in [B + 1, C - 1] 542e5dd7070Spatrick if (auto N = V.getAs<NonLoc>()) { 543*ec727ea7Spatrick const IntRangeVector &R = getRanges(); 544e5dd7070Spatrick size_t E = R.size(); 545e5dd7070Spatrick 546e5dd7070Spatrick const llvm::APSInt &MinusInf = BVF.getMinValue(T); 547e5dd7070Spatrick const llvm::APSInt &PlusInf = BVF.getMaxValue(T); 548e5dd7070Spatrick 549e5dd7070Spatrick const llvm::APSInt &Left = BVF.getValue(R[0].first - 1ULL, T); 550e5dd7070Spatrick if (Left != PlusInf) { 551e5dd7070Spatrick assert(MinusInf <= Left); 552e5dd7070Spatrick State = CM.assumeInclusiveRange(State, *N, MinusInf, Left, false); 553e5dd7070Spatrick if (!State) 554e5dd7070Spatrick return nullptr; 555e5dd7070Spatrick } 556e5dd7070Spatrick 557e5dd7070Spatrick const llvm::APSInt &Right = BVF.getValue(R[E - 1].second + 1ULL, T); 558e5dd7070Spatrick if (Right != MinusInf) { 559e5dd7070Spatrick assert(Right <= PlusInf); 560e5dd7070Spatrick State = CM.assumeInclusiveRange(State, *N, Right, PlusInf, false); 561e5dd7070Spatrick if (!State) 562e5dd7070Spatrick return nullptr; 563e5dd7070Spatrick } 564e5dd7070Spatrick 565e5dd7070Spatrick for (size_t I = 1; I != E; ++I) { 566e5dd7070Spatrick const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, T); 567e5dd7070Spatrick const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, T); 568*ec727ea7Spatrick if (Min <= Max) { 569e5dd7070Spatrick State = CM.assumeInclusiveRange(State, *N, Min, Max, false); 570e5dd7070Spatrick if (!State) 571e5dd7070Spatrick return nullptr; 572e5dd7070Spatrick } 573e5dd7070Spatrick } 574*ec727ea7Spatrick } 575e5dd7070Spatrick 576e5dd7070Spatrick return State; 577e5dd7070Spatrick } 578e5dd7070Spatrick 579*ec727ea7Spatrick ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( 580*ec727ea7Spatrick ProgramStateRef State, const CallEvent &Call, const Summary &Summary, 581*ec727ea7Spatrick CheckerContext &C) const { 582e5dd7070Spatrick 583e5dd7070Spatrick ProgramStateManager &Mgr = State->getStateManager(); 584e5dd7070Spatrick SValBuilder &SVB = Mgr.getSValBuilder(); 585e5dd7070Spatrick QualType CondT = SVB.getConditionType(); 586*ec727ea7Spatrick QualType T = Summary.getArgType(getArgNo()); 587e5dd7070Spatrick SVal V = getArgSVal(Call, getArgNo()); 588e5dd7070Spatrick 589e5dd7070Spatrick BinaryOperator::Opcode Op = getOpcode(); 590*ec727ea7Spatrick ArgNo OtherArg = getOtherArgNo(); 591e5dd7070Spatrick SVal OtherV = getArgSVal(Call, OtherArg); 592*ec727ea7Spatrick QualType OtherT = Summary.getArgType(OtherArg); 593e5dd7070Spatrick // Note: we avoid integral promotion for comparison. 594e5dd7070Spatrick OtherV = SVB.evalCast(OtherV, T, OtherT); 595e5dd7070Spatrick if (auto CompV = SVB.evalBinOp(State, Op, V, OtherV, CondT) 596e5dd7070Spatrick .getAs<DefinedOrUnknownSVal>()) 597e5dd7070Spatrick State = State->assume(*CompV, true); 598e5dd7070Spatrick return State; 599e5dd7070Spatrick } 600e5dd7070Spatrick 601*ec727ea7Spatrick void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, 602e5dd7070Spatrick CheckerContext &C) const { 603*ec727ea7Spatrick Optional<Summary> FoundSummary = findFunctionSummary(Call, C); 604e5dd7070Spatrick if (!FoundSummary) 605e5dd7070Spatrick return; 606e5dd7070Spatrick 607*ec727ea7Spatrick const Summary &Summary = *FoundSummary; 608e5dd7070Spatrick ProgramStateRef State = C.getState(); 609e5dd7070Spatrick 610e5dd7070Spatrick ProgramStateRef NewState = State; 611*ec727ea7Spatrick for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) { 612*ec727ea7Spatrick ProgramStateRef SuccessSt = Constraint->apply(NewState, Call, Summary, C); 613*ec727ea7Spatrick ProgramStateRef FailureSt = 614*ec727ea7Spatrick Constraint->negate()->apply(NewState, Call, Summary, C); 615*ec727ea7Spatrick // The argument constraint is not satisfied. 616*ec727ea7Spatrick if (FailureSt && !SuccessSt) { 617*ec727ea7Spatrick if (ExplodedNode *N = C.generateErrorNode(NewState)) 618*ec727ea7Spatrick reportBug(Call, N, C); 619*ec727ea7Spatrick break; 620*ec727ea7Spatrick } else { 621*ec727ea7Spatrick // We will apply the constraint even if we cannot reason about the 622*ec727ea7Spatrick // argument. This means both SuccessSt and FailureSt can be true. If we 623*ec727ea7Spatrick // weren't applying the constraint that would mean that symbolic 624*ec727ea7Spatrick // execution continues on a code whose behaviour is undefined. 625*ec727ea7Spatrick assert(SuccessSt); 626*ec727ea7Spatrick NewState = SuccessSt; 627*ec727ea7Spatrick } 628*ec727ea7Spatrick } 629*ec727ea7Spatrick if (NewState && NewState != State) 630*ec727ea7Spatrick C.addTransition(NewState); 631*ec727ea7Spatrick } 632*ec727ea7Spatrick 633*ec727ea7Spatrick void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, 634*ec727ea7Spatrick CheckerContext &C) const { 635*ec727ea7Spatrick Optional<Summary> FoundSummary = findFunctionSummary(Call, C); 636*ec727ea7Spatrick if (!FoundSummary) 637*ec727ea7Spatrick return; 638*ec727ea7Spatrick 639*ec727ea7Spatrick // Now apply the constraints. 640*ec727ea7Spatrick const Summary &Summary = *FoundSummary; 641*ec727ea7Spatrick ProgramStateRef State = C.getState(); 642*ec727ea7Spatrick 643*ec727ea7Spatrick // Apply case/branch specifications. 644*ec727ea7Spatrick for (const ConstraintSet &Case : Summary.getCaseConstraints()) { 645*ec727ea7Spatrick ProgramStateRef NewState = State; 646*ec727ea7Spatrick for (const ValueConstraintPtr &Constraint : Case) { 647*ec727ea7Spatrick NewState = Constraint->apply(NewState, Call, Summary, C); 648e5dd7070Spatrick if (!NewState) 649e5dd7070Spatrick break; 650e5dd7070Spatrick } 651e5dd7070Spatrick 652e5dd7070Spatrick if (NewState && NewState != State) 653e5dd7070Spatrick C.addTransition(NewState); 654e5dd7070Spatrick } 655e5dd7070Spatrick } 656e5dd7070Spatrick 657e5dd7070Spatrick bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, 658e5dd7070Spatrick CheckerContext &C) const { 659*ec727ea7Spatrick Optional<Summary> FoundSummary = findFunctionSummary(Call, C); 660e5dd7070Spatrick if (!FoundSummary) 661e5dd7070Spatrick return false; 662e5dd7070Spatrick 663*ec727ea7Spatrick const Summary &Summary = *FoundSummary; 664*ec727ea7Spatrick switch (Summary.getInvalidationKd()) { 665e5dd7070Spatrick case EvalCallAsPure: { 666e5dd7070Spatrick ProgramStateRef State = C.getState(); 667e5dd7070Spatrick const LocationContext *LC = C.getLocationContext(); 668*ec727ea7Spatrick const auto *CE = cast_or_null<CallExpr>(Call.getOriginExpr()); 669e5dd7070Spatrick SVal V = C.getSValBuilder().conjureSymbolVal( 670e5dd7070Spatrick CE, LC, CE->getType().getCanonicalType(), C.blockCount()); 671e5dd7070Spatrick State = State->BindExpr(CE, LC, V); 672e5dd7070Spatrick C.addTransition(State); 673e5dd7070Spatrick return true; 674e5dd7070Spatrick } 675e5dd7070Spatrick case NoEvalCall: 676e5dd7070Spatrick // Summary tells us to avoid performing eval::Call. The function is possibly 677e5dd7070Spatrick // evaluated by another checker, or evaluated conservatively. 678e5dd7070Spatrick return false; 679e5dd7070Spatrick } 680e5dd7070Spatrick llvm_unreachable("Unknown invalidation kind!"); 681e5dd7070Spatrick } 682e5dd7070Spatrick 683*ec727ea7Spatrick bool StdLibraryFunctionsChecker::Signature::matches( 684*ec727ea7Spatrick const FunctionDecl *FD) const { 685e5dd7070Spatrick // Check number of arguments: 686*ec727ea7Spatrick if (FD->param_size() != ArgTys.size()) 687e5dd7070Spatrick return false; 688e5dd7070Spatrick 689*ec727ea7Spatrick // Check return type. 690*ec727ea7Spatrick if (!isIrrelevant(RetTy)) 691*ec727ea7Spatrick if (RetTy != FD->getReturnType().getCanonicalType()) 692e5dd7070Spatrick return false; 693e5dd7070Spatrick 694*ec727ea7Spatrick // Check argument types. 695*ec727ea7Spatrick for (size_t I = 0, E = ArgTys.size(); I != E; ++I) { 696*ec727ea7Spatrick QualType ArgTy = ArgTys[I]; 697*ec727ea7Spatrick if (isIrrelevant(ArgTy)) 698e5dd7070Spatrick continue; 699*ec727ea7Spatrick if (ArgTy != FD->getParamDecl(I)->getType().getCanonicalType()) 700e5dd7070Spatrick return false; 701e5dd7070Spatrick } 702e5dd7070Spatrick 703e5dd7070Spatrick return true; 704e5dd7070Spatrick } 705e5dd7070Spatrick 706*ec727ea7Spatrick Optional<StdLibraryFunctionsChecker::Summary> 707e5dd7070Spatrick StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD, 708e5dd7070Spatrick CheckerContext &C) const { 709e5dd7070Spatrick if (!FD) 710e5dd7070Spatrick return None; 711e5dd7070Spatrick 712*ec727ea7Spatrick initFunctionSummaries(C); 713e5dd7070Spatrick 714*ec727ea7Spatrick auto FSMI = FunctionSummaryMap.find(FD->getCanonicalDecl()); 715e5dd7070Spatrick if (FSMI == FunctionSummaryMap.end()) 716e5dd7070Spatrick return None; 717*ec727ea7Spatrick return FSMI->second; 718*ec727ea7Spatrick } 719e5dd7070Spatrick 720*ec727ea7Spatrick Optional<StdLibraryFunctionsChecker::Summary> 721*ec727ea7Spatrick StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call, 722*ec727ea7Spatrick CheckerContext &C) const { 723*ec727ea7Spatrick const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); 724*ec727ea7Spatrick if (!FD) 725*ec727ea7Spatrick return None; 726*ec727ea7Spatrick return findFunctionSummary(FD, C); 727*ec727ea7Spatrick } 728e5dd7070Spatrick 729*ec727ea7Spatrick static llvm::Optional<QualType> lookupType(StringRef Name, 730*ec727ea7Spatrick const ASTContext &ACtx) { 731*ec727ea7Spatrick IdentifierInfo &II = ACtx.Idents.get(Name); 732*ec727ea7Spatrick auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); 733*ec727ea7Spatrick if (LookupRes.size() == 0) 734*ec727ea7Spatrick return None; 735*ec727ea7Spatrick 736*ec727ea7Spatrick // Prioritze typedef declarations. 737*ec727ea7Spatrick // This is needed in case of C struct typedefs. E.g.: 738*ec727ea7Spatrick // typedef struct FILE FILE; 739*ec727ea7Spatrick // In this case, we have a RecordDecl 'struct FILE' with the name 'FILE' and 740*ec727ea7Spatrick // we have a TypedefDecl with the name 'FILE'. 741*ec727ea7Spatrick for (Decl *D : LookupRes) 742*ec727ea7Spatrick if (auto *TD = dyn_cast<TypedefNameDecl>(D)) 743*ec727ea7Spatrick return ACtx.getTypeDeclType(TD).getCanonicalType(); 744*ec727ea7Spatrick 745*ec727ea7Spatrick // Find the first TypeDecl. 746*ec727ea7Spatrick // There maybe cases when a function has the same name as a struct. 747*ec727ea7Spatrick // E.g. in POSIX: `struct stat` and the function `stat()`: 748*ec727ea7Spatrick // int stat(const char *restrict path, struct stat *restrict buf); 749*ec727ea7Spatrick for (Decl *D : LookupRes) 750*ec727ea7Spatrick if (auto *TD = dyn_cast<TypeDecl>(D)) 751*ec727ea7Spatrick return ACtx.getTypeDeclType(TD).getCanonicalType(); 752e5dd7070Spatrick return None; 753e5dd7070Spatrick } 754e5dd7070Spatrick 755e5dd7070Spatrick void StdLibraryFunctionsChecker::initFunctionSummaries( 756*ec727ea7Spatrick CheckerContext &C) const { 757e5dd7070Spatrick if (!FunctionSummaryMap.empty()) 758e5dd7070Spatrick return; 759e5dd7070Spatrick 760*ec727ea7Spatrick SValBuilder &SVB = C.getSValBuilder(); 761*ec727ea7Spatrick BasicValueFactory &BVF = SVB.getBasicValueFactory(); 762*ec727ea7Spatrick const ASTContext &ACtx = BVF.getContext(); 763e5dd7070Spatrick 764e5dd7070Spatrick // These types are useful for writing specifications quickly, 765e5dd7070Spatrick // New specifications should probably introduce more types. 766e5dd7070Spatrick // Some types are hard to obtain from the AST, eg. "ssize_t". 767e5dd7070Spatrick // In such cases it should be possible to provide multiple variants 768e5dd7070Spatrick // of function summary for common cases (eg. ssize_t could be int or long 769e5dd7070Spatrick // or long long, so three summary variants would be enough). 770e5dd7070Spatrick // Of course, function variants are also useful for C++ overloads. 771*ec727ea7Spatrick const QualType VoidTy = ACtx.VoidTy; 772*ec727ea7Spatrick const QualType IntTy = ACtx.IntTy; 773*ec727ea7Spatrick const QualType UnsignedIntTy = ACtx.UnsignedIntTy; 774*ec727ea7Spatrick const QualType LongTy = ACtx.LongTy; 775*ec727ea7Spatrick const QualType LongLongTy = ACtx.LongLongTy; 776*ec727ea7Spatrick const QualType SizeTy = ACtx.getSizeType(); 777e5dd7070Spatrick 778*ec727ea7Spatrick const QualType VoidPtrTy = ACtx.VoidPtrTy; // void * 779*ec727ea7Spatrick const QualType IntPtrTy = ACtx.getPointerType(IntTy); // int * 780*ec727ea7Spatrick const QualType UnsignedIntPtrTy = 781*ec727ea7Spatrick ACtx.getPointerType(UnsignedIntTy); // unsigned int * 782*ec727ea7Spatrick const QualType VoidPtrRestrictTy = 783*ec727ea7Spatrick ACtx.getLangOpts().C99 ? ACtx.getRestrictType(VoidPtrTy) // void *restrict 784*ec727ea7Spatrick : VoidPtrTy; 785*ec727ea7Spatrick const QualType ConstVoidPtrTy = 786*ec727ea7Spatrick ACtx.getPointerType(ACtx.VoidTy.withConst()); // const void * 787*ec727ea7Spatrick const QualType CharPtrTy = ACtx.getPointerType(ACtx.CharTy); // char * 788*ec727ea7Spatrick const QualType CharPtrRestrictTy = 789*ec727ea7Spatrick ACtx.getLangOpts().C99 ? ACtx.getRestrictType(CharPtrTy) // char *restrict 790*ec727ea7Spatrick : CharPtrTy; 791*ec727ea7Spatrick const QualType ConstCharPtrTy = 792*ec727ea7Spatrick ACtx.getPointerType(ACtx.CharTy.withConst()); // const char * 793*ec727ea7Spatrick const QualType ConstCharPtrRestrictTy = 794*ec727ea7Spatrick ACtx.getLangOpts().C99 795*ec727ea7Spatrick ? ACtx.getRestrictType(ConstCharPtrTy) // const char *restrict 796*ec727ea7Spatrick : ConstCharPtrTy; 797*ec727ea7Spatrick const QualType Wchar_tPtrTy = ACtx.getPointerType(ACtx.WCharTy); // wchar_t * 798*ec727ea7Spatrick const QualType ConstWchar_tPtrTy = 799*ec727ea7Spatrick ACtx.getPointerType(ACtx.WCharTy.withConst()); // const wchar_t * 800*ec727ea7Spatrick const QualType ConstVoidPtrRestrictTy = 801*ec727ea7Spatrick ACtx.getLangOpts().C99 802*ec727ea7Spatrick ? ACtx.getRestrictType(ConstVoidPtrTy) // const void *restrict 803*ec727ea7Spatrick : ConstVoidPtrTy; 804*ec727ea7Spatrick 805*ec727ea7Spatrick const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue(); 806*ec727ea7Spatrick const RangeInt UnsignedIntMax = 807*ec727ea7Spatrick BVF.getMaxValue(UnsignedIntTy).getLimitedValue(); 808*ec727ea7Spatrick const RangeInt LongMax = BVF.getMaxValue(LongTy).getLimitedValue(); 809*ec727ea7Spatrick const RangeInt LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue(); 810*ec727ea7Spatrick const RangeInt SizeMax = BVF.getMaxValue(SizeTy).getLimitedValue(); 811*ec727ea7Spatrick 812*ec727ea7Spatrick // Set UCharRangeMax to min of int or uchar maximum value. 813*ec727ea7Spatrick // The C standard states that the arguments of functions like isalpha must 814*ec727ea7Spatrick // be representable as an unsigned char. Their type is 'int', so the max 815*ec727ea7Spatrick // value of the argument should be min(UCharMax, IntMax). This just happen 816*ec727ea7Spatrick // to be true for commonly used and well tested instruction set 817*ec727ea7Spatrick // architectures, but not for others. 818*ec727ea7Spatrick const RangeInt UCharRangeMax = 819*ec727ea7Spatrick std::min(BVF.getMaxValue(ACtx.UnsignedCharTy).getLimitedValue(), IntMax); 820*ec727ea7Spatrick 821*ec727ea7Spatrick // The platform dependent value of EOF. 822*ec727ea7Spatrick // Try our best to parse this from the Preprocessor, otherwise fallback to -1. 823*ec727ea7Spatrick const auto EOFv = [&C]() -> RangeInt { 824*ec727ea7Spatrick if (const llvm::Optional<int> OptInt = 825*ec727ea7Spatrick tryExpandAsInteger("EOF", C.getPreprocessor())) 826*ec727ea7Spatrick return *OptInt; 827*ec727ea7Spatrick return -1; 828*ec727ea7Spatrick }(); 829*ec727ea7Spatrick 830*ec727ea7Spatrick // Auxiliary class to aid adding summaries to the summary map. 831*ec727ea7Spatrick struct AddToFunctionSummaryMap { 832*ec727ea7Spatrick const ASTContext &ACtx; 833*ec727ea7Spatrick FunctionSummaryMapType ⤅ 834*ec727ea7Spatrick bool DisplayLoadedSummaries; 835*ec727ea7Spatrick AddToFunctionSummaryMap(const ASTContext &ACtx, FunctionSummaryMapType &FSM, 836*ec727ea7Spatrick bool DisplayLoadedSummaries) 837*ec727ea7Spatrick : ACtx(ACtx), Map(FSM), DisplayLoadedSummaries(DisplayLoadedSummaries) { 838*ec727ea7Spatrick } 839*ec727ea7Spatrick 840*ec727ea7Spatrick // Add a summary to a FunctionDecl found by lookup. The lookup is performed 841*ec727ea7Spatrick // by the given Name, and in the global scope. The summary will be attached 842*ec727ea7Spatrick // to the found FunctionDecl only if the signatures match. 843*ec727ea7Spatrick void operator()(StringRef Name, Summary S) { 844*ec727ea7Spatrick IdentifierInfo &II = ACtx.Idents.get(Name); 845*ec727ea7Spatrick auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); 846*ec727ea7Spatrick if (LookupRes.size() == 0) 847*ec727ea7Spatrick return; 848*ec727ea7Spatrick for (Decl *D : LookupRes) { 849*ec727ea7Spatrick if (auto *FD = dyn_cast<FunctionDecl>(D)) { 850*ec727ea7Spatrick if (S.matchesAndSet(FD)) { 851*ec727ea7Spatrick auto Res = Map.insert({FD->getCanonicalDecl(), S}); 852*ec727ea7Spatrick assert(Res.second && "Function already has a summary set!"); 853*ec727ea7Spatrick (void)Res; 854*ec727ea7Spatrick if (DisplayLoadedSummaries) { 855*ec727ea7Spatrick llvm::errs() << "Loaded summary for: "; 856*ec727ea7Spatrick FD->print(llvm::errs()); 857*ec727ea7Spatrick llvm::errs() << "\n"; 858*ec727ea7Spatrick } 859*ec727ea7Spatrick return; 860*ec727ea7Spatrick } 861*ec727ea7Spatrick } 862*ec727ea7Spatrick } 863*ec727ea7Spatrick } 864*ec727ea7Spatrick // Add several summaries for the given name. 865*ec727ea7Spatrick void operator()(StringRef Name, const std::vector<Summary> &Summaries) { 866*ec727ea7Spatrick for (const Summary &S : Summaries) 867*ec727ea7Spatrick operator()(Name, S); 868*ec727ea7Spatrick } 869*ec727ea7Spatrick } addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries); 870e5dd7070Spatrick 871e5dd7070Spatrick // We are finally ready to define specifications for all supported functions. 872e5dd7070Spatrick // 873e5dd7070Spatrick // The signature needs to have the correct number of arguments. 874e5dd7070Spatrick // However, we insert `Irrelevant' when the type is insignificant. 875e5dd7070Spatrick // 876e5dd7070Spatrick // Argument ranges should always cover all variants. If return value 877e5dd7070Spatrick // is completely unknown, omit it from the respective range set. 878e5dd7070Spatrick // 879e5dd7070Spatrick // All types in the spec need to be canonical. 880e5dd7070Spatrick // 881e5dd7070Spatrick // Every item in the list of range sets represents a particular 882e5dd7070Spatrick // execution path the analyzer would need to explore once 883e5dd7070Spatrick // the call is modeled - a new program state is constructed 884e5dd7070Spatrick // for every range set, and each range line in the range set 885e5dd7070Spatrick // corresponds to a specific constraint within this state. 886e5dd7070Spatrick // 887e5dd7070Spatrick // Upon comparing to another argument, the other argument is casted 888e5dd7070Spatrick // to the current argument's type. This avoids proper promotion but 889e5dd7070Spatrick // seems useful. For example, read() receives size_t argument, 890e5dd7070Spatrick // and its return value, which is of type ssize_t, cannot be greater 891e5dd7070Spatrick // than this argument. If we made a promotion, and the size argument 892e5dd7070Spatrick // is equal to, say, 10, then we'd impose a range of [0, 10] on the 893e5dd7070Spatrick // return value, however the correct range is [-1, 10]. 894e5dd7070Spatrick // 895e5dd7070Spatrick // Please update the list of functions in the header after editing! 896e5dd7070Spatrick 897*ec727ea7Spatrick // Below are helpers functions to create the summaries. 898*ec727ea7Spatrick auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, 899*ec727ea7Spatrick IntRangeVector Ranges) { 900*ec727ea7Spatrick return std::make_shared<RangeConstraint>(ArgN, Kind, Ranges); 901*ec727ea7Spatrick }; 902*ec727ea7Spatrick auto BufferSize = [](auto... Args) { 903*ec727ea7Spatrick return std::make_shared<BufferSizeConstraint>(Args...); 904*ec727ea7Spatrick }; 905*ec727ea7Spatrick struct { 906*ec727ea7Spatrick auto operator()(RangeKind Kind, IntRangeVector Ranges) { 907*ec727ea7Spatrick return std::make_shared<RangeConstraint>(Ret, Kind, Ranges); 908*ec727ea7Spatrick } 909*ec727ea7Spatrick auto operator()(BinaryOperator::Opcode Op, ArgNo OtherArgN) { 910*ec727ea7Spatrick return std::make_shared<ComparisonConstraint>(Ret, Op, OtherArgN); 911*ec727ea7Spatrick } 912*ec727ea7Spatrick } ReturnValueCondition; 913*ec727ea7Spatrick auto Range = [](RangeInt b, RangeInt e) { 914*ec727ea7Spatrick return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}}; 915*ec727ea7Spatrick }; 916*ec727ea7Spatrick auto SingleValue = [](RangeInt v) { 917*ec727ea7Spatrick return IntRangeVector{std::pair<RangeInt, RangeInt>{v, v}}; 918*ec727ea7Spatrick }; 919*ec727ea7Spatrick auto LessThanOrEq = BO_LE; 920*ec727ea7Spatrick auto NotNull = [&](ArgNo ArgN) { 921*ec727ea7Spatrick return std::make_shared<NotNullConstraint>(ArgN); 922*ec727ea7Spatrick }; 923e5dd7070Spatrick 924*ec727ea7Spatrick Optional<QualType> FileTy = lookupType("FILE", ACtx); 925*ec727ea7Spatrick Optional<QualType> FilePtrTy, FilePtrRestrictTy; 926*ec727ea7Spatrick if (FileTy) { 927*ec727ea7Spatrick // FILE * 928*ec727ea7Spatrick FilePtrTy = ACtx.getPointerType(*FileTy); 929*ec727ea7Spatrick // FILE *restrict 930*ec727ea7Spatrick FilePtrRestrictTy = 931*ec727ea7Spatrick ACtx.getLangOpts().C99 ? ACtx.getRestrictType(*FilePtrTy) : *FilePtrTy; 932*ec727ea7Spatrick } 933*ec727ea7Spatrick 934*ec727ea7Spatrick using RetType = QualType; 935*ec727ea7Spatrick // Templates for summaries that are reused by many functions. 936*ec727ea7Spatrick auto Getc = [&]() { 937*ec727ea7Spatrick return Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall) 938*ec727ea7Spatrick .Case({ReturnValueCondition(WithinRange, 939*ec727ea7Spatrick {{EOFv, EOFv}, {0, UCharRangeMax}})}); 940*ec727ea7Spatrick }; 941*ec727ea7Spatrick auto Read = [&](RetType R, RangeInt Max) { 942*ec727ea7Spatrick return Summary(ArgTypes{Irrelevant, Irrelevant, SizeTy}, RetType{R}, 943*ec727ea7Spatrick NoEvalCall) 944*ec727ea7Spatrick .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), 945*ec727ea7Spatrick ReturnValueCondition(WithinRange, Range(-1, Max))}); 946*ec727ea7Spatrick }; 947*ec727ea7Spatrick auto Fread = [&]() { 948*ec727ea7Spatrick return Summary( 949*ec727ea7Spatrick ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, *FilePtrRestrictTy}, 950*ec727ea7Spatrick RetType{SizeTy}, NoEvalCall) 951*ec727ea7Spatrick .Case({ 952*ec727ea7Spatrick ReturnValueCondition(LessThanOrEq, ArgNo(2)), 953*ec727ea7Spatrick }) 954*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))); 955*ec727ea7Spatrick }; 956*ec727ea7Spatrick auto Fwrite = [&]() { 957*ec727ea7Spatrick return Summary(ArgTypes{ConstVoidPtrRestrictTy, SizeTy, SizeTy, 958*ec727ea7Spatrick *FilePtrRestrictTy}, 959*ec727ea7Spatrick RetType{SizeTy}, NoEvalCall) 960*ec727ea7Spatrick .Case({ 961*ec727ea7Spatrick ReturnValueCondition(LessThanOrEq, ArgNo(2)), 962*ec727ea7Spatrick }) 963*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))); 964*ec727ea7Spatrick }; 965*ec727ea7Spatrick auto Getline = [&](RetType R, RangeInt Max) { 966*ec727ea7Spatrick return Summary(ArgTypes{Irrelevant, Irrelevant, Irrelevant}, RetType{R}, 967*ec727ea7Spatrick NoEvalCall) 968*ec727ea7Spatrick .Case({ReturnValueCondition(WithinRange, {{-1, -1}, {1, Max}})}); 969*ec727ea7Spatrick }; 970*ec727ea7Spatrick 971e5dd7070Spatrick // The isascii() family of functions. 972*ec727ea7Spatrick // The behavior is undefined if the value of the argument is not 973*ec727ea7Spatrick // representable as unsigned char or is not equal to EOF. See e.g. C99 974*ec727ea7Spatrick // 7.4.1.2 The isalpha function (p: 181-182). 975*ec727ea7Spatrick addToFunctionSummaryMap( 976*ec727ea7Spatrick "isalnum", 977*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 978*ec727ea7Spatrick // Boils down to isupper() or islower() or isdigit(). 979*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, 980*ec727ea7Spatrick {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}}), 981*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 982*ec727ea7Spatrick // The locale-specific range. 983e5dd7070Spatrick // No post-condition. We are completely unaware of 984e5dd7070Spatrick // locale-specific return values. 985*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) 986*ec727ea7Spatrick .Case( 987*ec727ea7Spatrick {ArgumentCondition( 988*ec727ea7Spatrick 0U, OutOfRange, 989*ec727ea7Spatrick {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), 990*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))}) 991*ec727ea7Spatrick .ArgConstraint(ArgumentCondition( 992*ec727ea7Spatrick 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); 993*ec727ea7Spatrick addToFunctionSummaryMap( 994*ec727ea7Spatrick "isalpha", 995*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 996*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, {{'A', 'Z'}, {'a', 'z'}}), 997*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 998*ec727ea7Spatrick // The locale-specific range. 999*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) 1000*ec727ea7Spatrick .Case({ArgumentCondition( 1001*ec727ea7Spatrick 0U, OutOfRange, 1002*ec727ea7Spatrick {{'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), 1003*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1004*ec727ea7Spatrick addToFunctionSummaryMap( 1005*ec727ea7Spatrick "isascii", 1006*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1007*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, Range(0, 127)), 1008*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 1009*ec727ea7Spatrick .Case({ArgumentCondition(0U, OutOfRange, Range(0, 127)), 1010*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1011*ec727ea7Spatrick addToFunctionSummaryMap( 1012*ec727ea7Spatrick "isblank", 1013*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1014*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, {{'\t', '\t'}, {' ', ' '}}), 1015*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 1016*ec727ea7Spatrick .Case({ArgumentCondition(0U, OutOfRange, {{'\t', '\t'}, {' ', ' '}}), 1017*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1018*ec727ea7Spatrick addToFunctionSummaryMap( 1019*ec727ea7Spatrick "iscntrl", 1020*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1021*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, {{0, 32}, {127, 127}}), 1022*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 1023*ec727ea7Spatrick .Case({ArgumentCondition(0U, OutOfRange, {{0, 32}, {127, 127}}), 1024*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1025*ec727ea7Spatrick addToFunctionSummaryMap( 1026*ec727ea7Spatrick "isdigit", 1027*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1028*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, Range('0', '9')), 1029*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 1030*ec727ea7Spatrick .Case({ArgumentCondition(0U, OutOfRange, Range('0', '9')), 1031*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1032*ec727ea7Spatrick addToFunctionSummaryMap( 1033*ec727ea7Spatrick "isgraph", 1034*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1035*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, Range(33, 126)), 1036*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 1037*ec727ea7Spatrick .Case({ArgumentCondition(0U, OutOfRange, Range(33, 126)), 1038*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1039*ec727ea7Spatrick addToFunctionSummaryMap( 1040*ec727ea7Spatrick "islower", 1041*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1042*ec727ea7Spatrick // Is certainly lowercase. 1043*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, Range('a', 'z')), 1044*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 1045*ec727ea7Spatrick // Is ascii but not lowercase. 1046*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, Range(0, 127)), 1047*ec727ea7Spatrick ArgumentCondition(0U, OutOfRange, Range('a', 'z')), 1048*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))}) 1049*ec727ea7Spatrick // The locale-specific range. 1050*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) 1051*ec727ea7Spatrick // Is not an unsigned char. 1052*ec727ea7Spatrick .Case({ArgumentCondition(0U, OutOfRange, Range(0, UCharRangeMax)), 1053*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1054*ec727ea7Spatrick addToFunctionSummaryMap( 1055*ec727ea7Spatrick "isprint", 1056*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1057*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, Range(32, 126)), 1058*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 1059*ec727ea7Spatrick .Case({ArgumentCondition(0U, OutOfRange, Range(32, 126)), 1060*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1061*ec727ea7Spatrick addToFunctionSummaryMap( 1062*ec727ea7Spatrick "ispunct", 1063*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1064*ec727ea7Spatrick .Case({ArgumentCondition( 1065*ec727ea7Spatrick 0U, WithinRange, 1066*ec727ea7Spatrick {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), 1067*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 1068*ec727ea7Spatrick .Case({ArgumentCondition( 1069*ec727ea7Spatrick 0U, OutOfRange, 1070*ec727ea7Spatrick {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), 1071*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1072*ec727ea7Spatrick addToFunctionSummaryMap( 1073*ec727ea7Spatrick "isspace", 1074*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1075*ec727ea7Spatrick // Space, '\f', '\n', '\r', '\t', '\v'. 1076*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, {{9, 13}, {' ', ' '}}), 1077*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 1078*ec727ea7Spatrick // The locale-specific range. 1079*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) 1080*ec727ea7Spatrick .Case({ArgumentCondition(0U, OutOfRange, 1081*ec727ea7Spatrick {{9, 13}, {' ', ' '}, {128, UCharRangeMax}}), 1082*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1083*ec727ea7Spatrick addToFunctionSummaryMap( 1084*ec727ea7Spatrick "isupper", 1085*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1086*ec727ea7Spatrick // Is certainly uppercase. 1087*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, Range('A', 'Z')), 1088*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 1089*ec727ea7Spatrick // The locale-specific range. 1090*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) 1091*ec727ea7Spatrick // Other. 1092*ec727ea7Spatrick .Case({ArgumentCondition(0U, OutOfRange, 1093*ec727ea7Spatrick {{'A', 'Z'}, {128, UCharRangeMax}}), 1094*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1095*ec727ea7Spatrick addToFunctionSummaryMap( 1096*ec727ea7Spatrick "isxdigit", 1097*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1098*ec727ea7Spatrick .Case({ArgumentCondition(0U, WithinRange, 1099*ec727ea7Spatrick {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), 1100*ec727ea7Spatrick ReturnValueCondition(OutOfRange, SingleValue(0))}) 1101*ec727ea7Spatrick .Case({ArgumentCondition(0U, OutOfRange, 1102*ec727ea7Spatrick {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), 1103*ec727ea7Spatrick ReturnValueCondition(WithinRange, SingleValue(0))})); 1104e5dd7070Spatrick 1105e5dd7070Spatrick // The getc() family of functions that returns either a char or an EOF. 1106*ec727ea7Spatrick if (FilePtrTy) { 1107*ec727ea7Spatrick addToFunctionSummaryMap("getc", Getc()); 1108*ec727ea7Spatrick addToFunctionSummaryMap("fgetc", Getc()); 1109*ec727ea7Spatrick } 1110*ec727ea7Spatrick addToFunctionSummaryMap( 1111*ec727ea7Spatrick "getchar", Summary(ArgTypes{}, RetType{IntTy}, NoEvalCall) 1112*ec727ea7Spatrick .Case({ReturnValueCondition( 1113*ec727ea7Spatrick WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})})); 1114e5dd7070Spatrick 1115e5dd7070Spatrick // read()-like functions that never return more than buffer size. 1116*ec727ea7Spatrick if (FilePtrRestrictTy) { 1117*ec727ea7Spatrick addToFunctionSummaryMap("fread", Fread()); 1118*ec727ea7Spatrick addToFunctionSummaryMap("fwrite", Fwrite()); 1119*ec727ea7Spatrick } 1120*ec727ea7Spatrick 1121*ec727ea7Spatrick // We are not sure how ssize_t is defined on every platform, so we 1122*ec727ea7Spatrick // provide three variants that should cover common cases. 1123*ec727ea7Spatrick // FIXME these are actually defined by POSIX and not by the C standard, we 1124*ec727ea7Spatrick // should handle them together with the rest of the POSIX functions. 1125*ec727ea7Spatrick addToFunctionSummaryMap("read", {Read(IntTy, IntMax), Read(LongTy, LongMax), 1126*ec727ea7Spatrick Read(LongLongTy, LongLongMax)}); 1127*ec727ea7Spatrick addToFunctionSummaryMap("write", {Read(IntTy, IntMax), Read(LongTy, LongMax), 1128*ec727ea7Spatrick Read(LongLongTy, LongLongMax)}); 1129e5dd7070Spatrick 1130e5dd7070Spatrick // getline()-like functions either fail or read at least the delimiter. 1131*ec727ea7Spatrick // FIXME these are actually defined by POSIX and not by the C standard, we 1132*ec727ea7Spatrick // should handle them together with the rest of the POSIX functions. 1133*ec727ea7Spatrick addToFunctionSummaryMap("getline", 1134*ec727ea7Spatrick {Getline(IntTy, IntMax), Getline(LongTy, LongMax), 1135*ec727ea7Spatrick Getline(LongLongTy, LongLongMax)}); 1136*ec727ea7Spatrick addToFunctionSummaryMap("getdelim", 1137*ec727ea7Spatrick {Getline(IntTy, IntMax), Getline(LongTy, LongMax), 1138*ec727ea7Spatrick Getline(LongLongTy, LongLongMax)}); 1139*ec727ea7Spatrick 1140*ec727ea7Spatrick if (ModelPOSIX) { 1141*ec727ea7Spatrick 1142*ec727ea7Spatrick // long a64l(const char *str64); 1143*ec727ea7Spatrick addToFunctionSummaryMap( 1144*ec727ea7Spatrick "a64l", Summary(ArgTypes{ConstCharPtrTy}, RetType{LongTy}, NoEvalCall) 1145*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1146*ec727ea7Spatrick 1147*ec727ea7Spatrick // char *l64a(long value); 1148*ec727ea7Spatrick addToFunctionSummaryMap( 1149*ec727ea7Spatrick "l64a", Summary(ArgTypes{LongTy}, RetType{CharPtrTy}, NoEvalCall) 1150*ec727ea7Spatrick .ArgConstraint( 1151*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, LongMax)))); 1152*ec727ea7Spatrick 1153*ec727ea7Spatrick // int access(const char *pathname, int amode); 1154*ec727ea7Spatrick addToFunctionSummaryMap("access", Summary(ArgTypes{ConstCharPtrTy, IntTy}, 1155*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1156*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1157*ec727ea7Spatrick 1158*ec727ea7Spatrick // int faccessat(int dirfd, const char *pathname, int mode, int flags); 1159*ec727ea7Spatrick addToFunctionSummaryMap( 1160*ec727ea7Spatrick "faccessat", Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy}, 1161*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1162*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1163*ec727ea7Spatrick 1164*ec727ea7Spatrick // int dup(int fildes); 1165*ec727ea7Spatrick addToFunctionSummaryMap( 1166*ec727ea7Spatrick "dup", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall) 1167*ec727ea7Spatrick .ArgConstraint( 1168*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, IntMax)))); 1169*ec727ea7Spatrick 1170*ec727ea7Spatrick // int dup2(int fildes1, int filedes2); 1171*ec727ea7Spatrick addToFunctionSummaryMap( 1172*ec727ea7Spatrick "dup2", 1173*ec727ea7Spatrick Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, NoEvalCall) 1174*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) 1175*ec727ea7Spatrick .ArgConstraint( 1176*ec727ea7Spatrick ArgumentCondition(1, WithinRange, Range(0, IntMax)))); 1177*ec727ea7Spatrick 1178*ec727ea7Spatrick // int fdatasync(int fildes); 1179*ec727ea7Spatrick addToFunctionSummaryMap( 1180*ec727ea7Spatrick "fdatasync", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall) 1181*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0, WithinRange, 1182*ec727ea7Spatrick Range(0, IntMax)))); 1183*ec727ea7Spatrick 1184*ec727ea7Spatrick // int fnmatch(const char *pattern, const char *string, int flags); 1185*ec727ea7Spatrick addToFunctionSummaryMap( 1186*ec727ea7Spatrick "fnmatch", Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy}, 1187*ec727ea7Spatrick RetType{IntTy}, EvalCallAsPure) 1188*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1189*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1190*ec727ea7Spatrick 1191*ec727ea7Spatrick // int fsync(int fildes); 1192*ec727ea7Spatrick addToFunctionSummaryMap( 1193*ec727ea7Spatrick "fsync", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall) 1194*ec727ea7Spatrick .ArgConstraint( 1195*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, IntMax)))); 1196*ec727ea7Spatrick 1197*ec727ea7Spatrick Optional<QualType> Off_tTy = lookupType("off_t", ACtx); 1198*ec727ea7Spatrick 1199*ec727ea7Spatrick if (Off_tTy) 1200*ec727ea7Spatrick // int truncate(const char *path, off_t length); 1201*ec727ea7Spatrick addToFunctionSummaryMap("truncate", 1202*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrTy, *Off_tTy}, 1203*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1204*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1205*ec727ea7Spatrick 1206*ec727ea7Spatrick // int symlink(const char *oldpath, const char *newpath); 1207*ec727ea7Spatrick addToFunctionSummaryMap("symlink", 1208*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, 1209*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1210*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1211*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1212*ec727ea7Spatrick 1213*ec727ea7Spatrick // int symlinkat(const char *oldpath, int newdirfd, const char *newpath); 1214*ec727ea7Spatrick addToFunctionSummaryMap( 1215*ec727ea7Spatrick "symlinkat", 1216*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy}, RetType{IntTy}, 1217*ec727ea7Spatrick NoEvalCall) 1218*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1219*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, IntMax))) 1220*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(2)))); 1221*ec727ea7Spatrick 1222*ec727ea7Spatrick if (Off_tTy) 1223*ec727ea7Spatrick // int lockf(int fd, int cmd, off_t len); 1224*ec727ea7Spatrick addToFunctionSummaryMap( 1225*ec727ea7Spatrick "lockf", 1226*ec727ea7Spatrick Summary(ArgTypes{IntTy, IntTy, *Off_tTy}, RetType{IntTy}, NoEvalCall) 1227*ec727ea7Spatrick .ArgConstraint( 1228*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, IntMax)))); 1229*ec727ea7Spatrick 1230*ec727ea7Spatrick Optional<QualType> Mode_tTy = lookupType("mode_t", ACtx); 1231*ec727ea7Spatrick 1232*ec727ea7Spatrick if (Mode_tTy) 1233*ec727ea7Spatrick // int creat(const char *pathname, mode_t mode); 1234*ec727ea7Spatrick addToFunctionSummaryMap("creat", 1235*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy}, 1236*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1237*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1238*ec727ea7Spatrick 1239*ec727ea7Spatrick // unsigned int sleep(unsigned int seconds); 1240*ec727ea7Spatrick addToFunctionSummaryMap( 1241*ec727ea7Spatrick "sleep", 1242*ec727ea7Spatrick Summary(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}, NoEvalCall) 1243*ec727ea7Spatrick .ArgConstraint( 1244*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); 1245*ec727ea7Spatrick 1246*ec727ea7Spatrick Optional<QualType> DirTy = lookupType("DIR", ACtx); 1247*ec727ea7Spatrick Optional<QualType> DirPtrTy; 1248*ec727ea7Spatrick if (DirTy) 1249*ec727ea7Spatrick DirPtrTy = ACtx.getPointerType(*DirTy); 1250*ec727ea7Spatrick 1251*ec727ea7Spatrick if (DirPtrTy) 1252*ec727ea7Spatrick // int dirfd(DIR *dirp); 1253*ec727ea7Spatrick addToFunctionSummaryMap( 1254*ec727ea7Spatrick "dirfd", Summary(ArgTypes{*DirPtrTy}, RetType{IntTy}, NoEvalCall) 1255*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1256*ec727ea7Spatrick 1257*ec727ea7Spatrick // unsigned int alarm(unsigned int seconds); 1258*ec727ea7Spatrick addToFunctionSummaryMap( 1259*ec727ea7Spatrick "alarm", 1260*ec727ea7Spatrick Summary(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}, NoEvalCall) 1261*ec727ea7Spatrick .ArgConstraint( 1262*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); 1263*ec727ea7Spatrick 1264*ec727ea7Spatrick if (DirPtrTy) 1265*ec727ea7Spatrick // int closedir(DIR *dir); 1266*ec727ea7Spatrick addToFunctionSummaryMap( 1267*ec727ea7Spatrick "closedir", Summary(ArgTypes{*DirPtrTy}, RetType{IntTy}, NoEvalCall) 1268*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1269*ec727ea7Spatrick 1270*ec727ea7Spatrick // char *strdup(const char *s); 1271*ec727ea7Spatrick addToFunctionSummaryMap("strdup", Summary(ArgTypes{ConstCharPtrTy}, 1272*ec727ea7Spatrick RetType{CharPtrTy}, NoEvalCall) 1273*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1274*ec727ea7Spatrick 1275*ec727ea7Spatrick // char *strndup(const char *s, size_t n); 1276*ec727ea7Spatrick addToFunctionSummaryMap( 1277*ec727ea7Spatrick "strndup", Summary(ArgTypes{ConstCharPtrTy, SizeTy}, RetType{CharPtrTy}, 1278*ec727ea7Spatrick NoEvalCall) 1279*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1280*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(1, WithinRange, 1281*ec727ea7Spatrick Range(0, SizeMax)))); 1282*ec727ea7Spatrick 1283*ec727ea7Spatrick // wchar_t *wcsdup(const wchar_t *s); 1284*ec727ea7Spatrick addToFunctionSummaryMap("wcsdup", Summary(ArgTypes{ConstWchar_tPtrTy}, 1285*ec727ea7Spatrick RetType{Wchar_tPtrTy}, NoEvalCall) 1286*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1287*ec727ea7Spatrick 1288*ec727ea7Spatrick // int mkstemp(char *template); 1289*ec727ea7Spatrick addToFunctionSummaryMap( 1290*ec727ea7Spatrick "mkstemp", Summary(ArgTypes{CharPtrTy}, RetType{IntTy}, NoEvalCall) 1291*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1292*ec727ea7Spatrick 1293*ec727ea7Spatrick // char *mkdtemp(char *template); 1294*ec727ea7Spatrick addToFunctionSummaryMap( 1295*ec727ea7Spatrick "mkdtemp", Summary(ArgTypes{CharPtrTy}, RetType{CharPtrTy}, NoEvalCall) 1296*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1297*ec727ea7Spatrick 1298*ec727ea7Spatrick // char *getcwd(char *buf, size_t size); 1299*ec727ea7Spatrick addToFunctionSummaryMap( 1300*ec727ea7Spatrick "getcwd", 1301*ec727ea7Spatrick Summary(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}, NoEvalCall) 1302*ec727ea7Spatrick .ArgConstraint( 1303*ec727ea7Spatrick ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); 1304*ec727ea7Spatrick 1305*ec727ea7Spatrick if (Mode_tTy) { 1306*ec727ea7Spatrick // int mkdir(const char *pathname, mode_t mode); 1307*ec727ea7Spatrick addToFunctionSummaryMap("mkdir", 1308*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy}, 1309*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1310*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1311*ec727ea7Spatrick 1312*ec727ea7Spatrick // int mkdirat(int dirfd, const char *pathname, mode_t mode); 1313*ec727ea7Spatrick addToFunctionSummaryMap( 1314*ec727ea7Spatrick "mkdirat", Summary(ArgTypes{IntTy, ConstCharPtrTy, *Mode_tTy}, 1315*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1316*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1317*ec727ea7Spatrick } 1318*ec727ea7Spatrick 1319*ec727ea7Spatrick Optional<QualType> Dev_tTy = lookupType("dev_t", ACtx); 1320*ec727ea7Spatrick 1321*ec727ea7Spatrick if (Mode_tTy && Dev_tTy) { 1322*ec727ea7Spatrick // int mknod(const char *pathname, mode_t mode, dev_t dev); 1323*ec727ea7Spatrick addToFunctionSummaryMap( 1324*ec727ea7Spatrick "mknod", Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy, *Dev_tTy}, 1325*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1326*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1327*ec727ea7Spatrick 1328*ec727ea7Spatrick // int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev); 1329*ec727ea7Spatrick addToFunctionSummaryMap("mknodat", Summary(ArgTypes{IntTy, ConstCharPtrTy, 1330*ec727ea7Spatrick *Mode_tTy, *Dev_tTy}, 1331*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1332*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1333*ec727ea7Spatrick } 1334*ec727ea7Spatrick 1335*ec727ea7Spatrick if (Mode_tTy) { 1336*ec727ea7Spatrick // int chmod(const char *path, mode_t mode); 1337*ec727ea7Spatrick addToFunctionSummaryMap("chmod", 1338*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrTy, *Mode_tTy}, 1339*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1340*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1341*ec727ea7Spatrick 1342*ec727ea7Spatrick // int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags); 1343*ec727ea7Spatrick addToFunctionSummaryMap( 1344*ec727ea7Spatrick "fchmodat", Summary(ArgTypes{IntTy, ConstCharPtrTy, *Mode_tTy, IntTy}, 1345*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1346*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0, WithinRange, 1347*ec727ea7Spatrick Range(0, IntMax))) 1348*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1349*ec727ea7Spatrick 1350*ec727ea7Spatrick // int fchmod(int fildes, mode_t mode); 1351*ec727ea7Spatrick addToFunctionSummaryMap( 1352*ec727ea7Spatrick "fchmod", 1353*ec727ea7Spatrick Summary(ArgTypes{IntTy, *Mode_tTy}, RetType{IntTy}, NoEvalCall) 1354*ec727ea7Spatrick .ArgConstraint( 1355*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, IntMax)))); 1356*ec727ea7Spatrick } 1357*ec727ea7Spatrick 1358*ec727ea7Spatrick Optional<QualType> Uid_tTy = lookupType("uid_t", ACtx); 1359*ec727ea7Spatrick Optional<QualType> Gid_tTy = lookupType("gid_t", ACtx); 1360*ec727ea7Spatrick 1361*ec727ea7Spatrick if (Uid_tTy && Gid_tTy) { 1362*ec727ea7Spatrick // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, 1363*ec727ea7Spatrick // int flags); 1364*ec727ea7Spatrick addToFunctionSummaryMap( 1365*ec727ea7Spatrick "fchownat", 1366*ec727ea7Spatrick Summary(ArgTypes{IntTy, ConstCharPtrTy, *Uid_tTy, *Gid_tTy, IntTy}, 1367*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1368*ec727ea7Spatrick .ArgConstraint( 1369*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, IntMax))) 1370*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1371*ec727ea7Spatrick 1372*ec727ea7Spatrick // int chown(const char *path, uid_t owner, gid_t group); 1373*ec727ea7Spatrick addToFunctionSummaryMap( 1374*ec727ea7Spatrick "chown", Summary(ArgTypes{ConstCharPtrTy, *Uid_tTy, *Gid_tTy}, 1375*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1376*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1377*ec727ea7Spatrick 1378*ec727ea7Spatrick // int lchown(const char *path, uid_t owner, gid_t group); 1379*ec727ea7Spatrick addToFunctionSummaryMap( 1380*ec727ea7Spatrick "lchown", Summary(ArgTypes{ConstCharPtrTy, *Uid_tTy, *Gid_tTy}, 1381*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1382*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1383*ec727ea7Spatrick 1384*ec727ea7Spatrick // int fchown(int fildes, uid_t owner, gid_t group); 1385*ec727ea7Spatrick addToFunctionSummaryMap( 1386*ec727ea7Spatrick "fchown", Summary(ArgTypes{IntTy, *Uid_tTy, *Gid_tTy}, RetType{IntTy}, 1387*ec727ea7Spatrick NoEvalCall) 1388*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0, WithinRange, 1389*ec727ea7Spatrick Range(0, IntMax)))); 1390*ec727ea7Spatrick } 1391*ec727ea7Spatrick 1392*ec727ea7Spatrick // int rmdir(const char *pathname); 1393*ec727ea7Spatrick addToFunctionSummaryMap( 1394*ec727ea7Spatrick "rmdir", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall) 1395*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1396*ec727ea7Spatrick 1397*ec727ea7Spatrick // int chdir(const char *path); 1398*ec727ea7Spatrick addToFunctionSummaryMap( 1399*ec727ea7Spatrick "chdir", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall) 1400*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1401*ec727ea7Spatrick 1402*ec727ea7Spatrick // int link(const char *oldpath, const char *newpath); 1403*ec727ea7Spatrick addToFunctionSummaryMap("link", 1404*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, 1405*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1406*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1407*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1408*ec727ea7Spatrick 1409*ec727ea7Spatrick // int linkat(int fd1, const char *path1, int fd2, const char *path2, 1410*ec727ea7Spatrick // int flag); 1411*ec727ea7Spatrick addToFunctionSummaryMap( 1412*ec727ea7Spatrick "linkat", 1413*ec727ea7Spatrick Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy}, 1414*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1415*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) 1416*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1))) 1417*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(2, WithinRange, Range(0, IntMax))) 1418*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(3)))); 1419*ec727ea7Spatrick 1420*ec727ea7Spatrick // int unlink(const char *pathname); 1421*ec727ea7Spatrick addToFunctionSummaryMap( 1422*ec727ea7Spatrick "unlink", Summary(ArgTypes{ConstCharPtrTy}, RetType{IntTy}, NoEvalCall) 1423*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1424*ec727ea7Spatrick 1425*ec727ea7Spatrick // int unlinkat(int fd, const char *path, int flag); 1426*ec727ea7Spatrick addToFunctionSummaryMap( 1427*ec727ea7Spatrick "unlinkat", 1428*ec727ea7Spatrick Summary(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}, 1429*ec727ea7Spatrick NoEvalCall) 1430*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) 1431*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1432*ec727ea7Spatrick 1433*ec727ea7Spatrick Optional<QualType> StructStatTy = lookupType("stat", ACtx); 1434*ec727ea7Spatrick Optional<QualType> StructStatPtrTy, StructStatPtrRestrictTy; 1435*ec727ea7Spatrick if (StructStatTy) { 1436*ec727ea7Spatrick StructStatPtrTy = ACtx.getPointerType(*StructStatTy); 1437*ec727ea7Spatrick StructStatPtrRestrictTy = ACtx.getLangOpts().C99 1438*ec727ea7Spatrick ? ACtx.getRestrictType(*StructStatPtrTy) 1439*ec727ea7Spatrick : *StructStatPtrTy; 1440*ec727ea7Spatrick } 1441*ec727ea7Spatrick 1442*ec727ea7Spatrick if (StructStatPtrTy) 1443*ec727ea7Spatrick // int fstat(int fd, struct stat *statbuf); 1444*ec727ea7Spatrick addToFunctionSummaryMap( 1445*ec727ea7Spatrick "fstat", 1446*ec727ea7Spatrick Summary(ArgTypes{IntTy, *StructStatPtrTy}, RetType{IntTy}, NoEvalCall) 1447*ec727ea7Spatrick .ArgConstraint( 1448*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, IntMax))) 1449*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1450*ec727ea7Spatrick 1451*ec727ea7Spatrick if (StructStatPtrRestrictTy) { 1452*ec727ea7Spatrick // int stat(const char *restrict path, struct stat *restrict buf); 1453*ec727ea7Spatrick addToFunctionSummaryMap( 1454*ec727ea7Spatrick "stat", 1455*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrRestrictTy, *StructStatPtrRestrictTy}, 1456*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1457*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1458*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1459*ec727ea7Spatrick 1460*ec727ea7Spatrick // int lstat(const char *restrict path, struct stat *restrict buf); 1461*ec727ea7Spatrick addToFunctionSummaryMap( 1462*ec727ea7Spatrick "lstat", 1463*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrRestrictTy, *StructStatPtrRestrictTy}, 1464*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1465*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1466*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1467*ec727ea7Spatrick 1468*ec727ea7Spatrick // int fstatat(int fd, const char *restrict path, 1469*ec727ea7Spatrick // struct stat *restrict buf, int flag); 1470*ec727ea7Spatrick addToFunctionSummaryMap( 1471*ec727ea7Spatrick "fstatat", Summary(ArgTypes{IntTy, ConstCharPtrRestrictTy, 1472*ec727ea7Spatrick *StructStatPtrRestrictTy, IntTy}, 1473*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1474*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0, WithinRange, 1475*ec727ea7Spatrick Range(0, IntMax))) 1476*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1))) 1477*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(2)))); 1478*ec727ea7Spatrick } 1479*ec727ea7Spatrick 1480*ec727ea7Spatrick if (DirPtrTy) { 1481*ec727ea7Spatrick // DIR *opendir(const char *name); 1482*ec727ea7Spatrick addToFunctionSummaryMap("opendir", Summary(ArgTypes{ConstCharPtrTy}, 1483*ec727ea7Spatrick RetType{*DirPtrTy}, NoEvalCall) 1484*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1485*ec727ea7Spatrick 1486*ec727ea7Spatrick // DIR *fdopendir(int fd); 1487*ec727ea7Spatrick addToFunctionSummaryMap( 1488*ec727ea7Spatrick "fdopendir", Summary(ArgTypes{IntTy}, RetType{*DirPtrTy}, NoEvalCall) 1489*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0, WithinRange, 1490*ec727ea7Spatrick Range(0, IntMax)))); 1491*ec727ea7Spatrick } 1492*ec727ea7Spatrick 1493*ec727ea7Spatrick // int isatty(int fildes); 1494*ec727ea7Spatrick addToFunctionSummaryMap( 1495*ec727ea7Spatrick "isatty", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall) 1496*ec727ea7Spatrick .ArgConstraint( 1497*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, IntMax)))); 1498*ec727ea7Spatrick 1499*ec727ea7Spatrick if (FilePtrTy) { 1500*ec727ea7Spatrick // FILE *popen(const char *command, const char *type); 1501*ec727ea7Spatrick addToFunctionSummaryMap("popen", 1502*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, 1503*ec727ea7Spatrick RetType{*FilePtrTy}, NoEvalCall) 1504*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1505*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1506*ec727ea7Spatrick 1507*ec727ea7Spatrick // int pclose(FILE *stream); 1508*ec727ea7Spatrick addToFunctionSummaryMap( 1509*ec727ea7Spatrick "pclose", Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall) 1510*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1511*ec727ea7Spatrick } 1512*ec727ea7Spatrick 1513*ec727ea7Spatrick // int close(int fildes); 1514*ec727ea7Spatrick addToFunctionSummaryMap( 1515*ec727ea7Spatrick "close", Summary(ArgTypes{IntTy}, RetType{IntTy}, NoEvalCall) 1516*ec727ea7Spatrick .ArgConstraint( 1517*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, IntMax)))); 1518*ec727ea7Spatrick 1519*ec727ea7Spatrick // long fpathconf(int fildes, int name); 1520*ec727ea7Spatrick addToFunctionSummaryMap( 1521*ec727ea7Spatrick "fpathconf", 1522*ec727ea7Spatrick Summary(ArgTypes{IntTy, IntTy}, RetType{LongTy}, NoEvalCall) 1523*ec727ea7Spatrick .ArgConstraint( 1524*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, IntMax)))); 1525*ec727ea7Spatrick 1526*ec727ea7Spatrick // long pathconf(const char *path, int name); 1527*ec727ea7Spatrick addToFunctionSummaryMap("pathconf", Summary(ArgTypes{ConstCharPtrTy, IntTy}, 1528*ec727ea7Spatrick RetType{LongTy}, NoEvalCall) 1529*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1530*ec727ea7Spatrick 1531*ec727ea7Spatrick if (FilePtrTy) 1532*ec727ea7Spatrick // FILE *fdopen(int fd, const char *mode); 1533*ec727ea7Spatrick addToFunctionSummaryMap( 1534*ec727ea7Spatrick "fdopen", Summary(ArgTypes{IntTy, ConstCharPtrTy}, 1535*ec727ea7Spatrick RetType{*FilePtrTy}, NoEvalCall) 1536*ec727ea7Spatrick .ArgConstraint( 1537*ec727ea7Spatrick ArgumentCondition(0, WithinRange, Range(0, IntMax))) 1538*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1539*ec727ea7Spatrick 1540*ec727ea7Spatrick if (DirPtrTy) { 1541*ec727ea7Spatrick // void rewinddir(DIR *dir); 1542*ec727ea7Spatrick addToFunctionSummaryMap( 1543*ec727ea7Spatrick "rewinddir", Summary(ArgTypes{*DirPtrTy}, RetType{VoidTy}, NoEvalCall) 1544*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1545*ec727ea7Spatrick 1546*ec727ea7Spatrick // void seekdir(DIR *dirp, long loc); 1547*ec727ea7Spatrick addToFunctionSummaryMap("seekdir", Summary(ArgTypes{*DirPtrTy, LongTy}, 1548*ec727ea7Spatrick RetType{VoidTy}, NoEvalCall) 1549*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1550*ec727ea7Spatrick } 1551*ec727ea7Spatrick 1552*ec727ea7Spatrick // int rand_r(unsigned int *seedp); 1553*ec727ea7Spatrick addToFunctionSummaryMap("rand_r", Summary(ArgTypes{UnsignedIntPtrTy}, 1554*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1555*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1556*ec727ea7Spatrick 1557*ec727ea7Spatrick // int strcasecmp(const char *s1, const char *s2); 1558*ec727ea7Spatrick addToFunctionSummaryMap("strcasecmp", 1559*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, 1560*ec727ea7Spatrick RetType{IntTy}, EvalCallAsPure) 1561*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1562*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1563*ec727ea7Spatrick 1564*ec727ea7Spatrick // int strncasecmp(const char *s1, const char *s2, size_t n); 1565*ec727ea7Spatrick addToFunctionSummaryMap( 1566*ec727ea7Spatrick "strncasecmp", Summary(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, SizeTy}, 1567*ec727ea7Spatrick RetType{IntTy}, EvalCallAsPure) 1568*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1569*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1))) 1570*ec727ea7Spatrick .ArgConstraint(ArgumentCondition( 1571*ec727ea7Spatrick 2, WithinRange, Range(0, SizeMax)))); 1572*ec727ea7Spatrick 1573*ec727ea7Spatrick if (FilePtrTy && Off_tTy) { 1574*ec727ea7Spatrick 1575*ec727ea7Spatrick // int fileno(FILE *stream); 1576*ec727ea7Spatrick addToFunctionSummaryMap( 1577*ec727ea7Spatrick "fileno", Summary(ArgTypes{*FilePtrTy}, RetType{IntTy}, NoEvalCall) 1578*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1579*ec727ea7Spatrick 1580*ec727ea7Spatrick // int fseeko(FILE *stream, off_t offset, int whence); 1581*ec727ea7Spatrick addToFunctionSummaryMap("fseeko", 1582*ec727ea7Spatrick Summary(ArgTypes{*FilePtrTy, *Off_tTy, IntTy}, 1583*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1584*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1585*ec727ea7Spatrick 1586*ec727ea7Spatrick // off_t ftello(FILE *stream); 1587*ec727ea7Spatrick addToFunctionSummaryMap( 1588*ec727ea7Spatrick "ftello", Summary(ArgTypes{*FilePtrTy}, RetType{*Off_tTy}, NoEvalCall) 1589*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1590*ec727ea7Spatrick } 1591*ec727ea7Spatrick 1592*ec727ea7Spatrick if (Off_tTy) { 1593*ec727ea7Spatrick Optional<RangeInt> Off_tMax = BVF.getMaxValue(*Off_tTy).getLimitedValue(); 1594*ec727ea7Spatrick 1595*ec727ea7Spatrick // void *mmap(void *addr, size_t length, int prot, int flags, int fd, 1596*ec727ea7Spatrick // off_t offset); 1597*ec727ea7Spatrick addToFunctionSummaryMap( 1598*ec727ea7Spatrick "mmap", 1599*ec727ea7Spatrick Summary(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, *Off_tTy}, 1600*ec727ea7Spatrick RetType{VoidPtrTy}, NoEvalCall) 1601*ec727ea7Spatrick .ArgConstraint( 1602*ec727ea7Spatrick ArgumentCondition(1, WithinRange, Range(1, SizeMax))) 1603*ec727ea7Spatrick .ArgConstraint( 1604*ec727ea7Spatrick ArgumentCondition(4, WithinRange, Range(0, *Off_tMax)))); 1605*ec727ea7Spatrick } 1606*ec727ea7Spatrick 1607*ec727ea7Spatrick Optional<QualType> Off64_tTy = lookupType("off64_t", ACtx); 1608*ec727ea7Spatrick Optional<RangeInt> Off64_tMax; 1609*ec727ea7Spatrick if (Off64_tTy) { 1610*ec727ea7Spatrick Off64_tMax = BVF.getMaxValue(*Off_tTy).getLimitedValue(); 1611*ec727ea7Spatrick // void *mmap64(void *addr, size_t length, int prot, int flags, int fd, 1612*ec727ea7Spatrick // off64_t offset); 1613*ec727ea7Spatrick addToFunctionSummaryMap( 1614*ec727ea7Spatrick "mmap64", 1615*ec727ea7Spatrick Summary(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, *Off64_tTy}, 1616*ec727ea7Spatrick RetType{VoidPtrTy}, NoEvalCall) 1617*ec727ea7Spatrick .ArgConstraint( 1618*ec727ea7Spatrick ArgumentCondition(1, WithinRange, Range(1, SizeMax))) 1619*ec727ea7Spatrick .ArgConstraint( 1620*ec727ea7Spatrick ArgumentCondition(4, WithinRange, Range(0, *Off64_tMax)))); 1621*ec727ea7Spatrick } 1622*ec727ea7Spatrick 1623*ec727ea7Spatrick // int pipe(int fildes[2]); 1624*ec727ea7Spatrick addToFunctionSummaryMap( 1625*ec727ea7Spatrick "pipe", Summary(ArgTypes{IntPtrTy}, RetType{IntTy}, NoEvalCall) 1626*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1627*ec727ea7Spatrick 1628*ec727ea7Spatrick if (Off_tTy) 1629*ec727ea7Spatrick // off_t lseek(int fildes, off_t offset, int whence); 1630*ec727ea7Spatrick addToFunctionSummaryMap( 1631*ec727ea7Spatrick "lseek", Summary(ArgTypes{IntTy, *Off_tTy, IntTy}, RetType{*Off_tTy}, 1632*ec727ea7Spatrick NoEvalCall) 1633*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0, WithinRange, 1634*ec727ea7Spatrick Range(0, IntMax)))); 1635*ec727ea7Spatrick 1636*ec727ea7Spatrick Optional<QualType> Ssize_tTy = lookupType("ssize_t", ACtx); 1637*ec727ea7Spatrick 1638*ec727ea7Spatrick if (Ssize_tTy) { 1639*ec727ea7Spatrick // ssize_t readlink(const char *restrict path, char *restrict buf, 1640*ec727ea7Spatrick // size_t bufsize); 1641*ec727ea7Spatrick addToFunctionSummaryMap( 1642*ec727ea7Spatrick "readlink", 1643*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, 1644*ec727ea7Spatrick RetType{*Ssize_tTy}, NoEvalCall) 1645*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1646*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1))) 1647*ec727ea7Spatrick .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), 1648*ec727ea7Spatrick /*BufSize=*/ArgNo(2))) 1649*ec727ea7Spatrick .ArgConstraint( 1650*ec727ea7Spatrick ArgumentCondition(2, WithinRange, Range(0, SizeMax)))); 1651*ec727ea7Spatrick 1652*ec727ea7Spatrick // ssize_t readlinkat(int fd, const char *restrict path, 1653*ec727ea7Spatrick // char *restrict buf, size_t bufsize); 1654*ec727ea7Spatrick addToFunctionSummaryMap( 1655*ec727ea7Spatrick "readlinkat", Summary(ArgTypes{IntTy, ConstCharPtrRestrictTy, 1656*ec727ea7Spatrick CharPtrRestrictTy, SizeTy}, 1657*ec727ea7Spatrick RetType{*Ssize_tTy}, NoEvalCall) 1658*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0, WithinRange, 1659*ec727ea7Spatrick Range(0, IntMax))) 1660*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1))) 1661*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(2))) 1662*ec727ea7Spatrick .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2), 1663*ec727ea7Spatrick /*BufSize=*/ArgNo(3))) 1664*ec727ea7Spatrick .ArgConstraint(ArgumentCondition( 1665*ec727ea7Spatrick 3, WithinRange, Range(0, SizeMax)))); 1666*ec727ea7Spatrick } 1667*ec727ea7Spatrick 1668*ec727ea7Spatrick // int renameat(int olddirfd, const char *oldpath, int newdirfd, const char 1669*ec727ea7Spatrick // *newpath); 1670*ec727ea7Spatrick addToFunctionSummaryMap("renameat", Summary(ArgTypes{IntTy, ConstCharPtrTy, 1671*ec727ea7Spatrick IntTy, ConstCharPtrTy}, 1672*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1673*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1))) 1674*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(3)))); 1675*ec727ea7Spatrick 1676*ec727ea7Spatrick // char *realpath(const char *restrict file_name, 1677*ec727ea7Spatrick // char *restrict resolved_name); 1678*ec727ea7Spatrick addToFunctionSummaryMap( 1679*ec727ea7Spatrick "realpath", Summary(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy}, 1680*ec727ea7Spatrick RetType{CharPtrTy}, NoEvalCall) 1681*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1682*ec727ea7Spatrick 1683*ec727ea7Spatrick QualType CharPtrConstPtr = ACtx.getPointerType(CharPtrTy.withConst()); 1684*ec727ea7Spatrick 1685*ec727ea7Spatrick // int execv(const char *path, char *const argv[]); 1686*ec727ea7Spatrick addToFunctionSummaryMap("execv", 1687*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, 1688*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1689*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1690*ec727ea7Spatrick 1691*ec727ea7Spatrick // int execvp(const char *file, char *const argv[]); 1692*ec727ea7Spatrick addToFunctionSummaryMap("execvp", 1693*ec727ea7Spatrick Summary(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, 1694*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1695*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1696*ec727ea7Spatrick 1697*ec727ea7Spatrick // int getopt(int argc, char * const argv[], const char *optstring); 1698*ec727ea7Spatrick addToFunctionSummaryMap( 1699*ec727ea7Spatrick "getopt", 1700*ec727ea7Spatrick Summary(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy}, 1701*ec727ea7Spatrick RetType{IntTy}, NoEvalCall) 1702*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) 1703*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1))) 1704*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(2)))); 1705*ec727ea7Spatrick } 1706*ec727ea7Spatrick 1707*ec727ea7Spatrick // Functions for testing. 1708*ec727ea7Spatrick if (ChecksEnabled[CK_StdCLibraryFunctionsTesterChecker]) { 1709*ec727ea7Spatrick addToFunctionSummaryMap( 1710*ec727ea7Spatrick "__two_constrained_args", 1711*ec727ea7Spatrick Summary(ArgTypes{IntTy, IntTy}, RetType{IntTy}, EvalCallAsPure) 1712*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(1))) 1713*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(1U, WithinRange, SingleValue(1)))); 1714*ec727ea7Spatrick addToFunctionSummaryMap( 1715*ec727ea7Spatrick "__arg_constrained_twice", 1716*ec727ea7Spatrick Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) 1717*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1))) 1718*ec727ea7Spatrick .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(2)))); 1719*ec727ea7Spatrick addToFunctionSummaryMap( 1720*ec727ea7Spatrick "__defaultparam", 1721*ec727ea7Spatrick Summary(ArgTypes{Irrelevant, IntTy}, RetType{IntTy}, EvalCallAsPure) 1722*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0)))); 1723*ec727ea7Spatrick addToFunctionSummaryMap("__variadic", 1724*ec727ea7Spatrick Summary(ArgTypes{VoidPtrTy, ConstCharPtrTy}, 1725*ec727ea7Spatrick RetType{IntTy}, EvalCallAsPure) 1726*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(0))) 1727*ec727ea7Spatrick .ArgConstraint(NotNull(ArgNo(1)))); 1728*ec727ea7Spatrick addToFunctionSummaryMap( 1729*ec727ea7Spatrick "__buf_size_arg_constraint", 1730*ec727ea7Spatrick Summary(ArgTypes{ConstVoidPtrTy, SizeTy}, RetType{IntTy}, 1731*ec727ea7Spatrick EvalCallAsPure) 1732*ec727ea7Spatrick .ArgConstraint( 1733*ec727ea7Spatrick BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1)))); 1734*ec727ea7Spatrick addToFunctionSummaryMap( 1735*ec727ea7Spatrick "__buf_size_arg_constraint_mul", 1736*ec727ea7Spatrick Summary(ArgTypes{ConstVoidPtrTy, SizeTy, SizeTy}, RetType{IntTy}, 1737*ec727ea7Spatrick EvalCallAsPure) 1738*ec727ea7Spatrick .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), 1739*ec727ea7Spatrick /*BufSizeMultiplier=*/ArgNo(2)))); 1740*ec727ea7Spatrick } 1741e5dd7070Spatrick } 1742e5dd7070Spatrick 1743e5dd7070Spatrick void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) { 1744*ec727ea7Spatrick auto *Checker = mgr.registerChecker<StdLibraryFunctionsChecker>(); 1745*ec727ea7Spatrick Checker->DisplayLoadedSummaries = 1746*ec727ea7Spatrick mgr.getAnalyzerOptions().getCheckerBooleanOption( 1747*ec727ea7Spatrick Checker, "DisplayLoadedSummaries"); 1748*ec727ea7Spatrick Checker->ModelPOSIX = 1749*ec727ea7Spatrick mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "ModelPOSIX"); 1750e5dd7070Spatrick } 1751e5dd7070Spatrick 1752*ec727ea7Spatrick bool ento::shouldRegisterStdCLibraryFunctionsChecker(const CheckerManager &mgr) { 1753e5dd7070Spatrick return true; 1754e5dd7070Spatrick } 1755*ec727ea7Spatrick 1756*ec727ea7Spatrick #define REGISTER_CHECKER(name) \ 1757*ec727ea7Spatrick void ento::register##name(CheckerManager &mgr) { \ 1758*ec727ea7Spatrick StdLibraryFunctionsChecker *checker = \ 1759*ec727ea7Spatrick mgr.getChecker<StdLibraryFunctionsChecker>(); \ 1760*ec727ea7Spatrick checker->ChecksEnabled[StdLibraryFunctionsChecker::CK_##name] = true; \ 1761*ec727ea7Spatrick checker->CheckNames[StdLibraryFunctionsChecker::CK_##name] = \ 1762*ec727ea7Spatrick mgr.getCurrentCheckerName(); \ 1763*ec727ea7Spatrick } \ 1764*ec727ea7Spatrick \ 1765*ec727ea7Spatrick bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } 1766*ec727ea7Spatrick 1767*ec727ea7Spatrick REGISTER_CHECKER(StdCLibraryFunctionArgsChecker) 1768*ec727ea7Spatrick REGISTER_CHECKER(StdCLibraryFunctionsTesterChecker) 1769