diff options
Diffstat (limited to 'clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp')
| -rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp | 1065 | 
1 files changed, 1065 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp new file mode 100644 index 000000000000..2cdee8da375e --- /dev/null +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -0,0 +1,1065 @@ +//=== StdLibraryFunctionsChecker.cpp - Model standard functions -*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This checker improves modeling of a few simple library functions. +// It does not generate warnings. +// +// This checker provides a specification format - `FunctionSummaryTy' - and +// contains descriptions of some library functions in this format. Each +// specification contains a list of branches for splitting the program state +// upon call, and range constraints on argument and return-value symbols that +// are satisfied on each branch. This spec can be expanded to include more +// items, like external effects of the function. +// +// The main difference between this approach and the body farms technique is +// in more explicit control over how many branches are produced. For example, +// consider standard C function `ispunct(int x)', which returns a non-zero value +// iff `x' is a punctuation character, that is, when `x' is in range +//   ['!', '/']   [':', '@']  U  ['[', '\`']  U  ['{', '~']. +// `FunctionSummaryTy' provides only two branches for this function. However, +// any attempt to describe this range with if-statements in the body farm +// would result in many more branches. Because each branch needs to be analyzed +// independently, this significantly reduces performance. Additionally, +// once we consider a branch on which `x' is in range, say, ['!', '/'], +// we assume that such branch is an important separate path through the program, +// which may lead to false positives because considering this particular path +// was not consciously intended, and therefore it might have been unreachable. +// +// This checker uses eval::Call for modeling "pure" functions, for which +// their `FunctionSummaryTy' is a precise model. This avoids unnecessary +// invalidation passes. Conflicts with other checkers are unlikely because +// if the function has no other effects, other checkers would probably never +// want to improve upon the modeling done by this checker. +// +// Non-"pure" functions, for which only partial improvement over the default +// behavior is expected, are modeled via check::PostCall, non-intrusively. +// +// The following standard C functions are currently supported: +// +//   fgetc      getline   isdigit   isupper +//   fread      isalnum   isgraph   isxdigit +//   fwrite     isalpha   islower   read +//   getc       isascii   isprint   write +//   getchar    isblank   ispunct +//   getdelim   iscntrl   isspace +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace clang::ento; + +namespace { +class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> { +  /// Below is a series of typedefs necessary to define function specs. +  /// We avoid nesting types here because each additional qualifier +  /// would need to be repeated in every function spec. +  struct FunctionSummaryTy; + +  /// Specify how much the analyzer engine should entrust modeling this function +  /// to us. If he doesn't, he performs additional invalidations. +  enum InvalidationKindTy { NoEvalCall, EvalCallAsPure }; + +  /// A pair of ValueRangeKindTy and IntRangeVectorTy would describe a range +  /// imposed on a particular argument or return value symbol. +  /// +  /// Given a range, should the argument stay inside or outside this range? +  /// The special `ComparesToArgument' value indicates that we should +  /// impose a constraint that involves other argument or return value symbols. +  enum ValueRangeKindTy { OutOfRange, WithinRange, ComparesToArgument }; + +  // The universal integral type to use in value range descriptions. +  // Unsigned to make sure overflows are well-defined. +  typedef uint64_t RangeIntTy; + +  /// Normally, describes a single range constraint, eg. {{0, 1}, {3, 4}} is +  /// a non-negative integer, which less than 5 and not equal to 2. For +  /// `ComparesToArgument', holds information about how exactly to compare to +  /// the argument. +  typedef std::vector<std::pair<RangeIntTy, RangeIntTy>> IntRangeVectorTy; + +  /// A reference to an argument or return value by its number. +  /// ArgNo in CallExpr and CallEvent is defined as Unsigned, but +  /// obviously uint32_t should be enough for all practical purposes. +  typedef uint32_t ArgNoTy; +  static const ArgNoTy Ret = std::numeric_limits<ArgNoTy>::max(); + +  /// Incapsulates a single range on a single symbol within a branch. +  class ValueRange { +    ArgNoTy ArgNo; // Argument to which we apply the range. +    ValueRangeKindTy Kind; // Kind of range definition. +    IntRangeVectorTy Args; // Polymorphic arguments. + +  public: +    ValueRange(ArgNoTy ArgNo, ValueRangeKindTy Kind, +               const IntRangeVectorTy &Args) +        : ArgNo(ArgNo), Kind(Kind), Args(Args) {} + +    ArgNoTy getArgNo() const { return ArgNo; } +    ValueRangeKindTy getKind() const { return Kind; } + +    BinaryOperator::Opcode getOpcode() const { +      assert(Kind == ComparesToArgument); +      assert(Args.size() == 1); +      BinaryOperator::Opcode Op = +          static_cast<BinaryOperator::Opcode>(Args[0].first); +      assert(BinaryOperator::isComparisonOp(Op) && +             "Only comparison ops are supported for ComparesToArgument"); +      return Op; +    } + +    ArgNoTy getOtherArgNo() const { +      assert(Kind == ComparesToArgument); +      assert(Args.size() == 1); +      return static_cast<ArgNoTy>(Args[0].second); +    } + +    const IntRangeVectorTy &getRanges() const { +      assert(Kind != ComparesToArgument); +      return Args; +    } + +    // We avoid creating a virtual apply() method because +    // it makes initializer lists harder to write. +  private: +    ProgramStateRef +    applyAsOutOfRange(ProgramStateRef State, const CallEvent &Call, +                      const FunctionSummaryTy &Summary) const; +    ProgramStateRef +    applyAsWithinRange(ProgramStateRef State, const CallEvent &Call, +                       const FunctionSummaryTy &Summary) const; +    ProgramStateRef +    applyAsComparesToArgument(ProgramStateRef State, const CallEvent &Call, +                              const FunctionSummaryTy &Summary) const; + +  public: +    ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                          const FunctionSummaryTy &Summary) const { +      switch (Kind) { +      case OutOfRange: +        return applyAsOutOfRange(State, Call, Summary); +      case WithinRange: +        return applyAsWithinRange(State, Call, Summary); +      case ComparesToArgument: +        return applyAsComparesToArgument(State, Call, Summary); +      } +      llvm_unreachable("Unknown ValueRange kind!"); +    } +  }; + +  /// The complete list of ranges that defines a single branch. +  typedef std::vector<ValueRange> ValueRangeSet; + +  /// Includes information about function prototype (which is necessary to +  /// ensure we're modeling the right function and casting values properly), +  /// approach to invalidation, and a list of branches - essentially, a list +  /// of list of ranges - essentially, a list of lists of lists of segments. +  struct FunctionSummaryTy { +    const std::vector<QualType> ArgTypes; +    const QualType RetType; +    const InvalidationKindTy InvalidationKind; +    const std::vector<ValueRangeSet> Ranges; + +  private: +    static void assertTypeSuitableForSummary(QualType T) { +      assert(!T->isVoidType() && +             "We should have had no significant void types in the spec"); +      assert(T.isCanonical() && +             "We should only have canonical types in the spec"); +      // FIXME: lift this assert (but not the ones above!) +      assert(T->isIntegralOrEnumerationType() && +             "We only support integral ranges in the spec"); +    } + +  public: +    QualType getArgType(ArgNoTy ArgNo) const { +      QualType T = (ArgNo == Ret) ? RetType : ArgTypes[ArgNo]; +      assertTypeSuitableForSummary(T); +      return T; +    } + +    /// Try our best to figure out if the call expression is the call of +    /// *the* library function to which this specification applies. +    bool matchesCall(const CallExpr *CE) const; +  }; + +  // The same function (as in, function identifier) may have different +  // summaries assigned to it, with different argument and return value types. +  // We call these "variants" of the function. This can be useful for handling +  // C++ function overloads, and also it can be used when the same function +  // may have different definitions on different platforms. +  typedef std::vector<FunctionSummaryTy> FunctionVariantsTy; + +  // The map of all functions supported by the checker. It is initialized +  // lazily, and it doesn't change after initialization. +  typedef llvm::StringMap<FunctionVariantsTy> FunctionSummaryMapTy; +  mutable FunctionSummaryMapTy FunctionSummaryMap; + +  // Auxiliary functions to support ArgNoTy within all structures +  // in a unified manner. +  static QualType getArgType(const FunctionSummaryTy &Summary, ArgNoTy ArgNo) { +    return Summary.getArgType(ArgNo); +  } +  static QualType getArgType(const CallEvent &Call, ArgNoTy ArgNo) { +    return ArgNo == Ret ? Call.getResultType().getCanonicalType() +                        : Call.getArgExpr(ArgNo)->getType().getCanonicalType(); +  } +  static QualType getArgType(const CallExpr *CE, ArgNoTy ArgNo) { +    return ArgNo == Ret ? CE->getType().getCanonicalType() +                        : CE->getArg(ArgNo)->getType().getCanonicalType(); +  } +  static SVal getArgSVal(const CallEvent &Call, ArgNoTy ArgNo) { +    return ArgNo == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgNo); +  } + +public: +  void checkPostCall(const CallEvent &Call, CheckerContext &C) const; +  bool evalCall(const CallEvent &Call, CheckerContext &C) const; + +private: +  Optional<FunctionSummaryTy> findFunctionSummary(const FunctionDecl *FD, +                                          const CallExpr *CE, +                                          CheckerContext &C) const; + +  void initFunctionSummaries(BasicValueFactory &BVF) const; +}; +} // end of anonymous namespace + +ProgramStateRef StdLibraryFunctionsChecker::ValueRange::applyAsOutOfRange( +    ProgramStateRef State, const CallEvent &Call, +    const FunctionSummaryTy &Summary) const { + +  ProgramStateManager &Mgr = State->getStateManager(); +  SValBuilder &SVB = Mgr.getSValBuilder(); +  BasicValueFactory &BVF = SVB.getBasicValueFactory(); +  ConstraintManager &CM = Mgr.getConstraintManager(); +  QualType T = getArgType(Summary, getArgNo()); +  SVal V = getArgSVal(Call, getArgNo()); + +  if (auto N = V.getAs<NonLoc>()) { +    const IntRangeVectorTy &R = getRanges(); +    size_t E = R.size(); +    for (size_t I = 0; I != E; ++I) { +      const llvm::APSInt &Min = BVF.getValue(R[I].first, T); +      const llvm::APSInt &Max = BVF.getValue(R[I].second, T); +      assert(Min <= Max); +      State = CM.assumeInclusiveRange(State, *N, Min, Max, false); +      if (!State) +        break; +    } +  } + +  return State; +} + +ProgramStateRef +StdLibraryFunctionsChecker::ValueRange::applyAsWithinRange( +    ProgramStateRef State, const CallEvent &Call, +    const FunctionSummaryTy &Summary) const { + +  ProgramStateManager &Mgr = State->getStateManager(); +  SValBuilder &SVB = Mgr.getSValBuilder(); +  BasicValueFactory &BVF = SVB.getBasicValueFactory(); +  ConstraintManager &CM = Mgr.getConstraintManager(); +  QualType T = getArgType(Summary, getArgNo()); +  SVal V = getArgSVal(Call, getArgNo()); + +  // "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R". +  // We cut off [T_MIN, min(R) - 1] and [max(R) + 1, T_MAX] if necessary, +  // and then cut away all holes in R one by one. +  if (auto N = V.getAs<NonLoc>()) { +    const IntRangeVectorTy &R = getRanges(); +    size_t E = R.size(); + +    const llvm::APSInt &MinusInf = BVF.getMinValue(T); +    const llvm::APSInt &PlusInf = BVF.getMaxValue(T); + +    const llvm::APSInt &Left = BVF.getValue(R[0].first - 1ULL, T); +    if (Left != PlusInf) { +      assert(MinusInf <= Left); +      State = CM.assumeInclusiveRange(State, *N, MinusInf, Left, false); +      if (!State) +        return nullptr; +    } + +    const llvm::APSInt &Right = BVF.getValue(R[E - 1].second + 1ULL, T); +    if (Right != MinusInf) { +      assert(Right <= PlusInf); +      State = CM.assumeInclusiveRange(State, *N, Right, PlusInf, false); +      if (!State) +        return nullptr; +    } + +    for (size_t I = 1; I != E; ++I) { +      const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, T); +      const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, T); +      assert(Min <= Max); +      State = CM.assumeInclusiveRange(State, *N, Min, Max, false); +      if (!State) +        return nullptr; +    } +  } + +  return State; +} + +ProgramStateRef +StdLibraryFunctionsChecker::ValueRange::applyAsComparesToArgument( +    ProgramStateRef State, const CallEvent &Call, +    const FunctionSummaryTy &Summary) const { + +  ProgramStateManager &Mgr = State->getStateManager(); +  SValBuilder &SVB = Mgr.getSValBuilder(); +  QualType CondT = SVB.getConditionType(); +  QualType T = getArgType(Summary, getArgNo()); +  SVal V = getArgSVal(Call, getArgNo()); + +  BinaryOperator::Opcode Op = getOpcode(); +  ArgNoTy OtherArg = getOtherArgNo(); +  SVal OtherV = getArgSVal(Call, OtherArg); +  QualType OtherT = getArgType(Call, OtherArg); +  // Note: we avoid integral promotion for comparison. +  OtherV = SVB.evalCast(OtherV, T, OtherT); +  if (auto CompV = SVB.evalBinOp(State, Op, V, OtherV, CondT) +                       .getAs<DefinedOrUnknownSVal>()) +    State = State->assume(*CompV, true); +  return State; +} + +void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, +                                               CheckerContext &C) const { +  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); +  if (!FD) +    return; + +  const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); +  if (!CE) +    return; + +  Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C); +  if (!FoundSummary) +    return; + +  // Now apply ranges. +  const FunctionSummaryTy &Summary = *FoundSummary; +  ProgramStateRef State = C.getState(); + +  for (const auto &VRS: Summary.Ranges) { +    ProgramStateRef NewState = State; +    for (const auto &VR: VRS) { +      NewState = VR.apply(NewState, Call, Summary); +      if (!NewState) +        break; +    } + +    if (NewState && NewState != State) +      C.addTransition(NewState); +  } +} + +bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, +                                          CheckerContext &C) const { +  const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); +  if (!FD) +    return false; + +  const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); +  if (!CE) +    return false; + +  Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C); +  if (!FoundSummary) +    return false; + +  const FunctionSummaryTy &Summary = *FoundSummary; +  switch (Summary.InvalidationKind) { +  case EvalCallAsPure: { +    ProgramStateRef State = C.getState(); +    const LocationContext *LC = C.getLocationContext(); +    SVal V = C.getSValBuilder().conjureSymbolVal( +        CE, LC, CE->getType().getCanonicalType(), C.blockCount()); +    State = State->BindExpr(CE, LC, V); +    C.addTransition(State); +    return true; +  } +  case NoEvalCall: +    // Summary tells us to avoid performing eval::Call. The function is possibly +    // evaluated by another checker, or evaluated conservatively. +    return false; +  } +  llvm_unreachable("Unknown invalidation kind!"); +} + +bool StdLibraryFunctionsChecker::FunctionSummaryTy::matchesCall( +    const CallExpr *CE) const { +  // Check number of arguments: +  if (CE->getNumArgs() != ArgTypes.size()) +    return false; + +  // Check return type if relevant: +  if (!RetType.isNull() && RetType != CE->getType().getCanonicalType()) +    return false; + +  // Check argument types when relevant: +  for (size_t I = 0, E = ArgTypes.size(); I != E; ++I) { +    QualType FormalT = ArgTypes[I]; +    // Null type marks irrelevant arguments. +    if (FormalT.isNull()) +      continue; + +    assertTypeSuitableForSummary(FormalT); + +    QualType ActualT = StdLibraryFunctionsChecker::getArgType(CE, I); +    assert(ActualT.isCanonical()); +    if (ActualT != FormalT) +      return false; +  } + +  return true; +} + +Optional<StdLibraryFunctionsChecker::FunctionSummaryTy> +StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD, +                                                const CallExpr *CE, +                                                CheckerContext &C) const { +  // Note: we cannot always obtain FD from CE +  // (eg. virtual call, or call by pointer). +  assert(CE); + +  if (!FD) +    return None; + +  SValBuilder &SVB = C.getSValBuilder(); +  BasicValueFactory &BVF = SVB.getBasicValueFactory(); +  initFunctionSummaries(BVF); + +  IdentifierInfo *II = FD->getIdentifier(); +  if (!II) +    return None; +  StringRef Name = II->getName(); +  if (Name.empty() || !C.isCLibraryFunction(FD, Name)) +    return None; + +  auto FSMI = FunctionSummaryMap.find(Name); +  if (FSMI == FunctionSummaryMap.end()) +    return None; + +  // Verify that function signature matches the spec in advance. +  // Otherwise we might be modeling the wrong function. +  // Strict checking is important because we will be conducting +  // very integral-type-sensitive operations on arguments and +  // return values. +  const FunctionVariantsTy &SpecVariants = FSMI->second; +  for (const FunctionSummaryTy &Spec : SpecVariants) +    if (Spec.matchesCall(CE)) +      return Spec; + +  return None; +} + +void StdLibraryFunctionsChecker::initFunctionSummaries( +    BasicValueFactory &BVF) const { +  if (!FunctionSummaryMap.empty()) +    return; + +  ASTContext &ACtx = BVF.getContext(); + +  // These types are useful for writing specifications quickly, +  // New specifications should probably introduce more types. +  // Some types are hard to obtain from the AST, eg. "ssize_t". +  // In such cases it should be possible to provide multiple variants +  // of function summary for common cases (eg. ssize_t could be int or long +  // or long long, so three summary variants would be enough). +  // Of course, function variants are also useful for C++ overloads. +  QualType Irrelevant; // A placeholder, whenever we do not care about the type. +  QualType IntTy = ACtx.IntTy; +  QualType LongTy = ACtx.LongTy; +  QualType LongLongTy = ACtx.LongLongTy; +  QualType SizeTy = ACtx.getSizeType(); + +  RangeIntTy IntMax = BVF.getMaxValue(IntTy).getLimitedValue(); +  RangeIntTy LongMax = BVF.getMaxValue(LongTy).getLimitedValue(); +  RangeIntTy LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue(); + +  // We are finally ready to define specifications for all supported functions. +  // +  // The signature needs to have the correct number of arguments. +  // However, we insert `Irrelevant' when the type is insignificant. +  // +  // Argument ranges should always cover all variants. If return value +  // is completely unknown, omit it from the respective range set. +  // +  // All types in the spec need to be canonical. +  // +  // Every item in the list of range sets represents a particular +  // execution path the analyzer would need to explore once +  // the call is modeled - a new program state is constructed +  // for every range set, and each range line in the range set +  // corresponds to a specific constraint within this state. +  // +  // Upon comparing to another argument, the other argument is casted +  // to the current argument's type. This avoids proper promotion but +  // seems useful. For example, read() receives size_t argument, +  // and its return value, which is of type ssize_t, cannot be greater +  // than this argument. If we made a promotion, and the size argument +  // is equal to, say, 10, then we'd impose a range of [0, 10] on the +  // return value, however the correct range is [-1, 10]. +  // +  // Please update the list of functions in the header after editing! +  // +  // The format is as follows: +  // +  //{ "function name", +  //  { spec: +  //    { argument types list, ... }, +  //    return type, purity, { range set list: +  //      { range list: +  //        { argument index, within or out of, {{from, to}, ...} }, +  //        { argument index, compares to argument, {{how, which}} }, +  //        ... +  //      } +  //    } +  //  } +  //} + +#define SUMMARY_WITH_VARIANTS(identifier) {#identifier, { +#define END_SUMMARY_WITH_VARIANTS }}, +#define VARIANT(argument_types, return_type, invalidation_approach)            \ +  { argument_types, return_type, invalidation_approach, { +#define END_VARIANT } }, +#define SUMMARY(identifier, argument_types, return_type,                       \ +                invalidation_approach)                                         \ +  { #identifier, { { argument_types, return_type, invalidation_approach, { +#define END_SUMMARY } } } }, +#define ARGUMENT_TYPES(...) { __VA_ARGS__ } +#define RETURN_TYPE(x) x +#define INVALIDATION_APPROACH(x) x +#define CASE { +#define END_CASE }, +#define ARGUMENT_CONDITION(argument_number, condition_kind)                    \ +  { argument_number, condition_kind, { +#define END_ARGUMENT_CONDITION }}, +#define RETURN_VALUE_CONDITION(condition_kind)                                 \ +  { Ret, condition_kind, { +#define END_RETURN_VALUE_CONDITION }}, +#define ARG_NO(x) x##U +#define RANGE(x, y) { x, y }, +#define SINGLE_VALUE(x) RANGE(x, x) +#define IS_LESS_THAN(arg) { BO_LE, arg } + +  FunctionSummaryMap = { +    // The isascii() family of functions. +    SUMMARY(isalnum, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE // Boils down to isupper() or islower() or isdigit() +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE('0', '9') +          RANGE('A', 'Z') +          RANGE('a', 'z') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE // The locale-specific range. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE(128, 255) +        END_ARGUMENT_CONDITION +        // No post-condition. We are completely unaware of +        // locale-specific return values. +      END_CASE +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE('0', '9') +          RANGE('A', 'Z') +          RANGE('a', 'z') +          RANGE(128, 255) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(isalpha, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE // isupper() or islower(). Note that 'Z' is less than 'a'. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE('A', 'Z') +          RANGE('a', 'z') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE // The locale-specific range. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE(128, 255) +        END_ARGUMENT_CONDITION +      END_CASE +      CASE // Other. +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE('A', 'Z') +          RANGE('a', 'z') +          RANGE(128, 255) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(isascii, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE // Is ASCII. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE(0, 127) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE(0, 127) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(isblank, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          SINGLE_VALUE('\t') +          SINGLE_VALUE(' ') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          SINGLE_VALUE('\t') +          SINGLE_VALUE(' ') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(iscntrl, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE // 0..31 or 127 +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE(0, 32) +          SINGLE_VALUE(127) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE(0, 32) +          SINGLE_VALUE(127) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(isdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE // Is a digit. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE('0', '9') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE('0', '9') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(isgraph, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE(33, 126) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE(33, 126) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(islower, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE // Is certainly lowercase. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE('a', 'z') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE // Is ascii but not lowercase. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE(0, 127) +        END_ARGUMENT_CONDITION +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE('a', 'z') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE // The locale-specific range. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE(128, 255) +        END_ARGUMENT_CONDITION +      END_CASE +      CASE // Is not an unsigned char. +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE(0, 255) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(isprint, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE(32, 126) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE(32, 126) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(ispunct, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE('!', '/') +          RANGE(':', '@') +          RANGE('[', '`') +          RANGE('{', '~') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE('!', '/') +          RANGE(':', '@') +          RANGE('[', '`') +          RANGE('{', '~') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(isspace, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE // Space, '\f', '\n', '\r', '\t', '\v'. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE(9, 13) +          SINGLE_VALUE(' ') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE // The locale-specific range. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE(128, 255) +        END_ARGUMENT_CONDITION +      END_CASE +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE(9, 13) +          SINGLE_VALUE(' ') +          RANGE(128, 255) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(isupper, ARGUMENT_TYPES(IntTy), RETURN_TYPE (IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE // Is certainly uppercase. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE('A', 'Z') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE // The locale-specific range. +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE(128, 255) +        END_ARGUMENT_CONDITION +      END_CASE +      CASE // Other. +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE('A', 'Z') RANGE(128, 255) +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(isxdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(EvalCallAsPure)) +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), WithinRange) +          RANGE('0', '9') +          RANGE('A', 'F') +          RANGE('a', 'f') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(OutOfRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +      CASE +        ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) +          RANGE('0', '9') +          RANGE('A', 'F') +          RANGE('a', 'f') +        END_ARGUMENT_CONDITION +        RETURN_VALUE_CONDITION(WithinRange) +          SINGLE_VALUE(0) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY + +    // The getc() family of functions that returns either a char or an EOF. +    SUMMARY(getc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(NoEvalCall)) +      CASE // FIXME: EOF is assumed to be defined as -1. +        RETURN_VALUE_CONDITION(WithinRange) +          RANGE(-1, 255) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(fgetc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(NoEvalCall)) +      CASE // FIXME: EOF is assumed to be defined as -1. +        RETURN_VALUE_CONDITION(WithinRange) +          RANGE(-1, 255) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(getchar, ARGUMENT_TYPES(), RETURN_TYPE(IntTy), +            INVALIDATION_APPROACH(NoEvalCall)) +      CASE // FIXME: EOF is assumed to be defined as -1. +        RETURN_VALUE_CONDITION(WithinRange) +          RANGE(-1, 255) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY + +    // read()-like functions that never return more than buffer size. +    // We are not sure how ssize_t is defined on every platform, so we provide +    // three variants that should cover common cases. +    SUMMARY_WITH_VARIANTS(read) +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), +              RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(ComparesToArgument) +            IS_LESS_THAN(ARG_NO(2)) +          END_RETURN_VALUE_CONDITION +          RETURN_VALUE_CONDITION(WithinRange) +            RANGE(-1, IntMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), +              RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(ComparesToArgument) +            IS_LESS_THAN(ARG_NO(2)) +          END_RETURN_VALUE_CONDITION +          RETURN_VALUE_CONDITION(WithinRange) +            RANGE(-1, LongMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), +              RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(ComparesToArgument) +            IS_LESS_THAN(ARG_NO(2)) +          END_RETURN_VALUE_CONDITION +          RETURN_VALUE_CONDITION(WithinRange) +            RANGE(-1, LongLongMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +    END_SUMMARY_WITH_VARIANTS +    SUMMARY_WITH_VARIANTS(write) +      // Again, due to elusive nature of ssize_t, we have duplicate +      // our summaries to cover different variants. +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), +              RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(ComparesToArgument) +            IS_LESS_THAN(ARG_NO(2)) +          END_RETURN_VALUE_CONDITION +          RETURN_VALUE_CONDITION(WithinRange) +            RANGE(-1, IntMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), +              RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(ComparesToArgument) +            IS_LESS_THAN(ARG_NO(2)) +          END_RETURN_VALUE_CONDITION +          RETURN_VALUE_CONDITION(WithinRange) +            RANGE(-1, LongMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), +              RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(ComparesToArgument) +            IS_LESS_THAN(ARG_NO(2)) +          END_RETURN_VALUE_CONDITION +          RETURN_VALUE_CONDITION(WithinRange) +            RANGE(-1, LongLongMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +    END_SUMMARY_WITH_VARIANTS +    SUMMARY(fread, +            ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant), +            RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall)) +      CASE +        RETURN_VALUE_CONDITION(ComparesToArgument) +          IS_LESS_THAN(ARG_NO(2)) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY +    SUMMARY(fwrite, +            ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant), +            RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall)) +      CASE +        RETURN_VALUE_CONDITION(ComparesToArgument) +          IS_LESS_THAN(ARG_NO(2)) +        END_RETURN_VALUE_CONDITION +      END_CASE +    END_SUMMARY + +    // getline()-like functions either fail or read at least the delimiter. +    SUMMARY_WITH_VARIANTS(getline) +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), +              RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(WithinRange) +            SINGLE_VALUE(-1) +            RANGE(1, IntMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), +              RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(WithinRange) +            SINGLE_VALUE(-1) +            RANGE(1, LongMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), +              RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(WithinRange) +            SINGLE_VALUE(-1) +            RANGE(1, LongLongMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +    END_SUMMARY_WITH_VARIANTS +    SUMMARY_WITH_VARIANTS(getdelim) +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), +            RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(WithinRange) +            SINGLE_VALUE(-1) +            RANGE(1, IntMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), +            RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(WithinRange) +            SINGLE_VALUE(-1) +            RANGE(1, LongMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +      VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), +            RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) +        CASE +          RETURN_VALUE_CONDITION(WithinRange) +            SINGLE_VALUE(-1) +            RANGE(1, LongLongMax) +          END_RETURN_VALUE_CONDITION +        END_CASE +      END_VARIANT +    END_SUMMARY_WITH_VARIANTS +  }; +} + +void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) { +  // If this checker grows large enough to support C++, Objective-C, or other +  // standard libraries, we could use multiple register...Checker() functions, +  // which would register various checkers with the help of the same Checker +  // class, turning on different function summaries. +  mgr.registerChecker<StdLibraryFunctionsChecker>(); +} + +bool ento::shouldRegisterStdCLibraryFunctionsChecker(const LangOptions &LO) { +  return true; +}  | 
