diff options
Diffstat (limited to 'contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp')
| -rw-r--r-- | contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp | 3959 | 
1 files changed, 3959 insertions, 0 deletions
| diff --git a/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp new file mode 100644 index 000000000000..8f4bd17afc85 --- /dev/null +++ b/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -0,0 +1,3959 @@ +//=== 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. +// +// This checker provides a specification format - `Summary' - 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  ['{', '~']. +// `Summary' 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 (functions without +// side effects), for which their `Summary' 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. +// +//===----------------------------------------------------------------------===// + +#include "ErrnoModeling.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.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" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/FormatVariadic.h" + +#include <optional> +#include <string> + +using namespace clang; +using namespace clang::ento; + +namespace { +class StdLibraryFunctionsChecker +    : public Checker<check::PreCall, check::PostCall, eval::Call> { + +  class Summary; + +  /// Specify how much the analyzer engine should entrust modeling this function +  /// to us. +  enum InvalidationKind { +    /// No \c eval::Call for the function, it can be modeled elsewhere. +    /// This checker checks only pre and post conditions. +    NoEvalCall, +    /// The function is modeled completely in this checker. +    EvalCallAsPure +  }; + +  /// Given a range, should the argument stay inside or outside this range? +  enum RangeKind { OutOfRange, WithinRange }; + +  static RangeKind negateKind(RangeKind K) { +    switch (K) { +    case OutOfRange: +      return WithinRange; +    case WithinRange: +      return OutOfRange; +    } +    llvm_unreachable("Unknown range kind"); +  } + +  /// The universal integral type to use in value range descriptions. +  /// Unsigned to make sure overflows are well-defined. +  typedef uint64_t RangeInt; + +  /// Describes a single range constraint. Eg. {{0, 1}, {3, 4}} is +  /// a non-negative integer, which less than 5 and not equal to 2. +  typedef std::vector<std::pair<RangeInt, RangeInt>> IntRangeVector; + +  /// 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 ArgNo; +  /// Special argument number for specifying the return value. +  static const ArgNo Ret; + +  /// Get a string representation of an argument index. +  /// E.g.: (1) -> '1st arg', (2) - > '2nd arg' +  static void printArgDesc(ArgNo, llvm::raw_ostream &Out); +  /// Print value X of the argument in form " (which is X)", +  /// if the value is a fixed known value, otherwise print nothing. +  /// This is used as simple explanation of values if possible. +  static void printArgValueInfo(ArgNo ArgN, ProgramStateRef State, +                                const CallEvent &Call, llvm::raw_ostream &Out); +  /// Append textual description of a numeric range [RMin,RMax] to +  /// \p Out. +  static void appendInsideRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax, +                                    QualType ArgT, BasicValueFactory &BVF, +                                    llvm::raw_ostream &Out); +  /// Append textual description of a numeric range out of [RMin,RMax] to +  /// \p Out. +  static void appendOutOfRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax, +                                   QualType ArgT, BasicValueFactory &BVF, +                                   llvm::raw_ostream &Out); + +  class ValueConstraint; + +  /// Pointer to the ValueConstraint. We need a copyable, polymorphic and +  /// default initializable type (vector needs that). A raw pointer was good, +  /// however, we cannot default initialize that. unique_ptr makes the Summary +  /// class non-copyable, therefore not an option. Releasing the copyability +  /// requirement would render the initialization of the Summary map infeasible. +  /// Mind that a pointer to a new value constraint is created when the negate +  /// function is used. +  using ValueConstraintPtr = std::shared_ptr<ValueConstraint>; + +  /// Polymorphic base class that represents a constraint on a given argument +  /// (or return value) of a function. Derived classes implement different kind +  /// of constraints, e.g range constraints or correlation between two +  /// arguments. +  /// These are used as argument constraints (preconditions) of functions, in +  /// which case a bug report may be emitted if the constraint is not satisfied. +  /// Another use is as conditions for summary cases, to create different +  /// classes of behavior for a function. In this case no description of the +  /// constraint is needed because the summary cases have an own (not generated) +  /// description string. +  class ValueConstraint { +  public: +    ValueConstraint(ArgNo ArgN) : ArgN(ArgN) {} +    virtual ~ValueConstraint() {} + +    /// Apply the effects of the constraint on the given program state. If null +    /// is returned then the constraint is not feasible. +    virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                                  const Summary &Summary, +                                  CheckerContext &C) const = 0; + +    /// Represents that in which context do we require a description of the +    /// constraint. +    enum DescriptionKind { +      /// Describe a constraint that was violated. +      /// Description should start with something like "should be". +      Violation, +      /// Describe a constraint that was assumed to be true. +      /// This can be used when a precondition is satisfied, or when a summary +      /// case is applied. +      /// Description should start with something like "is". +      Assumption +    }; + +    /// Give a description that explains the constraint to the user. Used when +    /// a bug is reported or when the constraint is applied and displayed as a +    /// note. The description should not mention the argument (getArgNo). +    /// See StdLibraryFunctionsChecker::reportBug about how this function is +    /// used (this function is used not only there). +    virtual void describe(DescriptionKind DK, const CallEvent &Call, +                          ProgramStateRef State, const Summary &Summary, +                          llvm::raw_ostream &Out) const { +      // There are some descendant classes that are not used as argument +      // constraints, e.g. ComparisonConstraint. In that case we can safely +      // ignore the implementation of this function. +      llvm_unreachable( +          "Description not implemented for summary case constraints"); +    } + +    /// Give a description that explains the actual argument value (where the +    /// current ValueConstraint applies to) to the user. This function should be +    /// called only when the current constraint is satisfied by the argument. +    /// It should produce a more precise description than the constraint itself. +    /// The actual value of the argument and the program state can be used to +    /// make the description more precise. In the most simple case, if the +    /// argument has a fixed known value this value can be printed into \p Out, +    /// this is done by default. +    /// The function should return true if a description was printed to \p Out, +    /// otherwise false. +    /// See StdLibraryFunctionsChecker::reportBug about how this function is +    /// used. +    virtual bool describeArgumentValue(const CallEvent &Call, +                                       ProgramStateRef State, +                                       const Summary &Summary, +                                       llvm::raw_ostream &Out) const { +      if (auto N = getArgSVal(Call, getArgNo()).getAs<NonLoc>()) { +        if (const llvm::APSInt *Int = N->getAsInteger()) { +          Out << *Int; +          return true; +        } +      } +      return false; +    } + +    /// Return those arguments that should be tracked when we report a bug about +    /// argument constraint violation. By default it is the argument that is +    /// constrained, however, in some special cases we need to track other +    /// arguments as well. E.g. a buffer size might be encoded in another +    /// argument. +    /// The "return value" argument number can not occur as returned value. +    virtual std::vector<ArgNo> getArgsToTrack() const { return {ArgN}; } + +    /// Get a constraint that represents exactly the opposite of the current. +    virtual ValueConstraintPtr negate() const { +      llvm_unreachable("Not implemented"); +    }; + +    /// Check whether the constraint is malformed or not. It is malformed if the +    /// specified argument has a mismatch with the given FunctionDecl (e.g. the +    /// arg number is out-of-range of the function's argument list). +    /// This condition can indicate if a probably wrong or unexpected function +    /// was found where the constraint is to be applied. +    bool checkValidity(const FunctionDecl *FD) const { +      const bool ValidArg = ArgN == Ret || ArgN < FD->getNumParams(); +      assert(ValidArg && "Arg out of range!"); +      if (!ValidArg) +        return false; +      // Subclasses may further refine the validation. +      return checkSpecificValidity(FD); +    } + +    /// Return the argument number (may be placeholder for "return value"). +    ArgNo getArgNo() const { return ArgN; } + +  protected: +    /// Argument to which to apply the constraint. It can be a real argument of +    /// the function to check, or a special value to indicate the return value +    /// of the function. +    /// Every constraint is assigned to one main argument, even if other +    /// arguments are involved. +    ArgNo ArgN; + +    /// Do constraint-specific validation check. +    virtual bool checkSpecificValidity(const FunctionDecl *FD) const { +      return true; +    } +  }; + +  /// Check if a single argument falls into a specific "range". +  /// A range is formed as a set of intervals. +  /// E.g. \code {['A', 'Z'], ['a', 'z'], ['_', '_']} \endcode +  /// The intervals are closed intervals that contain one or more values. +  /// +  /// The default constructed RangeConstraint has an empty range, applying +  /// such constraint does not involve any assumptions, thus the State remains +  /// unchanged. This is meaningful, if the range is dependent on a looked up +  /// type (e.g. [0, Socklen_tMax]). If the type is not found, then the range +  /// is default initialized to be empty. +  class RangeConstraint : public ValueConstraint { +    /// The constraint can be specified by allowing or disallowing the range. +    /// WithinRange indicates allowing the range, OutOfRange indicates +    /// disallowing it (allowing the complementary range). +    RangeKind Kind; + +    /// A set of intervals. +    IntRangeVector Ranges; + +    /// A textual description of this constraint for the specific case where the +    /// constraint is used. If empty a generated description will be used that +    /// is built from the range of the constraint. +    StringRef Description; + +  public: +    RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges, +                    StringRef Desc = "") +        : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges), Description(Desc) { +    } + +    const IntRangeVector &getRanges() const { return Ranges; } + +    ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                          const Summary &Summary, +                          CheckerContext &C) const override; + +    void describe(DescriptionKind DK, const CallEvent &Call, +                  ProgramStateRef State, const Summary &Summary, +                  llvm::raw_ostream &Out) const override; + +    bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, +                               const Summary &Summary, +                               llvm::raw_ostream &Out) const override; + +    ValueConstraintPtr negate() const override { +      RangeConstraint Tmp(*this); +      Tmp.Kind = negateKind(Kind); +      return std::make_shared<RangeConstraint>(Tmp); +    } + +  protected: +    bool checkSpecificValidity(const FunctionDecl *FD) const override { +      const bool ValidArg = +          getArgType(FD, ArgN)->isIntegralType(FD->getASTContext()); +      assert(ValidArg && +             "This constraint should be applied on an integral type"); +      return ValidArg; +    } + +  private: +    /// A callback function that is used when iterating over the range +    /// intervals. It gets the begin and end (inclusive) of one interval. +    /// This is used to make any kind of task possible that needs an iteration +    /// over the intervals. +    using RangeApplyFunction = +        std::function<bool(const llvm::APSInt &Min, const llvm::APSInt &Max)>; + +    /// Call a function on the intervals of the range. +    /// The function is called with all intervals in the range. +    void applyOnWithinRange(BasicValueFactory &BVF, QualType ArgT, +                            const RangeApplyFunction &F) const; +    /// Call a function on all intervals in the complementary range. +    /// The function is called with all intervals that fall out of the range. +    /// E.g. consider an interval list [A, B] and [C, D] +    /// \code +    /// -------+--------+------------------+------------+-----------> +    ///        A        B                  C            D +    /// \endcode +    /// We get the ranges [-inf, A - 1], [D + 1, +inf], [B + 1, C - 1]. +    /// The \p ArgT is used to determine the min and max of the type that is +    /// used as "-inf" and "+inf". +    void applyOnOutOfRange(BasicValueFactory &BVF, QualType ArgT, +                           const RangeApplyFunction &F) const; +    /// Call a function on the intervals of the range or the complementary +    /// range. +    void applyOnRange(RangeKind Kind, BasicValueFactory &BVF, QualType ArgT, +                      const RangeApplyFunction &F) const { +      switch (Kind) { +      case OutOfRange: +        applyOnOutOfRange(BVF, ArgT, F); +        break; +      case WithinRange: +        applyOnWithinRange(BVF, ArgT, F); +        break; +      }; +    } +  }; + +  /// Check relation of an argument to another. +  class ComparisonConstraint : public ValueConstraint { +    BinaryOperator::Opcode Opcode; +    ArgNo OtherArgN; + +  public: +    ComparisonConstraint(ArgNo ArgN, BinaryOperator::Opcode Opcode, +                         ArgNo OtherArgN) +        : ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {} +    ArgNo getOtherArgNo() const { return OtherArgN; } +    BinaryOperator::Opcode getOpcode() const { return Opcode; } +    ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                          const Summary &Summary, +                          CheckerContext &C) const override; +  }; + +  /// Check null or non-null-ness of an argument that is of pointer type. +  class NotNullConstraint : public ValueConstraint { +    using ValueConstraint::ValueConstraint; +    // This variable has a role when we negate the constraint. +    bool CannotBeNull = true; + +  public: +    NotNullConstraint(ArgNo ArgN, bool CannotBeNull = true) +        : ValueConstraint(ArgN), CannotBeNull(CannotBeNull) {} + +    ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                          const Summary &Summary, +                          CheckerContext &C) const override; + +    void describe(DescriptionKind DK, const CallEvent &Call, +                  ProgramStateRef State, const Summary &Summary, +                  llvm::raw_ostream &Out) const override; + +    bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, +                               const Summary &Summary, +                               llvm::raw_ostream &Out) const override; + +    ValueConstraintPtr negate() const override { +      NotNullConstraint Tmp(*this); +      Tmp.CannotBeNull = !this->CannotBeNull; +      return std::make_shared<NotNullConstraint>(Tmp); +    } + +  protected: +    bool checkSpecificValidity(const FunctionDecl *FD) const override { +      const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); +      assert(ValidArg && +             "This constraint should be applied only on a pointer type"); +      return ValidArg; +    } +  }; + +  /// Check null or non-null-ness of an argument that is of pointer type. +  /// The argument is meant to be a buffer that has a size constraint, and it +  /// is allowed to have a NULL value if the size is 0. The size can depend on +  /// 1 or 2 additional arguments, if one of these is 0 the buffer is allowed to +  /// be NULL. This is useful for functions like `fread` which have this special +  /// property. +  class NotNullBufferConstraint : public ValueConstraint { +    using ValueConstraint::ValueConstraint; +    ArgNo SizeArg1N; +    std::optional<ArgNo> SizeArg2N; +    // This variable has a role when we negate the constraint. +    bool CannotBeNull = true; + +  public: +    NotNullBufferConstraint(ArgNo ArgN, ArgNo SizeArg1N, +                            std::optional<ArgNo> SizeArg2N, +                            bool CannotBeNull = true) +        : ValueConstraint(ArgN), SizeArg1N(SizeArg1N), SizeArg2N(SizeArg2N), +          CannotBeNull(CannotBeNull) {} + +    ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                          const Summary &Summary, +                          CheckerContext &C) const override; + +    void describe(DescriptionKind DK, const CallEvent &Call, +                  ProgramStateRef State, const Summary &Summary, +                  llvm::raw_ostream &Out) const override; + +    bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, +                               const Summary &Summary, +                               llvm::raw_ostream &Out) const override; + +    ValueConstraintPtr negate() const override { +      NotNullBufferConstraint Tmp(*this); +      Tmp.CannotBeNull = !this->CannotBeNull; +      return std::make_shared<NotNullBufferConstraint>(Tmp); +    } + +  protected: +    bool checkSpecificValidity(const FunctionDecl *FD) const override { +      const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); +      assert(ValidArg && +             "This constraint should be applied only on a pointer type"); +      return ValidArg; +    } +  }; + +  // Represents a buffer argument with an additional size constraint. The +  // constraint may be a concrete value, or a symbolic value in an argument. +  // Example 1. Concrete value as the minimum buffer size. +  //   char *asctime_r(const struct tm *restrict tm, char *restrict buf); +  //   // `buf` size must be at least 26 bytes according the POSIX standard. +  // Example 2. Argument as a buffer size. +  //   ctime_s(char *buffer, rsize_t bufsz, const time_t *time); +  // Example 3. The size is computed as a multiplication of other args. +  //   size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); +  //   // Here, ptr is the buffer, and its minimum size is `size * nmemb`. +  class BufferSizeConstraint : public ValueConstraint { +    // The concrete value which is the minimum size for the buffer. +    std::optional<llvm::APSInt> ConcreteSize; +    // The argument which holds the size of the buffer. +    std::optional<ArgNo> SizeArgN; +    // The argument which is a multiplier to size. This is set in case of +    // `fread` like functions where the size is computed as a multiplication of +    // two arguments. +    std::optional<ArgNo> SizeMultiplierArgN; +    // The operator we use in apply. This is negated in negate(). +    BinaryOperator::Opcode Op = BO_LE; + +  public: +    BufferSizeConstraint(ArgNo Buffer, llvm::APSInt BufMinSize) +        : ValueConstraint(Buffer), ConcreteSize(BufMinSize) {} +    BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize) +        : ValueConstraint(Buffer), SizeArgN(BufSize) {} +    BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize, ArgNo BufSizeMultiplier) +        : ValueConstraint(Buffer), SizeArgN(BufSize), +          SizeMultiplierArgN(BufSizeMultiplier) {} + +    ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                          const Summary &Summary, +                          CheckerContext &C) const override; + +    void describe(DescriptionKind DK, const CallEvent &Call, +                  ProgramStateRef State, const Summary &Summary, +                  llvm::raw_ostream &Out) const override; + +    bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, +                               const Summary &Summary, +                               llvm::raw_ostream &Out) const override; + +    std::vector<ArgNo> getArgsToTrack() const override { +      std::vector<ArgNo> Result{ArgN}; +      if (SizeArgN) +        Result.push_back(*SizeArgN); +      if (SizeMultiplierArgN) +        Result.push_back(*SizeMultiplierArgN); +      return Result; +    } + +    ValueConstraintPtr negate() const override { +      BufferSizeConstraint Tmp(*this); +      Tmp.Op = BinaryOperator::negateComparisonOp(Op); +      return std::make_shared<BufferSizeConstraint>(Tmp); +    } + +  protected: +    bool checkSpecificValidity(const FunctionDecl *FD) const override { +      const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); +      assert(ValidArg && +             "This constraint should be applied only on a pointer type"); +      return ValidArg; +    } +  }; + +  /// The complete list of constraints that defines a single branch. +  using ConstraintSet = std::vector<ValueConstraintPtr>; + +  /// Define how a function affects the system variable 'errno'. +  /// This works together with the \c ErrnoModeling and \c ErrnoChecker classes. +  /// Currently 3 use cases exist: success, failure, irrelevant. +  /// In the future the failure case can be customized to set \c errno to a +  /// more specific constraint (for example > 0), or new case can be added +  /// for functions which require check of \c errno in both success and failure +  /// case. +  class ErrnoConstraintBase { +  public: +    /// Apply specific state changes related to the errno variable. +    virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                                  const Summary &Summary, +                                  CheckerContext &C) const = 0; +    /// Get a description about what happens with 'errno' here and how it causes +    /// a later bug report created by ErrnoChecker. +    /// Empty return value means that 'errno' related bug may not happen from +    /// the current analyzed function. +    virtual const std::string describe(CheckerContext &C) const { return ""; } + +    virtual ~ErrnoConstraintBase() {} + +  protected: +    ErrnoConstraintBase() = default; + +    /// This is used for conjure symbol for errno to differentiate from the +    /// original call expression (same expression is used for the errno symbol). +    static int Tag; +  }; + +  /// Reset errno constraints to irrelevant. +  /// This is applicable to functions that may change 'errno' and are not +  /// modeled elsewhere. +  class ResetErrnoConstraint : public ErrnoConstraintBase { +  public: +    ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                          const Summary &Summary, +                          CheckerContext &C) const override { +      return errno_modeling::setErrnoState(State, errno_modeling::Irrelevant); +    } +  }; + +  /// Do not change errno constraints. +  /// This is applicable to functions that are modeled in another checker +  /// and the already set errno constraints should not be changed in the +  /// post-call event. +  class NoErrnoConstraint : public ErrnoConstraintBase { +  public: +    ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                          const Summary &Summary, +                          CheckerContext &C) const override { +      return State; +    } +  }; + +  /// Set errno constraint at failure cases of standard functions. +  /// Failure case: 'errno' becomes not equal to 0 and may or may not be checked +  /// by the program. \c ErrnoChecker does not emit a bug report after such a +  /// function call. +  class FailureErrnoConstraint : public ErrnoConstraintBase { +  public: +    ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                          const Summary &Summary, +                          CheckerContext &C) const override { +      SValBuilder &SVB = C.getSValBuilder(); +      NonLoc ErrnoSVal = +          SVB.conjureSymbolVal(&Tag, Call.getOriginExpr(), +                               C.getLocationContext(), C.getASTContext().IntTy, +                               C.blockCount()) +              .castAs<NonLoc>(); +      return errno_modeling::setErrnoForStdFailure(State, C, ErrnoSVal); +    } +  }; + +  /// Set errno constraint at success cases of standard functions. +  /// Success case: 'errno' is not allowed to be used because the value is +  /// undefined after successful call. +  /// \c ErrnoChecker can emit bug report after such a function call if errno +  /// is used. +  class SuccessErrnoConstraint : public ErrnoConstraintBase { +  public: +    ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                          const Summary &Summary, +                          CheckerContext &C) const override { +      return errno_modeling::setErrnoForStdSuccess(State, C); +    } + +    const std::string describe(CheckerContext &C) const override { +      return "'errno' becomes undefined after the call"; +    } +  }; + +  /// Set errno constraint at functions that indicate failure only with 'errno'. +  /// In this case 'errno' is required to be observed. +  /// \c ErrnoChecker can emit bug report after such a function call if errno +  /// is overwritten without a read before. +  class ErrnoMustBeCheckedConstraint : public ErrnoConstraintBase { +  public: +    ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, +                          const Summary &Summary, +                          CheckerContext &C) const override { +      return errno_modeling::setErrnoStdMustBeChecked(State, C, +                                                      Call.getOriginExpr()); +    } + +    const std::string describe(CheckerContext &C) const override { +      return "reading 'errno' is required to find out if the call has failed"; +    } +  }; + +  /// A single branch of a function summary. +  /// +  /// A branch is defined by a series of constraints - "assumptions" - +  /// that together form a single possible outcome of invoking the function. +  /// When static analyzer considers a branch, it tries to introduce +  /// a child node in the Exploded Graph. The child node has to include +  /// constraints that define the branch. If the constraints contradict +  /// existing constraints in the state, the node is not created and the branch +  /// is dropped; otherwise it's queued for future exploration. +  /// The branch is accompanied by a note text that may be displayed +  /// to the user when a bug is found on a path that takes this branch. +  /// +  /// For example, consider the branches in `isalpha(x)`: +  ///   Branch 1) +  ///     x is in range ['A', 'Z'] or in ['a', 'z'] +  ///     then the return value is not 0. (I.e. out-of-range [0, 0]) +  ///     and the note may say "Assuming the character is alphabetical" +  ///   Branch 2) +  ///     x is out-of-range ['A', 'Z'] and out-of-range ['a', 'z'] +  ///     then the return value is 0 +  ///     and the note may say "Assuming the character is non-alphabetical". +  class SummaryCase { +    ConstraintSet Constraints; +    const ErrnoConstraintBase &ErrnoConstraint; +    StringRef Note; + +  public: +    SummaryCase(ConstraintSet &&Constraints, const ErrnoConstraintBase &ErrnoC, +                StringRef Note) +        : Constraints(std::move(Constraints)), ErrnoConstraint(ErrnoC), +          Note(Note) {} + +    SummaryCase(const ConstraintSet &Constraints, +                const ErrnoConstraintBase &ErrnoC, StringRef Note) +        : Constraints(Constraints), ErrnoConstraint(ErrnoC), Note(Note) {} + +    const ConstraintSet &getConstraints() const { return Constraints; } +    const ErrnoConstraintBase &getErrnoConstraint() const { +      return ErrnoConstraint; +    } +    StringRef getNote() const { return Note; } +  }; + +  using ArgTypes = ArrayRef<std::optional<QualType>>; +  using RetType = std::optional<QualType>; + +  // A placeholder type, we use it whenever we do not care about the concrete +  // type in a Signature. +  const QualType Irrelevant{}; +  bool static isIrrelevant(QualType T) { return T.isNull(); } + +  // The signature of a function we want to describe with a summary. This is a +  // concessive signature, meaning there may be irrelevant types in the +  // signature which we do not check against a function with concrete types. +  // All types in the spec need to be canonical. +  class Signature { +    using ArgQualTypes = std::vector<QualType>; +    ArgQualTypes ArgTys; +    QualType RetTy; +    // True if any component type is not found by lookup. +    bool Invalid = false; + +  public: +    // Construct a signature from optional types. If any of the optional types +    // are not set then the signature will be invalid. +    Signature(ArgTypes ArgTys, RetType RetTy) { +      for (std::optional<QualType> Arg : ArgTys) { +        if (!Arg) { +          Invalid = true; +          return; +        } else { +          assertArgTypeSuitableForSignature(*Arg); +          this->ArgTys.push_back(*Arg); +        } +      } +      if (!RetTy) { +        Invalid = true; +        return; +      } else { +        assertRetTypeSuitableForSignature(*RetTy); +        this->RetTy = *RetTy; +      } +    } + +    bool isInvalid() const { return Invalid; } +    bool matches(const FunctionDecl *FD) const; + +  private: +    static void assertArgTypeSuitableForSignature(QualType T) { +      assert((T.isNull() || !T->isVoidType()) && +             "We should have no void types in the spec"); +      assert((T.isNull() || T.isCanonical()) && +             "We should only have canonical types in the spec"); +    } +    static void assertRetTypeSuitableForSignature(QualType T) { +      assert((T.isNull() || T.isCanonical()) && +             "We should only have canonical types in the spec"); +    } +  }; + +  static QualType getArgType(const FunctionDecl *FD, ArgNo ArgN) { +    assert(FD && "Function must be set"); +    QualType T = (ArgN == Ret) +                     ? FD->getReturnType().getCanonicalType() +                     : FD->getParamDecl(ArgN)->getType().getCanonicalType(); +    return T; +  } + +  using SummaryCases = std::vector<SummaryCase>; + +  /// A summary includes information about +  ///   * function prototype (signature) +  ///   * approach to invalidation, +  ///   * a list of branches - so, a list of list of ranges, +  ///   * a list of argument constraints, that must be true on every branch. +  ///     If these constraints are not satisfied that means a fatal error +  ///     usually resulting in undefined behaviour. +  /// +  /// Application of a summary: +  ///   The signature and argument constraints together contain information +  ///   about which functions are handled by the summary. The signature can use +  ///   "wildcards", i.e. Irrelevant types. Irrelevant type of a parameter in +  ///   a signature means that type is not compared to the type of the parameter +  ///   in the found FunctionDecl. Argument constraints may specify additional +  ///   rules for the given parameter's type, those rules are checked once the +  ///   signature is matched. +  class Summary { +    const InvalidationKind InvalidationKd; +    SummaryCases Cases; +    ConstraintSet ArgConstraints; + +    // The function to which the summary applies. This is set after lookup and +    // match to the signature. +    const FunctionDecl *FD = nullptr; + +  public: +    Summary(InvalidationKind InvalidationKd) : InvalidationKd(InvalidationKd) {} + +    Summary &Case(ConstraintSet &&CS, const ErrnoConstraintBase &ErrnoC, +                  StringRef Note = "") { +      Cases.push_back(SummaryCase(std::move(CS), ErrnoC, Note)); +      return *this; +    } +    Summary &Case(const ConstraintSet &CS, const ErrnoConstraintBase &ErrnoC, +                  StringRef Note = "") { +      Cases.push_back(SummaryCase(CS, ErrnoC, Note)); +      return *this; +    } +    Summary &ArgConstraint(ValueConstraintPtr VC) { +      assert(VC->getArgNo() != Ret && +             "Arg constraint should not refer to the return value"); +      ArgConstraints.push_back(VC); +      return *this; +    } + +    InvalidationKind getInvalidationKd() const { return InvalidationKd; } +    const SummaryCases &getCases() const { return Cases; } +    const ConstraintSet &getArgConstraints() const { return ArgConstraints; } + +    QualType getArgType(ArgNo ArgN) const { +      return StdLibraryFunctionsChecker::getArgType(FD, ArgN); +    } + +    // Returns true if the summary should be applied to the given function. +    // And if yes then store the function declaration. +    bool matchesAndSet(const Signature &Sign, const FunctionDecl *FD) { +      bool Result = Sign.matches(FD) && validateByConstraints(FD); +      if (Result) { +        assert(!this->FD && "FD must not be set more than once"); +        this->FD = FD; +      } +      return Result; +    } + +  private: +    // Once we know the exact type of the function then do validation check on +    // all the given constraints. +    bool validateByConstraints(const FunctionDecl *FD) const { +      for (const SummaryCase &Case : Cases) +        for (const ValueConstraintPtr &Constraint : Case.getConstraints()) +          if (!Constraint->checkValidity(FD)) +            return false; +      for (const ValueConstraintPtr &Constraint : ArgConstraints) +        if (!Constraint->checkValidity(FD)) +          return false; +      return true; +    } +  }; + +  // The map of all functions supported by the checker. It is initialized +  // lazily, and it doesn't change after initialization. +  using FunctionSummaryMapType = llvm::DenseMap<const FunctionDecl *, Summary>; +  mutable FunctionSummaryMapType FunctionSummaryMap; + +  const BugType BT_InvalidArg{this, "Function call with invalid argument"}; +  mutable bool SummariesInitialized = false; + +  static SVal getArgSVal(const CallEvent &Call, ArgNo ArgN) { +    return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgN); +  } +  static std::string getFunctionName(const CallEvent &Call) { +    assert(Call.getDecl() && +           "Call was found by a summary, should have declaration"); +    return cast<NamedDecl>(Call.getDecl())->getNameAsString(); +  } + +public: +  void checkPreCall(const CallEvent &Call, CheckerContext &C) const; +  void checkPostCall(const CallEvent &Call, CheckerContext &C) const; +  bool evalCall(const CallEvent &Call, CheckerContext &C) const; + +  CheckerNameRef CheckName; +  bool AddTestFunctions = false; + +  bool DisplayLoadedSummaries = false; +  bool ModelPOSIX = false; +  bool ShouldAssumeControlledEnvironment = false; + +private: +  std::optional<Summary> findFunctionSummary(const FunctionDecl *FD, +                                             CheckerContext &C) const; +  std::optional<Summary> findFunctionSummary(const CallEvent &Call, +                                             CheckerContext &C) const; + +  void initFunctionSummaries(CheckerContext &C) const; + +  void reportBug(const CallEvent &Call, ExplodedNode *N, +                 const ValueConstraint *VC, const ValueConstraint *NegatedVC, +                 const Summary &Summary, CheckerContext &C) const { +    assert(Call.getDecl() && +           "Function found in summary must have a declaration available"); +    SmallString<256> Msg; +    llvm::raw_svector_ostream MsgOs(Msg); + +    MsgOs << "The "; +    printArgDesc(VC->getArgNo(), MsgOs); +    MsgOs << " to '" << getFunctionName(Call) << "' "; +    bool ValuesPrinted = +        NegatedVC->describeArgumentValue(Call, N->getState(), Summary, MsgOs); +    if (ValuesPrinted) +      MsgOs << " but "; +    else +      MsgOs << "is out of the accepted range; It "; +    VC->describe(ValueConstraint::Violation, Call, C.getState(), Summary, +                 MsgOs); +    Msg[0] = toupper(Msg[0]); +    auto R = std::make_unique<PathSensitiveBugReport>(BT_InvalidArg, Msg, N); + +    for (ArgNo ArgN : VC->getArgsToTrack()) { +      bugreporter::trackExpressionValue(N, Call.getArgExpr(ArgN), *R); +      R->markInteresting(Call.getArgSVal(ArgN)); +      // All tracked arguments are important, highlight them. +      R->addRange(Call.getArgSourceRange(ArgN)); +    } + +    C.emitReport(std::move(R)); +  } + +  /// These are the errno constraints that can be passed to summary cases. +  /// One of these should fit for a single summary case. +  /// Usually if a failure return value exists for function, that function +  /// needs different cases for success and failure with different errno +  /// constraints (and different return value constraints). +  const NoErrnoConstraint ErrnoUnchanged{}; +  const ResetErrnoConstraint ErrnoIrrelevant{}; +  const ErrnoMustBeCheckedConstraint ErrnoMustBeChecked{}; +  const SuccessErrnoConstraint ErrnoMustNotBeChecked{}; +  const FailureErrnoConstraint ErrnoNEZeroIrrelevant{}; +}; + +int StdLibraryFunctionsChecker::ErrnoConstraintBase::Tag = 0; + +const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret = +    std::numeric_limits<ArgNo>::max(); + +static BasicValueFactory &getBVF(ProgramStateRef State) { +  ProgramStateManager &Mgr = State->getStateManager(); +  SValBuilder &SVB = Mgr.getSValBuilder(); +  return SVB.getBasicValueFactory(); +} + +} // end of anonymous namespace + +void StdLibraryFunctionsChecker::printArgDesc( +    StdLibraryFunctionsChecker::ArgNo ArgN, llvm::raw_ostream &Out) { +  Out << std::to_string(ArgN + 1); +  Out << llvm::getOrdinalSuffix(ArgN + 1); +  Out << " argument"; +} + +void StdLibraryFunctionsChecker::printArgValueInfo(ArgNo ArgN, +                                                   ProgramStateRef State, +                                                   const CallEvent &Call, +                                                   llvm::raw_ostream &Out) { +  if (const llvm::APSInt *Val = +          State->getStateManager().getSValBuilder().getKnownValue( +              State, getArgSVal(Call, ArgN))) +    Out << " (which is " << *Val << ")"; +} + +void StdLibraryFunctionsChecker::appendInsideRangeDesc(llvm::APSInt RMin, +                                                       llvm::APSInt RMax, +                                                       QualType ArgT, +                                                       BasicValueFactory &BVF, +                                                       llvm::raw_ostream &Out) { +  if (RMin.isZero() && RMax.isZero()) +    Out << "zero"; +  else if (RMin == RMax) +    Out << RMin; +  else if (RMin == BVF.getMinValue(ArgT)) { +    if (RMax == -1) +      Out << "< 0"; +    else +      Out << "<= " << RMax; +  } else if (RMax == BVF.getMaxValue(ArgT)) { +    if (RMin.isOne()) +      Out << "> 0"; +    else +      Out << ">= " << RMin; +  } else if (RMin.isNegative() == RMax.isNegative() && +             RMin.getLimitedValue() == RMax.getLimitedValue() - 1) { +    Out << RMin << " or " << RMax; +  } else { +    Out << "between " << RMin << " and " << RMax; +  } +} + +void StdLibraryFunctionsChecker::appendOutOfRangeDesc(llvm::APSInt RMin, +                                                      llvm::APSInt RMax, +                                                      QualType ArgT, +                                                      BasicValueFactory &BVF, +                                                      llvm::raw_ostream &Out) { +  if (RMin.isZero() && RMax.isZero()) +    Out << "nonzero"; +  else if (RMin == RMax) { +    Out << "not equal to " << RMin; +  } else if (RMin == BVF.getMinValue(ArgT)) { +    if (RMax == -1) +      Out << ">= 0"; +    else +      Out << "> " << RMax; +  } else if (RMax == BVF.getMaxValue(ArgT)) { +    if (RMin.isOne()) +      Out << "<= 0"; +    else +      Out << "< " << RMin; +  } else if (RMin.isNegative() == RMax.isNegative() && +             RMin.getLimitedValue() == RMax.getLimitedValue() - 1) { +    Out << "not " << RMin << " and not " << RMax; +  } else { +    Out << "not between " << RMin << " and " << RMax; +  } +} + +void StdLibraryFunctionsChecker::RangeConstraint::applyOnWithinRange( +    BasicValueFactory &BVF, QualType ArgT, const RangeApplyFunction &F) const { +  if (Ranges.empty()) +    return; + +  for (auto [Start, End] : getRanges()) { +    const llvm::APSInt &Min = BVF.getValue(Start, ArgT); +    const llvm::APSInt &Max = BVF.getValue(End, ArgT); +    assert(Min <= Max); +    if (!F(Min, Max)) +      return; +  } +} + +void StdLibraryFunctionsChecker::RangeConstraint::applyOnOutOfRange( +    BasicValueFactory &BVF, QualType ArgT, const RangeApplyFunction &F) const { +  if (Ranges.empty()) +    return; + +  const IntRangeVector &R = getRanges(); +  size_t E = R.size(); + +  const llvm::APSInt &MinusInf = BVF.getMinValue(ArgT); +  const llvm::APSInt &PlusInf = BVF.getMaxValue(ArgT); + +  const llvm::APSInt &RangeLeft = BVF.getValue(R[0].first - 1ULL, ArgT); +  const llvm::APSInt &RangeRight = BVF.getValue(R[E - 1].second + 1ULL, ArgT); + +  // Iterate over the "holes" between intervals. +  for (size_t I = 1; I != E; ++I) { +    const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, ArgT); +    const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, ArgT); +    if (Min <= Max) { +      if (!F(Min, Max)) +        return; +    } +  } +  // Check the interval [T_MIN, min(R) - 1]. +  if (RangeLeft != PlusInf) { +    assert(MinusInf <= RangeLeft); +    if (!F(MinusInf, RangeLeft)) +      return; +  } +  // Check the interval [max(R) + 1, T_MAX], +  if (RangeRight != MinusInf) { +    assert(RangeRight <= PlusInf); +    if (!F(RangeRight, PlusInf)) +      return; +  } +} + +ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::apply( +    ProgramStateRef State, const CallEvent &Call, const Summary &Summary, +    CheckerContext &C) const { +  ConstraintManager &CM = C.getConstraintManager(); +  SVal V = getArgSVal(Call, getArgNo()); +  QualType T = Summary.getArgType(getArgNo()); + +  if (auto N = V.getAs<NonLoc>()) { +    auto ExcludeRangeFromArg = [&](const llvm::APSInt &Min, +                                   const llvm::APSInt &Max) { +      State = CM.assumeInclusiveRange(State, *N, Min, Max, false); +      return static_cast<bool>(State); +    }; +    // "OutOfRange R" is handled by excluding all ranges in R. +    // "WithinRange R" is treated as "OutOfRange [T_MIN, T_MAX] \ R". +    applyOnRange(negateKind(Kind), C.getSValBuilder().getBasicValueFactory(), T, +                 ExcludeRangeFromArg); +  } + +  return State; +} + +void StdLibraryFunctionsChecker::RangeConstraint::describe( +    DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, +    const Summary &Summary, llvm::raw_ostream &Out) const { + +  BasicValueFactory &BVF = getBVF(State); +  QualType T = Summary.getArgType(getArgNo()); + +  Out << ((DK == Violation) ? "should be " : "is "); +  if (!Description.empty()) { +    Out << Description; +  } else { +    unsigned I = Ranges.size(); +    if (Kind == WithinRange) { +      for (const std::pair<RangeInt, RangeInt> &R : Ranges) { +        appendInsideRangeDesc(BVF.getValue(R.first, T), +                              BVF.getValue(R.second, T), T, BVF, Out); +        if (--I > 0) +          Out << " or "; +      } +    } else { +      for (const std::pair<RangeInt, RangeInt> &R : Ranges) { +        appendOutOfRangeDesc(BVF.getValue(R.first, T), +                             BVF.getValue(R.second, T), T, BVF, Out); +        if (--I > 0) +          Out << " and "; +      } +    } +  } +} + +bool StdLibraryFunctionsChecker::RangeConstraint::describeArgumentValue( +    const CallEvent &Call, ProgramStateRef State, const Summary &Summary, +    llvm::raw_ostream &Out) const { +  unsigned int NRanges = 0; +  bool HaveAllRanges = true; + +  ProgramStateManager &Mgr = State->getStateManager(); +  BasicValueFactory &BVF = Mgr.getSValBuilder().getBasicValueFactory(); +  ConstraintManager &CM = Mgr.getConstraintManager(); +  SVal V = getArgSVal(Call, getArgNo()); + +  if (auto N = V.getAs<NonLoc>()) { +    if (const llvm::APSInt *Int = N->getAsInteger()) { +      Out << "is "; +      Out << *Int; +      return true; +    } +    QualType T = Summary.getArgType(getArgNo()); +    SmallString<128> MoreInfo; +    llvm::raw_svector_ostream MoreInfoOs(MoreInfo); +    auto ApplyF = [&](const llvm::APSInt &Min, const llvm::APSInt &Max) { +      if (CM.assumeInclusiveRange(State, *N, Min, Max, true)) { +        if (NRanges > 0) +          MoreInfoOs << " or "; +        appendInsideRangeDesc(Min, Max, T, BVF, MoreInfoOs); +        ++NRanges; +      } else { +        HaveAllRanges = false; +      } +      return true; +    }; + +    applyOnRange(Kind, BVF, T, ApplyF); +    assert(NRanges > 0); +    if (!HaveAllRanges || NRanges == 1) { +      Out << "is "; +      Out << MoreInfo; +      return true; +    } +  } +  return false; +} + +ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( +    ProgramStateRef State, const CallEvent &Call, const Summary &Summary, +    CheckerContext &C) const { + +  ProgramStateManager &Mgr = State->getStateManager(); +  SValBuilder &SVB = Mgr.getSValBuilder(); +  QualType CondT = SVB.getConditionType(); +  QualType T = Summary.getArgType(getArgNo()); +  SVal V = getArgSVal(Call, getArgNo()); + +  BinaryOperator::Opcode Op = getOpcode(); +  ArgNo OtherArg = getOtherArgNo(); +  SVal OtherV = getArgSVal(Call, OtherArg); +  QualType OtherT = Summary.getArgType(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; +} + +ProgramStateRef StdLibraryFunctionsChecker::NotNullConstraint::apply( +    ProgramStateRef State, const CallEvent &Call, const Summary &Summary, +    CheckerContext &C) const { +  SVal V = getArgSVal(Call, getArgNo()); +  if (V.isUndef()) +    return State; + +  DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>(); +  if (!isa<Loc>(L)) +    return State; + +  return State->assume(L, CannotBeNull); +} + +void StdLibraryFunctionsChecker::NotNullConstraint::describe( +    DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, +    const Summary &Summary, llvm::raw_ostream &Out) const { +  assert(CannotBeNull && +         "Describe should not be used when the value must be NULL"); +  if (DK == Violation) +    Out << "should not be NULL"; +  else +    Out << "is not NULL"; +} + +bool StdLibraryFunctionsChecker::NotNullConstraint::describeArgumentValue( +    const CallEvent &Call, ProgramStateRef State, const Summary &Summary, +    llvm::raw_ostream &Out) const { +  assert(!CannotBeNull && "This function is used when the value is NULL"); +  Out << "is NULL"; +  return true; +} + +ProgramStateRef StdLibraryFunctionsChecker::NotNullBufferConstraint::apply( +    ProgramStateRef State, const CallEvent &Call, const Summary &Summary, +    CheckerContext &C) const { +  SVal V = getArgSVal(Call, getArgNo()); +  if (V.isUndef()) +    return State; +  DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>(); +  if (!isa<Loc>(L)) +    return State; + +  std::optional<DefinedOrUnknownSVal> SizeArg1 = +      getArgSVal(Call, SizeArg1N).getAs<DefinedOrUnknownSVal>(); +  std::optional<DefinedOrUnknownSVal> SizeArg2; +  if (SizeArg2N) +    SizeArg2 = getArgSVal(Call, *SizeArg2N).getAs<DefinedOrUnknownSVal>(); + +  auto IsArgZero = [State](std::optional<DefinedOrUnknownSVal> Val) { +    if (!Val) +      return false; +    auto [IsNonNull, IsNull] = State->assume(*Val); +    return IsNull && !IsNonNull; +  }; + +  if (IsArgZero(SizeArg1) || IsArgZero(SizeArg2)) +    return State; + +  return State->assume(L, CannotBeNull); +} + +void StdLibraryFunctionsChecker::NotNullBufferConstraint::describe( +    DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, +    const Summary &Summary, llvm::raw_ostream &Out) const { +  assert(CannotBeNull && +         "Describe should not be used when the value must be NULL"); +  if (DK == Violation) +    Out << "should not be NULL"; +  else +    Out << "is not NULL"; +} + +bool StdLibraryFunctionsChecker::NotNullBufferConstraint::describeArgumentValue( +    const CallEvent &Call, ProgramStateRef State, const Summary &Summary, +    llvm::raw_ostream &Out) const { +  assert(!CannotBeNull && "This function is used when the value is NULL"); +  Out << "is NULL"; +  return true; +} + +ProgramStateRef StdLibraryFunctionsChecker::BufferSizeConstraint::apply( +    ProgramStateRef State, const CallEvent &Call, const Summary &Summary, +    CheckerContext &C) const { +  SValBuilder &SvalBuilder = C.getSValBuilder(); +  // The buffer argument. +  SVal BufV = getArgSVal(Call, getArgNo()); + +  // Get the size constraint. +  const SVal SizeV = [this, &State, &Call, &Summary, &SvalBuilder]() { +    if (ConcreteSize) { +      return SVal(SvalBuilder.makeIntVal(*ConcreteSize)); +    } +    assert(SizeArgN && "The constraint must be either a concrete value or " +                       "encoded in an argument."); +    // The size argument. +    SVal SizeV = getArgSVal(Call, *SizeArgN); +    // Multiply with another argument if given. +    if (SizeMultiplierArgN) { +      SVal SizeMulV = getArgSVal(Call, *SizeMultiplierArgN); +      SizeV = SvalBuilder.evalBinOp(State, BO_Mul, SizeV, SizeMulV, +                                    Summary.getArgType(*SizeArgN)); +    } +    return SizeV; +  }(); + +  // The dynamic size of the buffer argument, got from the analyzer engine. +  SVal BufDynSize = getDynamicExtentWithOffset(State, BufV); + +  SVal Feasible = SvalBuilder.evalBinOp(State, Op, SizeV, BufDynSize, +                                        SvalBuilder.getContext().BoolTy); +  if (auto F = Feasible.getAs<DefinedOrUnknownSVal>()) +    return State->assume(*F, true); + +  // We can get here only if the size argument or the dynamic size is +  // undefined. But the dynamic size should never be undefined, only +  // unknown. So, here, the size of the argument is undefined, i.e. we +  // cannot apply the constraint. Actually, other checkers like +  // CallAndMessage should catch this situation earlier, because we call a +  // function with an uninitialized argument. +  llvm_unreachable("Size argument or the dynamic size is Undefined"); +} + +void StdLibraryFunctionsChecker::BufferSizeConstraint::describe( +    DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, +    const Summary &Summary, llvm::raw_ostream &Out) const { +  Out << ((DK == Violation) ? "should be " : "is "); +  Out << "a buffer with size equal to or greater than "; +  if (ConcreteSize) { +    Out << *ConcreteSize; +  } else if (SizeArgN) { +    Out << "the value of the "; +    printArgDesc(*SizeArgN, Out); +    printArgValueInfo(*SizeArgN, State, Call, Out); +    if (SizeMultiplierArgN) { +      Out << " times the "; +      printArgDesc(*SizeMultiplierArgN, Out); +      printArgValueInfo(*SizeMultiplierArgN, State, Call, Out); +    } +  } +} + +bool StdLibraryFunctionsChecker::BufferSizeConstraint::describeArgumentValue( +    const CallEvent &Call, ProgramStateRef State, const Summary &Summary, +    llvm::raw_ostream &Out) const { +  SVal BufV = getArgSVal(Call, getArgNo()); +  SVal BufDynSize = getDynamicExtentWithOffset(State, BufV); +  if (const llvm::APSInt *Val = +          State->getStateManager().getSValBuilder().getKnownValue(State, +                                                                  BufDynSize)) { +    Out << "is a buffer with size " << *Val; +    return true; +  } +  return false; +} + +void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, +                                              CheckerContext &C) const { +  std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); +  if (!FoundSummary) +    return; + +  const Summary &Summary = *FoundSummary; +  ProgramStateRef State = C.getState(); + +  ProgramStateRef NewState = State; +  ExplodedNode *NewNode = C.getPredecessor(); +  for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) { +    ValueConstraintPtr NegatedConstraint = Constraint->negate(); +    ProgramStateRef SuccessSt = Constraint->apply(NewState, Call, Summary, C); +    ProgramStateRef FailureSt = +        NegatedConstraint->apply(NewState, Call, Summary, C); +    // The argument constraint is not satisfied. +    if (FailureSt && !SuccessSt) { +      if (ExplodedNode *N = C.generateErrorNode(State, NewNode)) +        reportBug(Call, N, Constraint.get(), NegatedConstraint.get(), Summary, +                  C); +      break; +    } +    // We will apply the constraint even if we cannot reason about the +    // argument. This means both SuccessSt and FailureSt can be true. If we +    // weren't applying the constraint that would mean that symbolic +    // execution continues on a code whose behaviour is undefined. +    assert(SuccessSt); +    NewState = SuccessSt; +    if (NewState != State) { +      SmallString<128> Msg; +      llvm::raw_svector_ostream Os(Msg); +      Os << "Assuming that the "; +      printArgDesc(Constraint->getArgNo(), Os); +      Os << " to '"; +      Os << getFunctionName(Call); +      Os << "' "; +      Constraint->describe(ValueConstraint::Assumption, Call, NewState, Summary, +                           Os); +      const auto ArgSVal = Call.getArgSVal(Constraint->getArgNo()); +      NewNode = C.addTransition( +          NewState, NewNode, +          C.getNoteTag([Msg = std::move(Msg), ArgSVal]( +                           PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { +            if (BR.isInteresting(ArgSVal)) +              OS << Msg; +          })); +    } +  } +} + +void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, +                                               CheckerContext &C) const { +  std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); +  if (!FoundSummary) +    return; + +  // Now apply the constraints. +  const Summary &Summary = *FoundSummary; +  ProgramStateRef State = C.getState(); +  ExplodedNode *Node = C.getPredecessor(); + +  // Apply case/branch specifications. +  for (const SummaryCase &Case : Summary.getCases()) { +    ProgramStateRef NewState = State; +    for (const ValueConstraintPtr &Constraint : Case.getConstraints()) { +      NewState = Constraint->apply(NewState, Call, Summary, C); +      if (!NewState) +        break; +    } + +    if (NewState) +      NewState = Case.getErrnoConstraint().apply(NewState, Call, Summary, C); + +    if (!NewState) +      continue; + +    // Here it's possible that NewState == State, e.g. when other checkers +    // already applied the same constraints (or stricter ones). +    // Still add these note tags, the other checker should add only its +    // specialized note tags. These general note tags are handled always by +    // StdLibraryFunctionsChecker. + +    ExplodedNode *Pred = Node; +    DeclarationName FunctionName = +        cast<NamedDecl>(Call.getDecl())->getDeclName(); + +    std::string ErrnoNote = Case.getErrnoConstraint().describe(C); +    std::string CaseNote; +    if (Case.getNote().empty()) { +      if (!ErrnoNote.empty()) +        ErrnoNote = +            llvm::formatv("After calling '{0}' {1}", FunctionName, ErrnoNote); +    } else { +      CaseNote = llvm::formatv(Case.getNote().str().c_str(), FunctionName); +    } +    const SVal RV = Call.getReturnValue(); + +    if (Summary.getInvalidationKd() == EvalCallAsPure) { +      // Do not expect that errno is interesting (the "pure" functions do not +      // affect it). +      if (!CaseNote.empty()) { +        const NoteTag *Tag = C.getNoteTag( +            [Node, CaseNote, RV](PathSensitiveBugReport &BR) -> std::string { +              // Try to omit the note if we know in advance which branch is +              // taken (this means, only one branch exists). +              // This check is performed inside the lambda, after other +              // (or this) checkers had a chance to add other successors. +              // Dereferencing the saved node object is valid because it's part +              // of a bug report call sequence. +              // FIXME: This check is not exact. We may be here after a state +              // split that was performed by another checker (and can not find +              // the successors). This is why this check is only used in the +              // EvalCallAsPure case. +              if (BR.isInteresting(RV) && Node->succ_size() > 1) +                return CaseNote; +              return ""; +            }); +        Pred = C.addTransition(NewState, Pred, Tag); +      } +    } else { +      if (!CaseNote.empty() || !ErrnoNote.empty()) { +        const NoteTag *Tag = +            C.getNoteTag([CaseNote, ErrnoNote, +                          RV](PathSensitiveBugReport &BR) -> std::string { +              // If 'errno' is interesting, show the user a note about the case +              // (what happened at the function call) and about how 'errno' +              // causes the problem. ErrnoChecker sets the errno (but not RV) to +              // interesting. +              // If only the return value is interesting, show only the case +              // note. +              std::optional<Loc> ErrnoLoc = +                  errno_modeling::getErrnoLoc(BR.getErrorNode()->getState()); +              bool ErrnoImportant = !ErrnoNote.empty() && ErrnoLoc && +                                    BR.isInteresting(ErrnoLoc->getAsRegion()); +              if (ErrnoImportant) { +                BR.markNotInteresting(ErrnoLoc->getAsRegion()); +                if (CaseNote.empty()) +                  return ErrnoNote; +                return llvm::formatv("{0}; {1}", CaseNote, ErrnoNote); +              } else { +                if (BR.isInteresting(RV)) +                  return CaseNote; +              } +              return ""; +            }); +        Pred = C.addTransition(NewState, Pred, Tag); +      } +    } + +    // Add the transition if no note tag was added. +    if (Pred == Node && NewState != State) +      C.addTransition(NewState); +  } +} + +bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, +                                          CheckerContext &C) const { +  std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); +  if (!FoundSummary) +    return false; + +  const Summary &Summary = *FoundSummary; +  switch (Summary.getInvalidationKd()) { +  case EvalCallAsPure: { +    ProgramStateRef State = C.getState(); +    const LocationContext *LC = C.getLocationContext(); +    const auto *CE = cast<CallExpr>(Call.getOriginExpr()); +    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::Signature::matches( +    const FunctionDecl *FD) const { +  assert(!isInvalid()); +  // Check the number of arguments. +  if (FD->param_size() != ArgTys.size()) +    return false; + +  // The "restrict" keyword is illegal in C++, however, many libc +  // implementations use the "__restrict" compiler intrinsic in functions +  // prototypes. The "__restrict" keyword qualifies a type as a restricted type +  // even in C++. +  // In case of any non-C99 languages, we don't want to match based on the +  // restrict qualifier because we cannot know if the given libc implementation +  // qualifies the paramter type or not. +  auto RemoveRestrict = [&FD](QualType T) { +    if (!FD->getASTContext().getLangOpts().C99) +      T.removeLocalRestrict(); +    return T; +  }; + +  // Check the return type. +  if (!isIrrelevant(RetTy)) { +    QualType FDRetTy = RemoveRestrict(FD->getReturnType().getCanonicalType()); +    if (RetTy != FDRetTy) +      return false; +  } + +  // Check the argument types. +  for (auto [Idx, ArgTy] : llvm::enumerate(ArgTys)) { +    if (isIrrelevant(ArgTy)) +      continue; +    QualType FDArgTy = +        RemoveRestrict(FD->getParamDecl(Idx)->getType().getCanonicalType()); +    if (ArgTy != FDArgTy) +      return false; +  } + +  return true; +} + +std::optional<StdLibraryFunctionsChecker::Summary> +StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD, +                                                CheckerContext &C) const { +  if (!FD) +    return std::nullopt; + +  initFunctionSummaries(C); + +  auto FSMI = FunctionSummaryMap.find(FD->getCanonicalDecl()); +  if (FSMI == FunctionSummaryMap.end()) +    return std::nullopt; +  return FSMI->second; +} + +std::optional<StdLibraryFunctionsChecker::Summary> +StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call, +                                                CheckerContext &C) const { +  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); +  if (!FD) +    return std::nullopt; +  return findFunctionSummary(FD, C); +} + +void StdLibraryFunctionsChecker::initFunctionSummaries( +    CheckerContext &C) const { +  if (SummariesInitialized) +    return; +  SummariesInitialized = true; + +  SValBuilder &SVB = C.getSValBuilder(); +  BasicValueFactory &BVF = SVB.getBasicValueFactory(); +  const ASTContext &ACtx = BVF.getContext(); +  Preprocessor &PP = C.getPreprocessor(); + +  // Helper class to lookup a type by its name. +  class LookupType { +    const ASTContext &ACtx; + +  public: +    LookupType(const ASTContext &ACtx) : ACtx(ACtx) {} + +    // Find the type. If not found then the optional is not set. +    std::optional<QualType> operator()(StringRef Name) { +      IdentifierInfo &II = ACtx.Idents.get(Name); +      auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); +      if (LookupRes.empty()) +        return std::nullopt; + +      // Prioritze typedef declarations. +      // This is needed in case of C struct typedefs. E.g.: +      //   typedef struct FILE FILE; +      // In this case, we have a RecordDecl 'struct FILE' with the name 'FILE' +      // and we have a TypedefDecl with the name 'FILE'. +      for (Decl *D : LookupRes) +        if (auto *TD = dyn_cast<TypedefNameDecl>(D)) +          return ACtx.getTypeDeclType(TD).getCanonicalType(); + +      // Find the first TypeDecl. +      // There maybe cases when a function has the same name as a struct. +      // E.g. in POSIX: `struct stat` and the function `stat()`: +      //   int stat(const char *restrict path, struct stat *restrict buf); +      for (Decl *D : LookupRes) +        if (auto *TD = dyn_cast<TypeDecl>(D)) +          return ACtx.getTypeDeclType(TD).getCanonicalType(); +      return std::nullopt; +    } +  } lookupTy(ACtx); + +  // Below are auxiliary classes to handle optional types that we get as a +  // result of the lookup. +  class GetRestrictTy { +    const ASTContext &ACtx; + +  public: +    GetRestrictTy(const ASTContext &ACtx) : ACtx(ACtx) {} +    QualType operator()(QualType Ty) { +      return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty; +    } +    std::optional<QualType> operator()(std::optional<QualType> Ty) { +      if (Ty) +        return operator()(*Ty); +      return std::nullopt; +    } +  } getRestrictTy(ACtx); +  class GetPointerTy { +    const ASTContext &ACtx; + +  public: +    GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {} +    QualType operator()(QualType Ty) { return ACtx.getPointerType(Ty); } +    std::optional<QualType> operator()(std::optional<QualType> Ty) { +      if (Ty) +        return operator()(*Ty); +      return std::nullopt; +    } +  } getPointerTy(ACtx); +  class { +  public: +    std::optional<QualType> operator()(std::optional<QualType> Ty) { +      return Ty ? std::optional<QualType>(Ty->withConst()) : std::nullopt; +    } +    QualType operator()(QualType Ty) { return Ty.withConst(); } +  } getConstTy; +  class GetMaxValue { +    BasicValueFactory &BVF; + +  public: +    GetMaxValue(BasicValueFactory &BVF) : BVF(BVF) {} +    std::optional<RangeInt> operator()(QualType Ty) { +      return BVF.getMaxValue(Ty).getLimitedValue(); +    } +    std::optional<RangeInt> operator()(std::optional<QualType> Ty) { +      if (Ty) { +        return operator()(*Ty); +      } +      return std::nullopt; +    } +  } getMaxValue(BVF); + +  // 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. +  const QualType VoidTy = ACtx.VoidTy; +  const QualType CharTy = ACtx.CharTy; +  const QualType WCharTy = ACtx.WCharTy; +  const QualType IntTy = ACtx.IntTy; +  const QualType UnsignedIntTy = ACtx.UnsignedIntTy; +  const QualType LongTy = ACtx.LongTy; +  const QualType SizeTy = ACtx.getSizeType(); + +  const QualType VoidPtrTy = getPointerTy(VoidTy); // void * +  const QualType IntPtrTy = getPointerTy(IntTy);   // int * +  const QualType UnsignedIntPtrTy = +      getPointerTy(UnsignedIntTy); // unsigned int * +  const QualType VoidPtrRestrictTy = getRestrictTy(VoidPtrTy); +  const QualType ConstVoidPtrTy = +      getPointerTy(getConstTy(VoidTy));            // const void * +  const QualType CharPtrTy = getPointerTy(CharTy); // char * +  const QualType CharPtrRestrictTy = getRestrictTy(CharPtrTy); +  const QualType ConstCharPtrTy = +      getPointerTy(getConstTy(CharTy)); // const char * +  const QualType ConstCharPtrRestrictTy = getRestrictTy(ConstCharPtrTy); +  const QualType Wchar_tPtrTy = getPointerTy(WCharTy); // wchar_t * +  const QualType ConstWchar_tPtrTy = +      getPointerTy(getConstTy(WCharTy)); // const wchar_t * +  const QualType ConstVoidPtrRestrictTy = getRestrictTy(ConstVoidPtrTy); +  const QualType SizePtrTy = getPointerTy(SizeTy); +  const QualType SizePtrRestrictTy = getRestrictTy(SizePtrTy); + +  const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue(); +  const RangeInt UnsignedIntMax = +      BVF.getMaxValue(UnsignedIntTy).getLimitedValue(); +  const RangeInt LongMax = BVF.getMaxValue(LongTy).getLimitedValue(); +  const RangeInt SizeMax = BVF.getMaxValue(SizeTy).getLimitedValue(); + +  // Set UCharRangeMax to min of int or uchar maximum value. +  // The C standard states that the arguments of functions like isalpha must +  // be representable as an unsigned char. Their type is 'int', so the max +  // value of the argument should be min(UCharMax, IntMax). This just happen +  // to be true for commonly used and well tested instruction set +  // architectures, but not for others. +  const RangeInt UCharRangeMax = +      std::min(BVF.getMaxValue(ACtx.UnsignedCharTy).getLimitedValue(), IntMax); + +  // Get platform dependent values of some macros. +  // Try our best to parse this from the Preprocessor, otherwise fallback to a +  // default value (what is found in a library header). +  const auto EOFv = tryExpandAsInteger("EOF", PP).value_or(-1); +  const auto AT_FDCWDv = tryExpandAsInteger("AT_FDCWD", PP).value_or(-100); + +  // Auxiliary class to aid adding summaries to the summary map. +  struct AddToFunctionSummaryMap { +    const ASTContext &ACtx; +    FunctionSummaryMapType ⤅ +    bool DisplayLoadedSummaries; +    AddToFunctionSummaryMap(const ASTContext &ACtx, FunctionSummaryMapType &FSM, +                            bool DisplayLoadedSummaries) +        : ACtx(ACtx), Map(FSM), DisplayLoadedSummaries(DisplayLoadedSummaries) { +    } + +    // Add a summary to a FunctionDecl found by lookup. The lookup is performed +    // by the given Name, and in the global scope. The summary will be attached +    // to the found FunctionDecl only if the signatures match. +    // +    // Returns true if the summary has been added, false otherwise. +    bool operator()(StringRef Name, Signature Sign, Summary Sum) { +      if (Sign.isInvalid()) +        return false; +      IdentifierInfo &II = ACtx.Idents.get(Name); +      auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); +      if (LookupRes.empty()) +        return false; +      for (Decl *D : LookupRes) { +        if (auto *FD = dyn_cast<FunctionDecl>(D)) { +          if (Sum.matchesAndSet(Sign, FD)) { +            auto Res = Map.insert({FD->getCanonicalDecl(), Sum}); +            assert(Res.second && "Function already has a summary set!"); +            (void)Res; +            if (DisplayLoadedSummaries) { +              llvm::errs() << "Loaded summary for: "; +              FD->print(llvm::errs()); +              llvm::errs() << "\n"; +            } +            return true; +          } +        } +      } +      return false; +    } +    // Add the same summary for different names with the Signature explicitly +    // given. +    void operator()(ArrayRef<StringRef> Names, Signature Sign, Summary Sum) { +      for (StringRef Name : Names) +        operator()(Name, Sign, Sum); +    } +  } addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries); + +  // Below are helpers functions to create the summaries. +  auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, IntRangeVector Ranges, +                              StringRef Desc = "") { +    return std::make_shared<RangeConstraint>(ArgN, Kind, Ranges, Desc); +  }; +  auto BufferSize = [](auto... Args) { +    return std::make_shared<BufferSizeConstraint>(Args...); +  }; +  struct { +    auto operator()(RangeKind Kind, IntRangeVector Ranges) { +      return std::make_shared<RangeConstraint>(Ret, Kind, Ranges); +    } +    auto operator()(BinaryOperator::Opcode Op, ArgNo OtherArgN) { +      return std::make_shared<ComparisonConstraint>(Ret, Op, OtherArgN); +    } +  } ReturnValueCondition; +  struct { +    auto operator()(RangeInt b, RangeInt e) { +      return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}}; +    } +    auto operator()(RangeInt b, std::optional<RangeInt> e) { +      if (e) +        return IntRangeVector{std::pair<RangeInt, RangeInt>{b, *e}}; +      return IntRangeVector{}; +    } +    auto operator()(std::pair<RangeInt, RangeInt> i0, +                    std::pair<RangeInt, std::optional<RangeInt>> i1) { +      if (i1.second) +        return IntRangeVector{i0, {i1.first, *(i1.second)}}; +      return IntRangeVector{i0}; +    } +  } Range; +  auto SingleValue = [](RangeInt v) { +    return IntRangeVector{std::pair<RangeInt, RangeInt>{v, v}}; +  }; +  auto LessThanOrEq = BO_LE; +  auto NotNull = [&](ArgNo ArgN) { +    return std::make_shared<NotNullConstraint>(ArgN); +  }; +  auto IsNull = [&](ArgNo ArgN) { +    return std::make_shared<NotNullConstraint>(ArgN, false); +  }; +  auto NotNullBuffer = [&](ArgNo ArgN, ArgNo SizeArg1N, ArgNo SizeArg2N) { +    return std::make_shared<NotNullBufferConstraint>(ArgN, SizeArg1N, +                                                     SizeArg2N); +  }; + +  std::optional<QualType> FileTy = lookupTy("FILE"); +  std::optional<QualType> FilePtrTy = getPointerTy(FileTy); +  std::optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy); + +  std::optional<QualType> FPosTTy = lookupTy("fpos_t"); +  std::optional<QualType> FPosTPtrTy = getPointerTy(FPosTTy); +  std::optional<QualType> ConstFPosTPtrTy = getPointerTy(getConstTy(FPosTTy)); +  std::optional<QualType> FPosTPtrRestrictTy = getRestrictTy(FPosTPtrTy); + +  constexpr llvm::StringLiteral GenericSuccessMsg( +      "Assuming that '{0}' is successful"); +  constexpr llvm::StringLiteral GenericFailureMsg("Assuming that '{0}' fails"); + +  // We are finally ready to define specifications for all supported functions. +  // +  // Argument ranges should always cover all variants. If return value +  // is completely unknown, omit it from the respective range set. +  // +  // 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. + +  // The isascii() family of functions. +  // The behavior is undefined if the value of the argument is not +  // representable as unsigned char or is not equal to EOF. See e.g. C99 +  // 7.4.1.2 The isalpha function (p: 181-182). +  addToFunctionSummaryMap( +      "isalnum", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          // Boils down to isupper() or islower() or isdigit(). +          .Case({ArgumentCondition(0U, WithinRange, +                                   {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}}), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, "Assuming the character is alphanumeric") +          // The locale-specific range. +          // No post-condition. We are completely unaware of +          // locale-specific return values. +          .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, +                ErrnoIrrelevant) +          .Case( +              {ArgumentCondition( +                   0U, OutOfRange, +                   {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), +               ReturnValueCondition(WithinRange, SingleValue(0))}, +              ErrnoIrrelevant, "Assuming the character is non-alphanumeric") +          .ArgConstraint(ArgumentCondition(0U, WithinRange, +                                           {{EOFv, EOFv}, {0, UCharRangeMax}}, +                                           "an unsigned char value or EOF"))); +  addToFunctionSummaryMap( +      "isalpha", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .Case({ArgumentCondition(0U, WithinRange, {{'A', 'Z'}, {'a', 'z'}}), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, "Assuming the character is alphabetical") +          // The locale-specific range. +          .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, +                ErrnoIrrelevant) +          .Case({ArgumentCondition( +                     0U, OutOfRange, +                     {{'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant, "Assuming the character is non-alphabetical")); +  addToFunctionSummaryMap( +      "isascii", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .Case({ArgumentCondition(0U, WithinRange, Range(0, 127)), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, "Assuming the character is an ASCII character") +          .Case({ArgumentCondition(0U, OutOfRange, Range(0, 127)), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is not an ASCII character")); +  addToFunctionSummaryMap( +      "isblank", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .Case({ArgumentCondition(0U, WithinRange, {{'\t', '\t'}, {' ', ' '}}), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, "Assuming the character is a blank character") +          .Case({ArgumentCondition(0U, OutOfRange, {{'\t', '\t'}, {' ', ' '}}), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is not a blank character")); +  addToFunctionSummaryMap( +      "iscntrl", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .Case({ArgumentCondition(0U, WithinRange, {{0, 32}, {127, 127}}), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is a control character") +          .Case({ArgumentCondition(0U, OutOfRange, {{0, 32}, {127, 127}}), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is not a control character")); +  addToFunctionSummaryMap( +      "isdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .Case({ArgumentCondition(0U, WithinRange, Range('0', '9')), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, "Assuming the character is a digit") +          .Case({ArgumentCondition(0U, OutOfRange, Range('0', '9')), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant, "Assuming the character is not a digit")); +  addToFunctionSummaryMap( +      "isgraph", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .Case({ArgumentCondition(0U, WithinRange, Range(33, 126)), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character has graphical representation") +          .Case( +              {ArgumentCondition(0U, OutOfRange, Range(33, 126)), +               ReturnValueCondition(WithinRange, SingleValue(0))}, +              ErrnoIrrelevant, +              "Assuming the character does not have graphical representation")); +  addToFunctionSummaryMap( +      "islower", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          // Is certainly lowercase. +          .Case({ArgumentCondition(0U, WithinRange, Range('a', 'z')), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, "Assuming the character is a lowercase letter") +          // Is ascii but not lowercase. +          .Case({ArgumentCondition(0U, WithinRange, Range(0, 127)), +                 ArgumentCondition(0U, OutOfRange, Range('a', 'z')), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is not a lowercase letter") +          // The locale-specific range. +          .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, +                ErrnoIrrelevant) +          // Is not an unsigned char. +          .Case({ArgumentCondition(0U, OutOfRange, Range(0, UCharRangeMax)), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant)); +  addToFunctionSummaryMap( +      "isprint", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .Case({ArgumentCondition(0U, WithinRange, Range(32, 126)), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, "Assuming the character is printable") +          .Case({ArgumentCondition(0U, OutOfRange, Range(32, 126)), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant, "Assuming the character is non-printable")); +  addToFunctionSummaryMap( +      "ispunct", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .Case({ArgumentCondition( +                     0U, WithinRange, +                     {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, "Assuming the character is a punctuation mark") +          .Case({ArgumentCondition( +                     0U, OutOfRange, +                     {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is not a punctuation mark")); +  addToFunctionSummaryMap( +      "isspace", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          // Space, '\f', '\n', '\r', '\t', '\v'. +          .Case({ArgumentCondition(0U, WithinRange, {{9, 13}, {' ', ' '}}), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is a whitespace character") +          // The locale-specific range. +          .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, +                ErrnoIrrelevant) +          .Case({ArgumentCondition(0U, OutOfRange, +                                   {{9, 13}, {' ', ' '}, {128, UCharRangeMax}}), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is not a whitespace character")); +  addToFunctionSummaryMap( +      "isupper", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          // Is certainly uppercase. +          .Case({ArgumentCondition(0U, WithinRange, Range('A', 'Z')), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is an uppercase letter") +          // The locale-specific range. +          .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, +                ErrnoIrrelevant) +          // Other. +          .Case({ArgumentCondition(0U, OutOfRange, +                                   {{'A', 'Z'}, {128, UCharRangeMax}}), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is not an uppercase letter")); +  addToFunctionSummaryMap( +      "isxdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .Case({ArgumentCondition(0U, WithinRange, +                                   {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), +                 ReturnValueCondition(OutOfRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is a hexadecimal digit") +          .Case({ArgumentCondition(0U, OutOfRange, +                                   {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoIrrelevant, +                "Assuming the character is not a hexadecimal digit")); +  addToFunctionSummaryMap( +      "toupper", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .ArgConstraint(ArgumentCondition(0U, WithinRange, +                                           {{EOFv, EOFv}, {0, UCharRangeMax}}, +                                           "an unsigned char value or EOF"))); +  addToFunctionSummaryMap( +      "tolower", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .ArgConstraint(ArgumentCondition(0U, WithinRange, +                                           {{EOFv, EOFv}, {0, UCharRangeMax}}, +                                           "an unsigned char value or EOF"))); +  addToFunctionSummaryMap( +      "toascii", Signature(ArgTypes{IntTy}, RetType{IntTy}), +      Summary(EvalCallAsPure) +          .ArgConstraint(ArgumentCondition(0U, WithinRange, +                                           {{EOFv, EOFv}, {0, UCharRangeMax}}, +                                           "an unsigned char value or EOF"))); + +  addToFunctionSummaryMap( +      "getchar", Signature(ArgTypes{}, RetType{IntTy}), +      Summary(NoEvalCall) +          .Case({ReturnValueCondition(WithinRange, +                                      {{EOFv, EOFv}, {0, UCharRangeMax}})}, +                ErrnoIrrelevant)); + +  // read()-like functions that never return more than buffer size. +  auto FreadSummary = +      Summary(NoEvalCall) +          .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)), +                 ArgumentCondition(2U, WithinRange, Range(1, SizeMax)), +                 ReturnValueCondition(BO_LT, ArgNo(2)), +                 ReturnValueCondition(WithinRange, Range(0, SizeMax))}, +                ErrnoNEZeroIrrelevant, GenericFailureMsg) +          .Case({ArgumentCondition(1U, WithinRange, Range(1, SizeMax)), +                 ReturnValueCondition(BO_EQ, ArgNo(2)), +                 ReturnValueCondition(WithinRange, Range(0, SizeMax))}, +                ErrnoMustNotBeChecked, GenericSuccessMsg) +          .Case({ArgumentCondition(1U, WithinRange, SingleValue(0)), +                 ReturnValueCondition(WithinRange, SingleValue(0))}, +                ErrnoMustNotBeChecked, +                "Assuming that argument 'size' to '{0}' is 0") +          .ArgConstraint(NotNullBuffer(ArgNo(0), ArgNo(1), ArgNo(2))) +          .ArgConstraint(NotNull(ArgNo(3))) +          .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), +                                    /*BufSizeMultiplier=*/ArgNo(2))); + +  // size_t fread(void *restrict ptr, size_t size, size_t nitems, +  //              FILE *restrict stream); +  addToFunctionSummaryMap( +      "fread", +      Signature(ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, FilePtrRestrictTy}, +                RetType{SizeTy}), +      FreadSummary); +  // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, +  //               FILE *restrict stream); +  addToFunctionSummaryMap("fwrite", +                          Signature(ArgTypes{ConstVoidPtrRestrictTy, SizeTy, +                                             SizeTy, FilePtrRestrictTy}, +                                    RetType{SizeTy}), +                          FreadSummary); + +  std::optional<QualType> Ssize_tTy = lookupTy("ssize_t"); +  std::optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy); + +  auto ReadSummary = +      Summary(NoEvalCall) +          .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), +                 ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}, +                ErrnoIrrelevant); + +  // FIXME these are actually defined by POSIX and not by the C standard, we +  // should handle them together with the rest of the POSIX functions. +  // ssize_t read(int fildes, void *buf, size_t nbyte); +  addToFunctionSummaryMap( +      "read", Signature(ArgTypes{IntTy, VoidPtrTy, SizeTy}, RetType{Ssize_tTy}), +      ReadSummary); +  // ssize_t write(int fildes, const void *buf, size_t nbyte); +  addToFunctionSummaryMap( +      "write", +      Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy}, RetType{Ssize_tTy}), +      ReadSummary); + +  auto GetLineSummary = +      Summary(NoEvalCall) +          .Case({ReturnValueCondition(WithinRange, +                                      Range({-1, -1}, {1, Ssize_tMax}))}, +                ErrnoIrrelevant); + +  QualType CharPtrPtrRestrictTy = getRestrictTy(getPointerTy(CharPtrTy)); + +  // getline()-like functions either fail or read at least the delimiter. +  // FIXME these are actually defined by POSIX and not by the C standard, we +  // should handle them together with the rest of the POSIX functions. +  // ssize_t getline(char **restrict lineptr, size_t *restrict n, +  //                 FILE *restrict stream); +  addToFunctionSummaryMap( +      "getline", +      Signature( +          ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, FilePtrRestrictTy}, +          RetType{Ssize_tTy}), +      GetLineSummary); +  // ssize_t getdelim(char **restrict lineptr, size_t *restrict n, +  //                  int delimiter, FILE *restrict stream); +  addToFunctionSummaryMap( +      "getdelim", +      Signature(ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, IntTy, +                         FilePtrRestrictTy}, +                RetType{Ssize_tTy}), +      GetLineSummary); + +  { +    Summary GetenvSummary = +        Summary(NoEvalCall) +            .ArgConstraint(NotNull(ArgNo(0))) +            .Case({NotNull(Ret)}, ErrnoIrrelevant, +                  "Assuming the environment variable exists"); +    // In untrusted environments the envvar might not exist. +    if (!ShouldAssumeControlledEnvironment) +      GetenvSummary.Case({NotNull(Ret)->negate()}, ErrnoIrrelevant, +                         "Assuming the environment variable does not exist"); + +    // char *getenv(const char *name); +    addToFunctionSummaryMap( +        "getenv", Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}), +        std::move(GetenvSummary)); +  } + +  if (!ModelPOSIX) { +    // Without POSIX use of 'errno' is not specified (in these cases). +    // Add these functions without 'errno' checks. +    addToFunctionSummaryMap( +        {"getc", "fgetc"}, Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(WithinRange, +                                        {{EOFv, EOFv}, {0, UCharRangeMax}})}, +                  ErrnoIrrelevant) +            .ArgConstraint(NotNull(ArgNo(0)))); +  } else { +    const auto ReturnsZeroOrMinusOne = +        ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))}; +    const auto ReturnsZero = +        ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(0))}; +    const auto ReturnsMinusOne = +        ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(-1))}; +    const auto ReturnsEOF = +        ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(EOFv))}; +    const auto ReturnsNonnegative = +        ConstraintSet{ReturnValueCondition(WithinRange, Range(0, IntMax))}; +    const auto ReturnsNonZero = +        ConstraintSet{ReturnValueCondition(OutOfRange, SingleValue(0))}; +    const auto ReturnsFileDescriptor = +        ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))}; +    const auto &ReturnsValidFileDescriptor = ReturnsNonnegative; + +    auto ValidFileDescriptorOrAtFdcwd = [&](ArgNo ArgN) { +      return std::make_shared<RangeConstraint>( +          ArgN, WithinRange, Range({AT_FDCWDv, AT_FDCWDv}, {0, IntMax}), +          "a valid file descriptor or AT_FDCWD"); +    }; + +    // FILE *fopen(const char *restrict pathname, const char *restrict mode); +    addToFunctionSummaryMap( +        "fopen", +        Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy}, +                  RetType{FilePtrTy}), +        Summary(NoEvalCall) +            .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // FILE *fdopen(int fd, const char *mode); +    addToFunctionSummaryMap( +        "fdopen", +        Signature(ArgTypes{IntTy, ConstCharPtrTy}, RetType{FilePtrTy}), +        Summary(NoEvalCall) +            .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // FILE *tmpfile(void); +    addToFunctionSummaryMap( +        "tmpfile", Signature(ArgTypes{}, RetType{FilePtrTy}), +        Summary(NoEvalCall) +            .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg)); + +    // FILE *freopen(const char *restrict pathname, const char *restrict mode, +    //               FILE *restrict stream); +    addToFunctionSummaryMap( +        "freopen", +        Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy, +                           FilePtrRestrictTy}, +                  RetType{FilePtrTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(BO_EQ, ArgNo(2))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(1))) +            .ArgConstraint(NotNull(ArgNo(2)))); + +    // FILE *popen(const char *command, const char *type); +    addToFunctionSummaryMap( +        "popen", +        Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{FilePtrTy}), +        Summary(NoEvalCall) +            .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int fclose(FILE *stream); +    addToFunctionSummaryMap( +        "fclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsEOF, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int pclose(FILE *stream); +    addToFunctionSummaryMap( +        "pclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(WithinRange, {{0, IntMax}})}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    std::optional<QualType> Off_tTy = lookupTy("off_t"); +    std::optional<RangeInt> Off_tMax = getMaxValue(Off_tTy); + +    // int fgetc(FILE *stream); +    // 'getc' is the same as 'fgetc' but may be a macro +    addToFunctionSummaryMap( +        {"getc", "fgetc"}, Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(WithinRange, {{0, UCharRangeMax}})}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv))}, +                  ErrnoIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int fputc(int c, FILE *stream); +    // 'putc' is the same as 'fputc' but may be a macro +    addToFunctionSummaryMap( +        {"putc", "fputc"}, +        Signature(ArgTypes{IntTy, FilePtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case({ArgumentCondition(0, WithinRange, Range(0, UCharRangeMax)), +                   ReturnValueCondition(BO_EQ, ArgNo(0))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ArgumentCondition(0, OutOfRange, Range(0, UCharRangeMax)), +                   ReturnValueCondition(WithinRange, Range(0, UCharRangeMax))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv))}, +                  ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // char *fgets(char *restrict s, int n, FILE *restrict stream); +    addToFunctionSummaryMap( +        "fgets", +        Signature(ArgTypes{CharPtrRestrictTy, IntTy, FilePtrRestrictTy}, +                  RetType{CharPtrTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(BO_EQ, ArgNo(0))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({IsNull(Ret)}, ErrnoIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, IntMax))) +            .ArgConstraint( +                BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1))) +            .ArgConstraint(NotNull(ArgNo(2)))); + +    // int fputs(const char *restrict s, FILE *restrict stream); +    addToFunctionSummaryMap( +        "fputs", +        Signature(ArgTypes{ConstCharPtrRestrictTy, FilePtrRestrictTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsNonnegative, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv))}, +                  ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int ungetc(int c, FILE *stream); +    addToFunctionSummaryMap( +        "ungetc", Signature(ArgTypes{IntTy, FilePtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(BO_EQ, ArgNo(0)), +                   ArgumentCondition(0, WithinRange, {{0, UCharRangeMax}})}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv)), +                   ArgumentCondition(0, WithinRange, SingleValue(EOFv))}, +                  ErrnoNEZeroIrrelevant, +                  "Assuming that 'ungetc' fails because EOF was passed as " +                  "character") +            .Case({ReturnValueCondition(WithinRange, SingleValue(EOFv)), +                   ArgumentCondition(0, WithinRange, {{0, UCharRangeMax}})}, +                  ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ArgumentCondition( +                0, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int fseek(FILE *stream, long offset, int whence); +    // FIXME: It can be possible to get the 'SEEK_' values (like EOFv) and use +    // these for condition of arg 2. +    // Now the range [0,2] is used (the `SEEK_*` constants are usually 0,1,2). +    addToFunctionSummaryMap( +        "fseek", Signature(ArgTypes{FilePtrTy, LongTy, IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}}))); + +    // int fseeko(FILE *stream, off_t offset, int whence); +    addToFunctionSummaryMap( +        "fseeko", +        Signature(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(ArgumentCondition(2, WithinRange, {{0, 2}}))); + +    // int fgetpos(FILE *restrict stream, fpos_t *restrict pos); +    // From 'The Open Group Base Specifications Issue 7, 2018 edition': +    // "The fgetpos() function shall not change the setting of errno if +    // successful." +    addToFunctionSummaryMap( +        "fgetpos", +        Signature(ArgTypes{FilePtrRestrictTy, FPosTPtrRestrictTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoUnchanged, GenericSuccessMsg) +            .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int fsetpos(FILE *stream, const fpos_t *pos); +    // From 'The Open Group Base Specifications Issue 7, 2018 edition': +    // "The fsetpos() function shall not change the setting of errno if +    // successful." +    addToFunctionSummaryMap( +        "fsetpos", +        Signature(ArgTypes{FilePtrTy, ConstFPosTPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoUnchanged, GenericSuccessMsg) +            .Case(ReturnsNonZero, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int fflush(FILE *stream); +    addToFunctionSummaryMap( +        "fflush", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsEOF, ErrnoNEZeroIrrelevant, GenericFailureMsg)); + +    // long ftell(FILE *stream); +    // From 'The Open Group Base Specifications Issue 7, 2018 edition': +    // "The ftell() function shall not change the setting of errno if +    // successful." +    addToFunctionSummaryMap( +        "ftell", Signature(ArgTypes{FilePtrTy}, RetType{LongTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(WithinRange, Range(0, LongMax))}, +                  ErrnoUnchanged, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // off_t ftello(FILE *stream); +    addToFunctionSummaryMap( +        "ftello", Signature(ArgTypes{FilePtrTy}, RetType{Off_tTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(WithinRange, Range(0, Off_tMax))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int fileno(FILE *stream); +    // According to POSIX 'fileno' may fail and set 'errno'. +    // But in Linux it may fail only if the specified file pointer is invalid. +    // At many places 'fileno' is used without check for failure and a failure +    // case here would produce a large amount of likely false positive warnings. +    // To avoid this, we assume here that it does not fail. +    addToFunctionSummaryMap( +        "fileno", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsValidFileDescriptor, ErrnoUnchanged, GenericSuccessMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // void rewind(FILE *stream); +    // This function indicates error only by setting of 'errno'. +    addToFunctionSummaryMap("rewind", +                            Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}), +                            Summary(NoEvalCall) +                                .Case({}, ErrnoMustBeChecked) +                                .ArgConstraint(NotNull(ArgNo(0)))); + +    // void clearerr(FILE *stream); +    addToFunctionSummaryMap( +        "clearerr", Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // int feof(FILE *stream); +    addToFunctionSummaryMap( +        "feof", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // int ferror(FILE *stream); +    addToFunctionSummaryMap( +        "ferror", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // long a64l(const char *str64); +    addToFunctionSummaryMap( +        "a64l", Signature(ArgTypes{ConstCharPtrTy}, RetType{LongTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // char *l64a(long value); +    addToFunctionSummaryMap("l64a", +                            Signature(ArgTypes{LongTy}, RetType{CharPtrTy}), +                            Summary(NoEvalCall) +                                .ArgConstraint(ArgumentCondition( +                                    0, WithinRange, Range(0, LongMax)))); + +    // int open(const char *path, int oflag, ...); +    addToFunctionSummaryMap( +        "open", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, +                  GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int openat(int fd, const char *path, int oflag, ...); +    addToFunctionSummaryMap( +        "openat", +        Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, +                  GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int access(const char *pathname, int amode); +    addToFunctionSummaryMap( +        "access", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int faccessat(int dirfd, const char *pathname, int mode, int flags); +    addToFunctionSummaryMap( +        "faccessat", +        Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int dup(int fildes); +    addToFunctionSummaryMap( +        "dup", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, +                  GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // int dup2(int fildes1, int filedes2); +    addToFunctionSummaryMap( +        "dup2", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, +                  GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) +            .ArgConstraint( +                ArgumentCondition(1, WithinRange, Range(0, IntMax)))); + +    // int fdatasync(int fildes); +    addToFunctionSummaryMap( +        "fdatasync", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // int fnmatch(const char *pattern, const char *string, int flags); +    addToFunctionSummaryMap( +        "fnmatch", +        Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int fsync(int fildes); +    addToFunctionSummaryMap( +        "fsync", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // int truncate(const char *path, off_t length); +    addToFunctionSummaryMap( +        "truncate", +        Signature(ArgTypes{ConstCharPtrTy, Off_tTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int symlink(const char *oldpath, const char *newpath); +    addToFunctionSummaryMap( +        "symlink", +        Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int symlinkat(const char *oldpath, int newdirfd, const char *newpath); +    addToFunctionSummaryMap( +        "symlinkat", +        Signature(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(1))) +            .ArgConstraint(NotNull(ArgNo(2)))); + +    // int lockf(int fd, int cmd, off_t len); +    addToFunctionSummaryMap( +        "lockf", Signature(ArgTypes{IntTy, IntTy, Off_tTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    std::optional<QualType> Mode_tTy = lookupTy("mode_t"); + +    // int creat(const char *pathname, mode_t mode); +    addToFunctionSummaryMap( +        "creat", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, +                  GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // unsigned int sleep(unsigned int seconds); +    addToFunctionSummaryMap( +        "sleep", Signature(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}), +        Summary(NoEvalCall) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); + +    std::optional<QualType> DirTy = lookupTy("DIR"); +    std::optional<QualType> DirPtrTy = getPointerTy(DirTy); + +    // int dirfd(DIR *dirp); +    addToFunctionSummaryMap( +        "dirfd", Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, +                  GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // unsigned int alarm(unsigned int seconds); +    addToFunctionSummaryMap( +        "alarm", Signature(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}), +        Summary(NoEvalCall) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); + +    // int closedir(DIR *dir); +    addToFunctionSummaryMap( +        "closedir", Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // char *strdup(const char *s); +    addToFunctionSummaryMap( +        "strdup", Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // char *strndup(const char *s, size_t n); +    addToFunctionSummaryMap( +        "strndup", +        Signature(ArgTypes{ConstCharPtrTy, SizeTy}, RetType{CharPtrTy}), +        Summary(NoEvalCall) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint( +                ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); + +    // wchar_t *wcsdup(const wchar_t *s); +    addToFunctionSummaryMap( +        "wcsdup", Signature(ArgTypes{ConstWchar_tPtrTy}, RetType{Wchar_tPtrTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // int mkstemp(char *template); +    addToFunctionSummaryMap( +        "mkstemp", Signature(ArgTypes{CharPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, +                  GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // char *mkdtemp(char *template); +    addToFunctionSummaryMap( +        "mkdtemp", Signature(ArgTypes{CharPtrTy}, RetType{CharPtrTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(BO_EQ, ArgNo(0))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // char *getcwd(char *buf, size_t size); +    addToFunctionSummaryMap( +        "getcwd", Signature(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}), +        Summary(NoEvalCall) +            .Case({ArgumentCondition(1, WithinRange, Range(1, SizeMax)), +                   ReturnValueCondition(BO_EQ, ArgNo(0))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ArgumentCondition(1, WithinRange, SingleValue(0)), +                   IsNull(Ret)}, +                  ErrnoNEZeroIrrelevant, "Assuming that argument 'size' is 0") +            .Case({ArgumentCondition(1, WithinRange, Range(1, SizeMax)), +                   IsNull(Ret)}, +                  ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint( +                BufferSize(/*Buffer*/ ArgNo(0), /*BufSize*/ ArgNo(1))) +            .ArgConstraint( +                ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); + +    // int mkdir(const char *pathname, mode_t mode); +    addToFunctionSummaryMap( +        "mkdir", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int mkdirat(int dirfd, const char *pathname, mode_t mode); +    addToFunctionSummaryMap( +        "mkdirat", +        Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    std::optional<QualType> Dev_tTy = lookupTy("dev_t"); + +    // int mknod(const char *pathname, mode_t mode, dev_t dev); +    addToFunctionSummaryMap( +        "mknod", +        Signature(ArgTypes{ConstCharPtrTy, Mode_tTy, Dev_tTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev); +    addToFunctionSummaryMap( +        "mknodat", +        Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, Dev_tTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int chmod(const char *path, mode_t mode); +    addToFunctionSummaryMap( +        "chmod", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags); +    addToFunctionSummaryMap( +        "fchmodat", +        Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, IntTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int fchmod(int fildes, mode_t mode); +    addToFunctionSummaryMap( +        "fchmod", Signature(ArgTypes{IntTy, Mode_tTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    std::optional<QualType> Uid_tTy = lookupTy("uid_t"); +    std::optional<QualType> Gid_tTy = lookupTy("gid_t"); + +    // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, +    //              int flags); +    addToFunctionSummaryMap( +        "fchownat", +        Signature(ArgTypes{IntTy, ConstCharPtrTy, Uid_tTy, Gid_tTy, IntTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int chown(const char *path, uid_t owner, gid_t group); +    addToFunctionSummaryMap( +        "chown", +        Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int lchown(const char *path, uid_t owner, gid_t group); +    addToFunctionSummaryMap( +        "lchown", +        Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int fchown(int fildes, uid_t owner, gid_t group); +    addToFunctionSummaryMap( +        "fchown", Signature(ArgTypes{IntTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // int rmdir(const char *pathname); +    addToFunctionSummaryMap( +        "rmdir", Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int chdir(const char *path); +    addToFunctionSummaryMap( +        "chdir", Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int link(const char *oldpath, const char *newpath); +    addToFunctionSummaryMap( +        "link", +        Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int linkat(int fd1, const char *path1, int fd2, const char *path2, +    //            int flag); +    addToFunctionSummaryMap( +        "linkat", +        Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1))) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(2))) +            .ArgConstraint(NotNull(ArgNo(3)))); + +    // int unlink(const char *pathname); +    addToFunctionSummaryMap( +        "unlink", Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int unlinkat(int fd, const char *path, int flag); +    addToFunctionSummaryMap( +        "unlinkat", +        Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    std::optional<QualType> StructStatTy = lookupTy("stat"); +    std::optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy); +    std::optional<QualType> StructStatPtrRestrictTy = +        getRestrictTy(StructStatPtrTy); + +    // int fstat(int fd, struct stat *statbuf); +    addToFunctionSummaryMap( +        "fstat", Signature(ArgTypes{IntTy, StructStatPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int stat(const char *restrict path, struct stat *restrict buf); +    addToFunctionSummaryMap( +        "stat", +        Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int lstat(const char *restrict path, struct stat *restrict buf); +    addToFunctionSummaryMap( +        "lstat", +        Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int fstatat(int fd, const char *restrict path, +    //             struct stat *restrict buf, int flag); +    addToFunctionSummaryMap( +        "fstatat", +        Signature(ArgTypes{IntTy, ConstCharPtrRestrictTy, +                           StructStatPtrRestrictTy, IntTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1))) +            .ArgConstraint(NotNull(ArgNo(2)))); + +    // DIR *opendir(const char *name); +    addToFunctionSummaryMap( +        "opendir", Signature(ArgTypes{ConstCharPtrTy}, RetType{DirPtrTy}), +        Summary(NoEvalCall) +            .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // DIR *fdopendir(int fd); +    addToFunctionSummaryMap( +        "fdopendir", Signature(ArgTypes{IntTy}, RetType{DirPtrTy}), +        Summary(NoEvalCall) +            .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // int isatty(int fildes); +    addToFunctionSummaryMap( +        "isatty", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(WithinRange, Range(0, 1))}, +                  ErrnoIrrelevant) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // int close(int fildes); +    addToFunctionSummaryMap( +        "close", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(-1, IntMax)))); + +    // long fpathconf(int fildes, int name); +    addToFunctionSummaryMap("fpathconf", +                            Signature(ArgTypes{IntTy, IntTy}, RetType{LongTy}), +                            Summary(NoEvalCall) +                                .ArgConstraint(ArgumentCondition( +                                    0, WithinRange, Range(0, IntMax)))); + +    // long pathconf(const char *path, int name); +    addToFunctionSummaryMap( +        "pathconf", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{LongTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // void rewinddir(DIR *dir); +    addToFunctionSummaryMap( +        "rewinddir", Signature(ArgTypes{DirPtrTy}, RetType{VoidTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // void seekdir(DIR *dirp, long loc); +    addToFunctionSummaryMap( +        "seekdir", Signature(ArgTypes{DirPtrTy, LongTy}, RetType{VoidTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // int rand_r(unsigned int *seedp); +    addToFunctionSummaryMap( +        "rand_r", Signature(ArgTypes{UnsignedIntPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // void *mmap(void *addr, size_t length, int prot, int flags, int fd, +    // off_t offset); +    // FIXME: Improve for errno modeling. +    addToFunctionSummaryMap( +        "mmap", +        Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off_tTy}, +                  RetType{VoidPtrTy}), +        Summary(NoEvalCall) +            .ArgConstraint(ArgumentCondition(1, WithinRange, Range(1, SizeMax))) +            .ArgConstraint( +                ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); + +    std::optional<QualType> Off64_tTy = lookupTy("off64_t"); +    // void *mmap64(void *addr, size_t length, int prot, int flags, int fd, +    // off64_t offset); +    // FIXME: Improve for errno modeling. +    addToFunctionSummaryMap( +        "mmap64", +        Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off64_tTy}, +                  RetType{VoidPtrTy}), +        Summary(NoEvalCall) +            .ArgConstraint(ArgumentCondition(1, WithinRange, Range(1, SizeMax))) +            .ArgConstraint( +                ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); + +    // int pipe(int fildes[2]); +    addToFunctionSummaryMap( +        "pipe", Signature(ArgTypes{IntPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // off_t lseek(int fildes, off_t offset, int whence); +    // In the first case we can not tell for sure if it failed or not. +    // A return value different from of the expected offset (that is unknown +    // here) may indicate failure. For this reason we do not enforce the errno +    // check (can cause false positive). +    addToFunctionSummaryMap( +        "lseek", Signature(ArgTypes{IntTy, Off_tTy, IntTy}, RetType{Off_tTy}), +        Summary(NoEvalCall) +            .Case(ReturnsNonnegative, ErrnoIrrelevant) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // ssize_t readlink(const char *restrict path, char *restrict buf, +    //                  size_t bufsize); +    addToFunctionSummaryMap( +        "readlink", +        Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, +                  RetType{Ssize_tTy}), +        Summary(NoEvalCall) +            .Case({ArgumentCondition(2, WithinRange, Range(1, IntMax)), +                   ReturnValueCondition(LessThanOrEq, ArgNo(2)), +                   ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ArgumentCondition(2, WithinRange, SingleValue(0)), +                   ReturnValueCondition(WithinRange, SingleValue(0))}, +                  ErrnoMustNotBeChecked, +                  "Assuming that argument 'bufsize' is 0") +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1))) +            .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), +                                      /*BufSize=*/ArgNo(2))) +            .ArgConstraint( +                ArgumentCondition(2, WithinRange, Range(0, SizeMax)))); + +    // ssize_t readlinkat(int fd, const char *restrict path, +    //                    char *restrict buf, size_t bufsize); +    addToFunctionSummaryMap( +        "readlinkat", +        Signature( +            ArgTypes{IntTy, ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, +            RetType{Ssize_tTy}), +        Summary(NoEvalCall) +            .Case({ArgumentCondition(3, WithinRange, Range(1, IntMax)), +                   ReturnValueCondition(LessThanOrEq, ArgNo(3)), +                   ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ArgumentCondition(3, WithinRange, SingleValue(0)), +                   ReturnValueCondition(WithinRange, SingleValue(0))}, +                  ErrnoMustNotBeChecked, +                  "Assuming that argument 'bufsize' is 0") +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1))) +            .ArgConstraint(NotNull(ArgNo(2))) +            .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2), +                                      /*BufSize=*/ArgNo(3))) +            .ArgConstraint( +                ArgumentCondition(3, WithinRange, Range(0, SizeMax)))); + +    // int renameat(int olddirfd, const char *oldpath, int newdirfd, const char +    // *newpath); +    addToFunctionSummaryMap( +        "renameat", +        Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1))) +            .ArgConstraint(ValidFileDescriptorOrAtFdcwd(ArgNo(2))) +            .ArgConstraint(NotNull(ArgNo(3)))); + +    // char *realpath(const char *restrict file_name, +    //                char *restrict resolved_name); +    // FIXME: If the argument 'resolved_name' is not NULL, macro 'PATH_MAX' +    //        should be defined in "limits.h" to guarrantee a success. +    addToFunctionSummaryMap( +        "realpath", +        Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy}, +                  RetType{CharPtrTy}), +        Summary(NoEvalCall) +            .Case({NotNull(Ret)}, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({IsNull(Ret)}, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    QualType CharPtrConstPtr = getPointerTy(getConstTy(CharPtrTy)); + +    // int execv(const char *path, char *const argv[]); +    addToFunctionSummaryMap( +        "execv", +        Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int execvp(const char *file, char *const argv[]); +    addToFunctionSummaryMap( +        "execvp", +        Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int getopt(int argc, char * const argv[], const char *optstring); +    addToFunctionSummaryMap( +        "getopt", +        Signature(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(WithinRange, Range(-1, UCharRangeMax))}, +                  ErrnoIrrelevant) +            .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) +            .ArgConstraint(NotNull(ArgNo(1))) +            .ArgConstraint(NotNull(ArgNo(2)))); + +    std::optional<QualType> StructSockaddrTy = lookupTy("sockaddr"); +    std::optional<QualType> StructSockaddrPtrTy = +        getPointerTy(StructSockaddrTy); +    std::optional<QualType> ConstStructSockaddrPtrTy = +        getPointerTy(getConstTy(StructSockaddrTy)); +    std::optional<QualType> StructSockaddrPtrRestrictTy = +        getRestrictTy(StructSockaddrPtrTy); +    std::optional<QualType> ConstStructSockaddrPtrRestrictTy = +        getRestrictTy(ConstStructSockaddrPtrTy); +    std::optional<QualType> Socklen_tTy = lookupTy("socklen_t"); +    std::optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy); +    std::optional<QualType> Socklen_tPtrRestrictTy = +        getRestrictTy(Socklen_tPtrTy); +    std::optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy); + +    // In 'socket.h' of some libc implementations with C99, sockaddr parameter +    // is a transparent union of the underlying sockaddr_ family of pointers +    // instead of being a pointer to struct sockaddr. In these cases, the +    // standardized signature will not match, thus we try to match with another +    // signature that has the joker Irrelevant type. We also remove those +    // constraints which require pointer types for the sockaddr param. + +    // int socket(int domain, int type, int protocol); +    addToFunctionSummaryMap( +        "socket", Signature(ArgTypes{IntTy, IntTy, IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, +                  GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg)); + +    auto Accept = +        Summary(NoEvalCall) +            .Case(ReturnsValidFileDescriptor, ErrnoMustNotBeChecked, +                  GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))); +    if (!addToFunctionSummaryMap( +            "accept", +            // int accept(int socket, struct sockaddr *restrict address, +            //            socklen_t *restrict address_len); +            Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, +                               Socklen_tPtrRestrictTy}, +                      RetType{IntTy}), +            Accept)) +      addToFunctionSummaryMap( +          "accept", +          Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, +                    RetType{IntTy}), +          Accept); + +    // int bind(int socket, const struct sockaddr *address, socklen_t +    //          address_len); +    if (!addToFunctionSummaryMap( +            "bind", +            Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy}, +                      RetType{IntTy}), +            Summary(NoEvalCall) +                .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +                .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +                .ArgConstraint( +                    ArgumentCondition(0, WithinRange, Range(0, IntMax))) +                .ArgConstraint(NotNull(ArgNo(1))) +                .ArgConstraint( +                    BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2))) +                .ArgConstraint( +                    ArgumentCondition(2, WithinRange, Range(0, Socklen_tMax))))) +      // Do not add constraints on sockaddr. +      addToFunctionSummaryMap( +          "bind", +          Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}), +          Summary(NoEvalCall) +              .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +              .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +              .ArgConstraint( +                  ArgumentCondition(0, WithinRange, Range(0, IntMax))) +              .ArgConstraint( +                  ArgumentCondition(2, WithinRange, Range(0, Socklen_tMax)))); + +    // int getpeername(int socket, struct sockaddr *restrict address, +    //                 socklen_t *restrict address_len); +    if (!addToFunctionSummaryMap( +            "getpeername", +            Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, +                               Socklen_tPtrRestrictTy}, +                      RetType{IntTy}), +            Summary(NoEvalCall) +                .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +                .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +                .ArgConstraint( +                    ArgumentCondition(0, WithinRange, Range(0, IntMax))) +                .ArgConstraint(NotNull(ArgNo(1))) +                .ArgConstraint(NotNull(ArgNo(2))))) +      addToFunctionSummaryMap( +          "getpeername", +          Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, +                    RetType{IntTy}), +          Summary(NoEvalCall) +              .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +              .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +              .ArgConstraint( +                  ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // int getsockname(int socket, struct sockaddr *restrict address, +    //                 socklen_t *restrict address_len); +    if (!addToFunctionSummaryMap( +            "getsockname", +            Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, +                               Socklen_tPtrRestrictTy}, +                      RetType{IntTy}), +            Summary(NoEvalCall) +                .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +                .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +                .ArgConstraint( +                    ArgumentCondition(0, WithinRange, Range(0, IntMax))) +                .ArgConstraint(NotNull(ArgNo(1))) +                .ArgConstraint(NotNull(ArgNo(2))))) +      addToFunctionSummaryMap( +          "getsockname", +          Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, +                    RetType{IntTy}), +          Summary(NoEvalCall) +              .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +              .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +              .ArgConstraint( +                  ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // int connect(int socket, const struct sockaddr *address, socklen_t +    //             address_len); +    if (!addToFunctionSummaryMap( +            "connect", +            Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy}, +                      RetType{IntTy}), +            Summary(NoEvalCall) +                .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +                .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +                .ArgConstraint( +                    ArgumentCondition(0, WithinRange, Range(0, IntMax))) +                .ArgConstraint(NotNull(ArgNo(1))))) +      addToFunctionSummaryMap( +          "connect", +          Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}), +          Summary(NoEvalCall) +              .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +              .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +              .ArgConstraint( +                  ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    auto Recvfrom = +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), +                   ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ReturnValueCondition(WithinRange, SingleValue(0)), +                   ArgumentCondition(2, WithinRange, SingleValue(0))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) +            .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), +                                      /*BufSize=*/ArgNo(2))); +    if (!addToFunctionSummaryMap( +            "recvfrom", +            // ssize_t recvfrom(int socket, void *restrict buffer, +            //                  size_t length, +            //                  int flags, struct sockaddr *restrict address, +            //                  socklen_t *restrict address_len); +            Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy, +                               StructSockaddrPtrRestrictTy, +                               Socklen_tPtrRestrictTy}, +                      RetType{Ssize_tTy}), +            Recvfrom)) +      addToFunctionSummaryMap( +          "recvfrom", +          Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy, +                             Irrelevant, Socklen_tPtrRestrictTy}, +                    RetType{Ssize_tTy}), +          Recvfrom); + +    auto Sendto = +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), +                   ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ReturnValueCondition(WithinRange, SingleValue(0)), +                   ArgumentCondition(2, WithinRange, SingleValue(0))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) +            .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), +                                      /*BufSize=*/ArgNo(2))); +    if (!addToFunctionSummaryMap( +            "sendto", +            // ssize_t sendto(int socket, const void *message, size_t length, +            //                int flags, const struct sockaddr *dest_addr, +            //                socklen_t dest_len); +            Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, +                               ConstStructSockaddrPtrTy, Socklen_tTy}, +                      RetType{Ssize_tTy}), +            Sendto)) +      addToFunctionSummaryMap( +          "sendto", +          Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, Irrelevant, +                             Socklen_tTy}, +                    RetType{Ssize_tTy}), +          Sendto); + +    // int listen(int sockfd, int backlog); +    addToFunctionSummaryMap( +        "listen", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // ssize_t recv(int sockfd, void *buf, size_t len, int flags); +    addToFunctionSummaryMap( +        "recv", +        Signature(ArgTypes{IntTy, VoidPtrTy, SizeTy, IntTy}, +                  RetType{Ssize_tTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), +                   ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ReturnValueCondition(WithinRange, SingleValue(0)), +                   ArgumentCondition(2, WithinRange, SingleValue(0))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) +            .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), +                                      /*BufSize=*/ArgNo(2)))); + +    std::optional<QualType> StructMsghdrTy = lookupTy("msghdr"); +    std::optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); +    std::optional<QualType> ConstStructMsghdrPtrTy = +        getPointerTy(getConstTy(StructMsghdrTy)); + +    // ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); +    addToFunctionSummaryMap( +        "recvmsg", +        Signature(ArgTypes{IntTy, StructMsghdrPtrTy, IntTy}, +                  RetType{Ssize_tTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); +    addToFunctionSummaryMap( +        "sendmsg", +        Signature(ArgTypes{IntTy, ConstStructMsghdrPtrTy, IntTy}, +                  RetType{Ssize_tTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // int setsockopt(int socket, int level, int option_name, +    //                const void *option_value, socklen_t option_len); +    addToFunctionSummaryMap( +        "setsockopt", +        Signature(ArgTypes{IntTy, IntTy, IntTy, ConstVoidPtrTy, Socklen_tTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(3))) +            .ArgConstraint( +                BufferSize(/*Buffer=*/ArgNo(3), /*BufSize=*/ArgNo(4))) +            .ArgConstraint( +                ArgumentCondition(4, WithinRange, Range(0, Socklen_tMax)))); + +    // int getsockopt(int socket, int level, int option_name, +    //                void *restrict option_value, +    //                socklen_t *restrict option_len); +    addToFunctionSummaryMap( +        "getsockopt", +        Signature(ArgTypes{IntTy, IntTy, IntTy, VoidPtrRestrictTy, +                           Socklen_tPtrRestrictTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(3))) +            .ArgConstraint(NotNull(ArgNo(4)))); + +    // ssize_t send(int sockfd, const void *buf, size_t len, int flags); +    addToFunctionSummaryMap( +        "send", +        Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy}, +                  RetType{Ssize_tTy}), +        Summary(NoEvalCall) +            .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), +                   ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case({ReturnValueCondition(WithinRange, SingleValue(0)), +                   ArgumentCondition(2, WithinRange, SingleValue(0))}, +                  ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) +            .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), +                                      /*BufSize=*/ArgNo(2)))); + +    // int socketpair(int domain, int type, int protocol, int sv[2]); +    addToFunctionSummaryMap( +        "socketpair", +        Signature(ArgTypes{IntTy, IntTy, IntTy, IntPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(3)))); + +    // int shutdown(int socket, int how); +    addToFunctionSummaryMap( +        "shutdown", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, +    //                 char *restrict node, socklen_t nodelen, +    //                 char *restrict service, +    //                 socklen_t servicelen, int flags); +    // +    // This is defined in netdb.h. And contrary to 'socket.h', the sockaddr +    // parameter is never handled as a transparent union in netdb.h +    addToFunctionSummaryMap( +        "getnameinfo", +        Signature(ArgTypes{ConstStructSockaddrPtrRestrictTy, Socklen_tTy, +                           CharPtrRestrictTy, Socklen_tTy, CharPtrRestrictTy, +                           Socklen_tTy, IntTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .ArgConstraint( +                BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1))) +            .ArgConstraint( +                ArgumentCondition(1, WithinRange, Range(0, Socklen_tMax))) +            .ArgConstraint( +                BufferSize(/*Buffer=*/ArgNo(2), /*BufSize=*/ArgNo(3))) +            .ArgConstraint( +                ArgumentCondition(3, WithinRange, Range(0, Socklen_tMax))) +            .ArgConstraint( +                BufferSize(/*Buffer=*/ArgNo(4), /*BufSize=*/ArgNo(5))) +            .ArgConstraint( +                ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax)))); + +    std::optional<QualType> StructUtimbufTy = lookupTy("utimbuf"); +    std::optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy); + +    // int utime(const char *filename, struct utimbuf *buf); +    addToFunctionSummaryMap( +        "utime", +        Signature(ArgTypes{ConstCharPtrTy, StructUtimbufPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    std::optional<QualType> StructTimespecTy = lookupTy("timespec"); +    std::optional<QualType> StructTimespecPtrTy = +        getPointerTy(StructTimespecTy); +    std::optional<QualType> ConstStructTimespecPtrTy = +        getPointerTy(getConstTy(StructTimespecTy)); + +    // int futimens(int fd, const struct timespec times[2]); +    addToFunctionSummaryMap( +        "futimens", +        Signature(ArgTypes{IntTy, ConstStructTimespecPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint( +                ArgumentCondition(0, WithinRange, Range(0, IntMax)))); + +    // int utimensat(int dirfd, const char *pathname, +    //               const struct timespec times[2], int flags); +    addToFunctionSummaryMap( +        "utimensat", +        Signature( +            ArgTypes{IntTy, ConstCharPtrTy, ConstStructTimespecPtrTy, IntTy}, +            RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    std::optional<QualType> StructTimevalTy = lookupTy("timeval"); +    std::optional<QualType> ConstStructTimevalPtrTy = +        getPointerTy(getConstTy(StructTimevalTy)); + +    // int utimes(const char *filename, const struct timeval times[2]); +    addToFunctionSummaryMap( +        "utimes", +        Signature(ArgTypes{ConstCharPtrTy, ConstStructTimevalPtrTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); +    addToFunctionSummaryMap( +        "nanosleep", +        Signature(ArgTypes{ConstStructTimespecPtrTy, StructTimespecPtrTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(0)))); + +    std::optional<QualType> Time_tTy = lookupTy("time_t"); +    std::optional<QualType> ConstTime_tPtrTy = +        getPointerTy(getConstTy(Time_tTy)); +    std::optional<QualType> ConstTime_tPtrRestrictTy = +        getRestrictTy(ConstTime_tPtrTy); + +    std::optional<QualType> StructTmTy = lookupTy("tm"); +    std::optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy); +    std::optional<QualType> StructTmPtrRestrictTy = +        getRestrictTy(StructTmPtrTy); +    std::optional<QualType> ConstStructTmPtrTy = +        getPointerTy(getConstTy(StructTmTy)); +    std::optional<QualType> ConstStructTmPtrRestrictTy = +        getRestrictTy(ConstStructTmPtrTy); + +    // struct tm * localtime(const time_t *tp); +    addToFunctionSummaryMap( +        "localtime", +        Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // struct tm *localtime_r(const time_t *restrict timer, +    //                        struct tm *restrict result); +    addToFunctionSummaryMap( +        "localtime_r", +        Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy}, +                  RetType{StructTmPtrTy}), +        Summary(NoEvalCall) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // char *asctime_r(const struct tm *restrict tm, char *restrict buf); +    addToFunctionSummaryMap( +        "asctime_r", +        Signature(ArgTypes{ConstStructTmPtrRestrictTy, CharPtrRestrictTy}, +                  RetType{CharPtrTy}), +        Summary(NoEvalCall) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1))) +            .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), +                                      /*MinBufSize=*/BVF.getValue(26, IntTy)))); + +    // char *ctime_r(const time_t *timep, char *buf); +    addToFunctionSummaryMap( +        "ctime_r", +        Signature(ArgTypes{ConstTime_tPtrTy, CharPtrTy}, RetType{CharPtrTy}), +        Summary(NoEvalCall) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1))) +            .ArgConstraint(BufferSize( +                /*Buffer=*/ArgNo(1), +                /*MinBufSize=*/BVF.getValue(26, IntTy)))); + +    // struct tm *gmtime_r(const time_t *restrict timer, +    //                     struct tm *restrict result); +    addToFunctionSummaryMap( +        "gmtime_r", +        Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy}, +                  RetType{StructTmPtrTy}), +        Summary(NoEvalCall) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // struct tm * gmtime(const time_t *tp); +    addToFunctionSummaryMap( +        "gmtime", Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    std::optional<QualType> Clockid_tTy = lookupTy("clockid_t"); + +    // int clock_gettime(clockid_t clock_id, struct timespec *tp); +    addToFunctionSummaryMap( +        "clock_gettime", +        Signature(ArgTypes{Clockid_tTy, StructTimespecPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    std::optional<QualType> StructItimervalTy = lookupTy("itimerval"); +    std::optional<QualType> StructItimervalPtrTy = +        getPointerTy(StructItimervalTy); + +    // int getitimer(int which, struct itimerval *curr_value); +    addToFunctionSummaryMap( +        "getitimer", +        Signature(ArgTypes{IntTy, StructItimervalPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .Case(ReturnsZero, ErrnoMustNotBeChecked, GenericSuccessMsg) +            .Case(ReturnsMinusOne, ErrnoNEZeroIrrelevant, GenericFailureMsg) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    std::optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t"); +    std::optional<QualType> Pthread_cond_tPtrTy = +        getPointerTy(Pthread_cond_tTy); +    std::optional<QualType> Pthread_tTy = lookupTy("pthread_t"); +    std::optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy); +    std::optional<QualType> Pthread_tPtrRestrictTy = +        getRestrictTy(Pthread_tPtrTy); +    std::optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t"); +    std::optional<QualType> Pthread_mutex_tPtrTy = +        getPointerTy(Pthread_mutex_tTy); +    std::optional<QualType> Pthread_mutex_tPtrRestrictTy = +        getRestrictTy(Pthread_mutex_tPtrTy); +    std::optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t"); +    std::optional<QualType> Pthread_attr_tPtrTy = +        getPointerTy(Pthread_attr_tTy); +    std::optional<QualType> ConstPthread_attr_tPtrTy = +        getPointerTy(getConstTy(Pthread_attr_tTy)); +    std::optional<QualType> ConstPthread_attr_tPtrRestrictTy = +        getRestrictTy(ConstPthread_attr_tPtrTy); +    std::optional<QualType> Pthread_mutexattr_tTy = +        lookupTy("pthread_mutexattr_t"); +    std::optional<QualType> ConstPthread_mutexattr_tPtrTy = +        getPointerTy(getConstTy(Pthread_mutexattr_tTy)); +    std::optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy = +        getRestrictTy(ConstPthread_mutexattr_tPtrTy); + +    QualType PthreadStartRoutineTy = getPointerTy( +        ACtx.getFunctionType(/*ResultTy=*/VoidPtrTy, /*Args=*/VoidPtrTy, +                             FunctionProtoType::ExtProtoInfo())); + +    // int pthread_cond_signal(pthread_cond_t *cond); +    // int pthread_cond_broadcast(pthread_cond_t *cond); +    addToFunctionSummaryMap( +        {"pthread_cond_signal", "pthread_cond_broadcast"}, +        Signature(ArgTypes{Pthread_cond_tPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // int pthread_create(pthread_t *restrict thread, +    //                    const pthread_attr_t *restrict attr, +    //                    void *(*start_routine)(void*), void *restrict arg); +    addToFunctionSummaryMap( +        "pthread_create", +        Signature(ArgTypes{Pthread_tPtrRestrictTy, +                           ConstPthread_attr_tPtrRestrictTy, +                           PthreadStartRoutineTy, VoidPtrRestrictTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(2)))); + +    // int pthread_attr_destroy(pthread_attr_t *attr); +    // int pthread_attr_init(pthread_attr_t *attr); +    addToFunctionSummaryMap( +        {"pthread_attr_destroy", "pthread_attr_init"}, +        Signature(ArgTypes{Pthread_attr_tPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, +    //                               size_t *restrict stacksize); +    // int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, +    //                               size_t *restrict guardsize); +    addToFunctionSummaryMap( +        {"pthread_attr_getstacksize", "pthread_attr_getguardsize"}, +        Signature(ArgTypes{ConstPthread_attr_tPtrRestrictTy, SizePtrRestrictTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); + +    // int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); +    // int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); +    addToFunctionSummaryMap( +        {"pthread_attr_setstacksize", "pthread_attr_setguardsize"}, +        Signature(ArgTypes{Pthread_attr_tPtrTy, SizeTy}, RetType{IntTy}), +        Summary(NoEvalCall) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint( +                ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); + +    // int pthread_mutex_init(pthread_mutex_t *restrict mutex, const +    //                        pthread_mutexattr_t *restrict attr); +    addToFunctionSummaryMap( +        "pthread_mutex_init", +        Signature(ArgTypes{Pthread_mutex_tPtrRestrictTy, +                           ConstPthread_mutexattr_tPtrRestrictTy}, +                  RetType{IntTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); + +    // int pthread_mutex_destroy(pthread_mutex_t *mutex); +    // int pthread_mutex_lock(pthread_mutex_t *mutex); +    // int pthread_mutex_trylock(pthread_mutex_t *mutex); +    // int pthread_mutex_unlock(pthread_mutex_t *mutex); +    addToFunctionSummaryMap( +        {"pthread_mutex_destroy", "pthread_mutex_lock", "pthread_mutex_trylock", +         "pthread_mutex_unlock"}, +        Signature(ArgTypes{Pthread_mutex_tPtrTy}, RetType{IntTy}), +        Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); +  } + +  // Functions for testing. +  if (AddTestFunctions) { +    const RangeInt IntMin = BVF.getMinValue(IntTy).getLimitedValue(); + +    addToFunctionSummaryMap( +        "__not_null", Signature(ArgTypes{IntPtrTy}, RetType{IntTy}), +        Summary(EvalCallAsPure).ArgConstraint(NotNull(ArgNo(0)))); + +    addToFunctionSummaryMap( +        "__not_null_buffer", +        Signature(ArgTypes{VoidPtrTy, IntTy, IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(NotNullBuffer(ArgNo(0), ArgNo(1), ArgNo(2)))); + +    // Test inside range constraints. +    addToFunctionSummaryMap( +        "__single_val_0", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(0)))); +    addToFunctionSummaryMap( +        "__single_val_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(1)))); +    addToFunctionSummaryMap( +        "__range_1_2", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(1, 2)))); +    addToFunctionSummaryMap( +        "__range_m1_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-1, 1)))); +    addToFunctionSummaryMap( +        "__range_m2_m1", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-2, -1)))); +    addToFunctionSummaryMap( +        "__range_m10_10", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-10, 10)))); +    addToFunctionSummaryMap("__range_m1_inf", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, WithinRange, Range(-1, IntMax)))); +    addToFunctionSummaryMap("__range_0_inf", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, WithinRange, Range(0, IntMax)))); +    addToFunctionSummaryMap("__range_1_inf", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, WithinRange, Range(1, IntMax)))); +    addToFunctionSummaryMap("__range_minf_m1", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, WithinRange, Range(IntMin, -1)))); +    addToFunctionSummaryMap("__range_minf_0", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, WithinRange, Range(IntMin, 0)))); +    addToFunctionSummaryMap("__range_minf_1", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, WithinRange, Range(IntMin, 1)))); +    addToFunctionSummaryMap("__range_1_2__4_6", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, WithinRange, Range({1, 2}, {4, 6})))); +    addToFunctionSummaryMap( +        "__range_1_2__4_inf", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, WithinRange, +                                             Range({1, 2}, {4, IntMax})))); + +    // Test out of range constraints. +    addToFunctionSummaryMap( +        "__single_val_out_0", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(0)))); +    addToFunctionSummaryMap( +        "__single_val_out_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1)))); +    addToFunctionSummaryMap( +        "__range_out_1_2", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(1, 2)))); +    addToFunctionSummaryMap( +        "__range_out_m1_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-1, 1)))); +    addToFunctionSummaryMap( +        "__range_out_m2_m1", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-2, -1)))); +    addToFunctionSummaryMap( +        "__range_out_m10_10", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-10, 10)))); +    addToFunctionSummaryMap("__range_out_m1_inf", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, OutOfRange, Range(-1, IntMax)))); +    addToFunctionSummaryMap("__range_out_0_inf", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, OutOfRange, Range(0, IntMax)))); +    addToFunctionSummaryMap("__range_out_1_inf", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, OutOfRange, Range(1, IntMax)))); +    addToFunctionSummaryMap("__range_out_minf_m1", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, OutOfRange, Range(IntMin, -1)))); +    addToFunctionSummaryMap("__range_out_minf_0", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, OutOfRange, Range(IntMin, 0)))); +    addToFunctionSummaryMap("__range_out_minf_1", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, OutOfRange, Range(IntMin, 1)))); +    addToFunctionSummaryMap("__range_out_1_2__4_6", +                            Signature(ArgTypes{IntTy}, RetType{IntTy}), +                            Summary(EvalCallAsPure) +                                .ArgConstraint(ArgumentCondition( +                                    0U, OutOfRange, Range({1, 2}, {4, 6})))); +    addToFunctionSummaryMap( +        "__range_out_1_2__4_inf", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint( +                ArgumentCondition(0U, OutOfRange, Range({1, 2}, {4, IntMax})))); + +    // Test range kind. +    addToFunctionSummaryMap( +        "__within", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(1)))); +    addToFunctionSummaryMap( +        "__out_of", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1)))); + +    addToFunctionSummaryMap( +        "__two_constrained_args", +        Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(1))) +            .ArgConstraint(ArgumentCondition(1U, WithinRange, SingleValue(1)))); +    addToFunctionSummaryMap( +        "__arg_constrained_twice", Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1))) +            .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(2)))); +    addToFunctionSummaryMap( +        "__defaultparam", +        Signature(ArgTypes{Irrelevant, IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure).ArgConstraint(NotNull(ArgNo(0)))); +    addToFunctionSummaryMap( +        "__variadic", +        Signature(ArgTypes{VoidPtrTy, ConstCharPtrTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(NotNull(ArgNo(0))) +            .ArgConstraint(NotNull(ArgNo(1)))); +    addToFunctionSummaryMap( +        "__buf_size_arg_constraint", +        Signature(ArgTypes{ConstVoidPtrTy, SizeTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint( +                BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1)))); +    addToFunctionSummaryMap( +        "__buf_size_arg_constraint_mul", +        Signature(ArgTypes{ConstVoidPtrTy, SizeTy, SizeTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), +                                      /*BufSizeMultiplier=*/ArgNo(2)))); +    addToFunctionSummaryMap( +        "__buf_size_arg_constraint_concrete", +        Signature(ArgTypes{ConstVoidPtrTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), +                                      /*BufSize=*/BVF.getValue(10, IntTy)))); +    addToFunctionSummaryMap( +        {"__test_restrict_param_0", "__test_restrict_param_1", +         "__test_restrict_param_2"}, +        Signature(ArgTypes{VoidPtrRestrictTy}, RetType{VoidTy}), +        Summary(EvalCallAsPure)); + +    // Test the application of cases. +    addToFunctionSummaryMap( +        "__test_case_note", Signature(ArgTypes{}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .Case({ReturnValueCondition(WithinRange, SingleValue(0))}, +                  ErrnoIrrelevant, "Function returns 0") +            .Case({ReturnValueCondition(WithinRange, SingleValue(1))}, +                  ErrnoIrrelevant, "Function returns 1")); +    addToFunctionSummaryMap( +        "__test_case_range_1_2__4_6", +        Signature(ArgTypes{IntTy}, RetType{IntTy}), +        Summary(EvalCallAsPure) +            .Case({ArgumentCondition(0U, WithinRange, +                                     IntRangeVector{{IntMin, 0}, {3, 3}}), +                   ReturnValueCondition(WithinRange, SingleValue(1))}, +                  ErrnoIrrelevant) +            .Case({ArgumentCondition(0U, WithinRange, +                                     IntRangeVector{{3, 3}, {7, IntMax}}), +                   ReturnValueCondition(WithinRange, SingleValue(2))}, +                  ErrnoIrrelevant) +            .Case({ArgumentCondition(0U, WithinRange, +                                     IntRangeVector{{IntMin, 0}, {7, IntMax}}), +                   ReturnValueCondition(WithinRange, SingleValue(3))}, +                  ErrnoIrrelevant) +            .Case({ArgumentCondition( +                       0U, WithinRange, +                       IntRangeVector{{IntMin, 0}, {3, 3}, {7, IntMax}}), +                   ReturnValueCondition(WithinRange, SingleValue(4))}, +                  ErrnoIrrelevant)); +  } +} + +void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) { +  auto *Checker = mgr.registerChecker<StdLibraryFunctionsChecker>(); +  Checker->CheckName = mgr.getCurrentCheckerName(); +  const AnalyzerOptions &Opts = mgr.getAnalyzerOptions(); +  Checker->DisplayLoadedSummaries = +      Opts.getCheckerBooleanOption(Checker, "DisplayLoadedSummaries"); +  Checker->ModelPOSIX = Opts.getCheckerBooleanOption(Checker, "ModelPOSIX"); +  Checker->ShouldAssumeControlledEnvironment = +      Opts.ShouldAssumeControlledEnvironment; +} + +bool ento::shouldRegisterStdCLibraryFunctionsChecker( +    const CheckerManager &mgr) { +  return true; +} + +void ento::registerStdCLibraryFunctionsTesterChecker(CheckerManager &mgr) { +  auto *Checker = mgr.getChecker<StdLibraryFunctionsChecker>(); +  Checker->AddTestFunctions = true; +} + +bool ento::shouldRegisterStdCLibraryFunctionsTesterChecker( +    const CheckerManager &mgr) { +  return true; +} | 
