| 1 | //=== StdLibraryFunctionsChecker.cpp - Model standard functions -*- C++ -*-===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // This checker improves modeling of a few simple library functions. |
| 10 | // |
| 11 | // This checker provides a specification format - `Summary' - and |
| 12 | // contains descriptions of some library functions in this format. Each |
| 13 | // specification contains a list of branches for splitting the program state |
| 14 | // upon call, and range constraints on argument and return-value symbols that |
| 15 | // are satisfied on each branch. This spec can be expanded to include more |
| 16 | // items, like external effects of the function. |
| 17 | // |
| 18 | // The main difference between this approach and the body farms technique is |
| 19 | // in more explicit control over how many branches are produced. For example, |
| 20 | // consider standard C function `ispunct(int x)', which returns a non-zero value |
| 21 | // iff `x' is a punctuation character, that is, when `x' is in range |
| 22 | // ['!', '/'] [':', '@'] U ['[', '\`'] U ['{', '~']. |
| 23 | // `Summary' provides only two branches for this function. However, |
| 24 | // any attempt to describe this range with if-statements in the body farm |
| 25 | // would result in many more branches. Because each branch needs to be analyzed |
| 26 | // independently, this significantly reduces performance. Additionally, |
| 27 | // once we consider a branch on which `x' is in range, say, ['!', '/'], |
| 28 | // we assume that such branch is an important separate path through the program, |
| 29 | // which may lead to false positives because considering this particular path |
| 30 | // was not consciously intended, and therefore it might have been unreachable. |
| 31 | // |
| 32 | // This checker uses eval::Call for modeling pure functions (functions without |
| 33 | // side effects), for which their `Summary' is a precise model. This avoids |
| 34 | // unnecessary invalidation passes. Conflicts with other checkers are unlikely |
| 35 | // because if the function has no other effects, other checkers would probably |
| 36 | // never want to improve upon the modeling done by this checker. |
| 37 | // |
| 38 | // Non-pure functions, for which only partial improvement over the default |
| 39 | // behavior is expected, are modeled via check::PostCall, non-intrusively. |
| 40 | // |
| 41 | //===----------------------------------------------------------------------===// |
| 42 | |
| 43 | #include "ErrnoModeling.h" |
| 44 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
| 45 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
| 46 | #include "clang/StaticAnalyzer/Core/Checker.h" |
| 47 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
| 48 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
| 49 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
| 50 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" |
| 51 | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h" |
| 52 | #include "llvm/ADT/STLExtras.h" |
| 53 | #include "llvm/ADT/SmallString.h" |
| 54 | #include "llvm/ADT/StringExtras.h" |
| 55 | #include "llvm/Support/FormatVariadic.h" |
| 56 | |
| 57 | #include <optional> |
| 58 | #include <string> |
| 59 | |
| 60 | using namespace clang; |
| 61 | using namespace clang::ento; |
| 62 | |
| 63 | namespace { |
| 64 | class StdLibraryFunctionsChecker |
| 65 | : public Checker<check::PreCall, check::PostCall, eval::Call> { |
| 66 | |
| 67 | class Summary; |
| 68 | |
| 69 | /// Specify how much the analyzer engine should entrust modeling this function |
| 70 | /// to us. |
| 71 | enum InvalidationKind { |
| 72 | /// No \c eval::Call for the function, it can be modeled elsewhere. |
| 73 | /// This checker checks only pre and post conditions. |
| 74 | NoEvalCall, |
| 75 | /// The function is modeled completely in this checker. |
| 76 | EvalCallAsPure |
| 77 | }; |
| 78 | |
| 79 | /// Given a range, should the argument stay inside or outside this range? |
| 80 | enum RangeKind { OutOfRange, WithinRange }; |
| 81 | |
| 82 | static RangeKind negateKind(RangeKind K) { |
| 83 | switch (K) { |
| 84 | case OutOfRange: |
| 85 | return WithinRange; |
| 86 | case WithinRange: |
| 87 | return OutOfRange; |
| 88 | } |
| 89 | llvm_unreachable("Unknown range kind" ); |
| 90 | } |
| 91 | |
| 92 | /// The universal integral type to use in value range descriptions. |
| 93 | /// Unsigned to make sure overflows are well-defined. |
| 94 | typedef uint64_t RangeInt; |
| 95 | |
| 96 | /// Describes a single range constraint. Eg. {{0, 1}, {3, 4}} is |
| 97 | /// a non-negative integer, which less than 5 and not equal to 2. |
| 98 | typedef std::vector<std::pair<RangeInt, RangeInt>> IntRangeVector; |
| 99 | |
| 100 | /// A reference to an argument or return value by its number. |
| 101 | /// ArgNo in CallExpr and CallEvent is defined as Unsigned, but |
| 102 | /// obviously uint32_t should be enough for all practical purposes. |
| 103 | typedef uint32_t ArgNo; |
| 104 | /// Special argument number for specifying the return value. |
| 105 | static const ArgNo Ret; |
| 106 | |
| 107 | /// Get a string representation of an argument index. |
| 108 | /// E.g.: (1) -> '1st arg', (2) - > '2nd arg' |
| 109 | static void printArgDesc(ArgNo, llvm::raw_ostream &Out); |
| 110 | /// Print value X of the argument in form " (which is X)", |
| 111 | /// if the value is a fixed known value, otherwise print nothing. |
| 112 | /// This is used as simple explanation of values if possible. |
| 113 | static void printArgValueInfo(ArgNo ArgN, ProgramStateRef State, |
| 114 | const CallEvent &Call, llvm::raw_ostream &Out); |
| 115 | /// Append textual description of a numeric range [RMin,RMax] to |
| 116 | /// \p Out. |
| 117 | static void appendInsideRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax, |
| 118 | QualType ArgT, BasicValueFactory &BVF, |
| 119 | llvm::raw_ostream &Out); |
| 120 | /// Append textual description of a numeric range out of [RMin,RMax] to |
| 121 | /// \p Out. |
| 122 | static void appendOutOfRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax, |
| 123 | QualType ArgT, BasicValueFactory &BVF, |
| 124 | llvm::raw_ostream &Out); |
| 125 | |
| 126 | class ValueConstraint; |
| 127 | |
| 128 | /// Pointer to the ValueConstraint. We need a copyable, polymorphic and |
| 129 | /// default initializable type (vector needs that). A raw pointer was good, |
| 130 | /// however, we cannot default initialize that. unique_ptr makes the Summary |
| 131 | /// class non-copyable, therefore not an option. Releasing the copyability |
| 132 | /// requirement would render the initialization of the Summary map infeasible. |
| 133 | /// Mind that a pointer to a new value constraint is created when the negate |
| 134 | /// function is used. |
| 135 | using ValueConstraintPtr = std::shared_ptr<ValueConstraint>; |
| 136 | |
| 137 | /// Polymorphic base class that represents a constraint on a given argument |
| 138 | /// (or return value) of a function. Derived classes implement different kind |
| 139 | /// of constraints, e.g range constraints or correlation between two |
| 140 | /// arguments. |
| 141 | /// These are used as argument constraints (preconditions) of functions, in |
| 142 | /// which case a bug report may be emitted if the constraint is not satisfied. |
| 143 | /// Another use is as conditions for summary cases, to create different |
| 144 | /// classes of behavior for a function. In this case no description of the |
| 145 | /// constraint is needed because the summary cases have an own (not generated) |
| 146 | /// description string. |
| 147 | class ValueConstraint { |
| 148 | public: |
| 149 | ValueConstraint(ArgNo ArgN) : ArgN(ArgN) {} |
| 150 | virtual ~ValueConstraint() {} |
| 151 | |
| 152 | /// Apply the effects of the constraint on the given program state. If null |
| 153 | /// is returned then the constraint is not feasible. |
| 154 | virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 155 | const Summary &Summary, |
| 156 | CheckerContext &C) const = 0; |
| 157 | |
| 158 | /// Represents that in which context do we require a description of the |
| 159 | /// constraint. |
| 160 | enum DescriptionKind { |
| 161 | /// Describe a constraint that was violated. |
| 162 | /// Description should start with something like "should be". |
| 163 | Violation, |
| 164 | /// Describe a constraint that was assumed to be true. |
| 165 | /// This can be used when a precondition is satisfied, or when a summary |
| 166 | /// case is applied. |
| 167 | /// Description should start with something like "is". |
| 168 | Assumption |
| 169 | }; |
| 170 | |
| 171 | /// Give a description that explains the constraint to the user. Used when |
| 172 | /// a bug is reported or when the constraint is applied and displayed as a |
| 173 | /// note. The description should not mention the argument (getArgNo). |
| 174 | /// See StdLibraryFunctionsChecker::reportBug about how this function is |
| 175 | /// used (this function is used not only there). |
| 176 | virtual void describe(DescriptionKind DK, const CallEvent &Call, |
| 177 | ProgramStateRef State, const Summary &Summary, |
| 178 | llvm::raw_ostream &Out) const { |
| 179 | // There are some descendant classes that are not used as argument |
| 180 | // constraints, e.g. ComparisonConstraint. In that case we can safely |
| 181 | // ignore the implementation of this function. |
| 182 | llvm_unreachable( |
| 183 | "Description not implemented for summary case constraints" ); |
| 184 | } |
| 185 | |
| 186 | /// Give a description that explains the actual argument value (where the |
| 187 | /// current ValueConstraint applies to) to the user. This function should be |
| 188 | /// called only when the current constraint is satisfied by the argument. |
| 189 | /// It should produce a more precise description than the constraint itself. |
| 190 | /// The actual value of the argument and the program state can be used to |
| 191 | /// make the description more precise. In the most simple case, if the |
| 192 | /// argument has a fixed known value this value can be printed into \p Out, |
| 193 | /// this is done by default. |
| 194 | /// The function should return true if a description was printed to \p Out, |
| 195 | /// otherwise false. |
| 196 | /// See StdLibraryFunctionsChecker::reportBug about how this function is |
| 197 | /// used. |
| 198 | virtual bool describeArgumentValue(const CallEvent &Call, |
| 199 | ProgramStateRef State, |
| 200 | const Summary &Summary, |
| 201 | llvm::raw_ostream &Out) const { |
| 202 | if (auto N = getArgSVal(Call, ArgN: getArgNo()).getAs<NonLoc>()) { |
| 203 | if (const llvm::APSInt *Int = N->getAsInteger()) { |
| 204 | Out << *Int; |
| 205 | return true; |
| 206 | } |
| 207 | } |
| 208 | return false; |
| 209 | } |
| 210 | |
| 211 | /// Return those arguments that should be tracked when we report a bug about |
| 212 | /// argument constraint violation. By default it is the argument that is |
| 213 | /// constrained, however, in some special cases we need to track other |
| 214 | /// arguments as well. E.g. a buffer size might be encoded in another |
| 215 | /// argument. |
| 216 | /// The "return value" argument number can not occur as returned value. |
| 217 | virtual std::vector<ArgNo> getArgsToTrack() const { return {ArgN}; } |
| 218 | |
| 219 | /// Get a constraint that represents exactly the opposite of the current. |
| 220 | virtual ValueConstraintPtr negate() const { |
| 221 | llvm_unreachable("Not implemented" ); |
| 222 | }; |
| 223 | |
| 224 | /// Check whether the constraint is malformed or not. It is malformed if the |
| 225 | /// specified argument has a mismatch with the given FunctionDecl (e.g. the |
| 226 | /// arg number is out-of-range of the function's argument list). |
| 227 | /// This condition can indicate if a probably wrong or unexpected function |
| 228 | /// was found where the constraint is to be applied. |
| 229 | bool checkValidity(const FunctionDecl *FD) const { |
| 230 | const bool ValidArg = ArgN == Ret || ArgN < FD->getNumParams(); |
| 231 | assert(ValidArg && "Arg out of range!" ); |
| 232 | if (!ValidArg) |
| 233 | return false; |
| 234 | // Subclasses may further refine the validation. |
| 235 | return checkSpecificValidity(FD); |
| 236 | } |
| 237 | |
| 238 | /// Return the argument number (may be placeholder for "return value"). |
| 239 | ArgNo getArgNo() const { return ArgN; } |
| 240 | |
| 241 | protected: |
| 242 | /// Argument to which to apply the constraint. It can be a real argument of |
| 243 | /// the function to check, or a special value to indicate the return value |
| 244 | /// of the function. |
| 245 | /// Every constraint is assigned to one main argument, even if other |
| 246 | /// arguments are involved. |
| 247 | ArgNo ArgN; |
| 248 | |
| 249 | /// Do constraint-specific validation check. |
| 250 | virtual bool checkSpecificValidity(const FunctionDecl *FD) const { |
| 251 | return true; |
| 252 | } |
| 253 | }; |
| 254 | |
| 255 | /// Check if a single argument falls into a specific "range". |
| 256 | /// A range is formed as a set of intervals. |
| 257 | /// E.g. \code {['A', 'Z'], ['a', 'z'], ['_', '_']} \endcode |
| 258 | /// The intervals are closed intervals that contain one or more values. |
| 259 | /// |
| 260 | /// The default constructed RangeConstraint has an empty range, applying |
| 261 | /// such constraint does not involve any assumptions, thus the State remains |
| 262 | /// unchanged. This is meaningful, if the range is dependent on a looked up |
| 263 | /// type (e.g. [0, Socklen_tMax]). If the type is not found, then the range |
| 264 | /// is default initialized to be empty. |
| 265 | class RangeConstraint : public ValueConstraint { |
| 266 | /// The constraint can be specified by allowing or disallowing the range. |
| 267 | /// WithinRange indicates allowing the range, OutOfRange indicates |
| 268 | /// disallowing it (allowing the complementary range). |
| 269 | RangeKind Kind; |
| 270 | |
| 271 | /// A set of intervals. |
| 272 | IntRangeVector Ranges; |
| 273 | |
| 274 | /// A textual description of this constraint for the specific case where the |
| 275 | /// constraint is used. If empty a generated description will be used that |
| 276 | /// is built from the range of the constraint. |
| 277 | StringRef Description; |
| 278 | |
| 279 | public: |
| 280 | RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges, |
| 281 | StringRef Desc = "" ) |
| 282 | : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges), Description(Desc) { |
| 283 | } |
| 284 | |
| 285 | const IntRangeVector &getRanges() const { return Ranges; } |
| 286 | |
| 287 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 288 | const Summary &Summary, |
| 289 | CheckerContext &C) const override; |
| 290 | |
| 291 | void describe(DescriptionKind DK, const CallEvent &Call, |
| 292 | ProgramStateRef State, const Summary &Summary, |
| 293 | llvm::raw_ostream &Out) const override; |
| 294 | |
| 295 | bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, |
| 296 | const Summary &Summary, |
| 297 | llvm::raw_ostream &Out) const override; |
| 298 | |
| 299 | ValueConstraintPtr negate() const override { |
| 300 | RangeConstraint Tmp(*this); |
| 301 | Tmp.Kind = negateKind(K: Kind); |
| 302 | return std::make_shared<RangeConstraint>(args&: Tmp); |
| 303 | } |
| 304 | |
| 305 | protected: |
| 306 | bool checkSpecificValidity(const FunctionDecl *FD) const override { |
| 307 | const bool ValidArg = |
| 308 | getArgType(FD, ArgN)->isIntegralType(Ctx: FD->getASTContext()); |
| 309 | assert(ValidArg && |
| 310 | "This constraint should be applied on an integral type" ); |
| 311 | return ValidArg; |
| 312 | } |
| 313 | |
| 314 | private: |
| 315 | /// A callback function that is used when iterating over the range |
| 316 | /// intervals. It gets the begin and end (inclusive) of one interval. |
| 317 | /// This is used to make any kind of task possible that needs an iteration |
| 318 | /// over the intervals. |
| 319 | using RangeApplyFunction = |
| 320 | std::function<bool(const llvm::APSInt &Min, const llvm::APSInt &Max)>; |
| 321 | |
| 322 | /// Call a function on the intervals of the range. |
| 323 | /// The function is called with all intervals in the range. |
| 324 | void applyOnWithinRange(BasicValueFactory &BVF, QualType ArgT, |
| 325 | const RangeApplyFunction &F) const; |
| 326 | /// Call a function on all intervals in the complementary range. |
| 327 | /// The function is called with all intervals that fall out of the range. |
| 328 | /// E.g. consider an interval list [A, B] and [C, D] |
| 329 | /// \code |
| 330 | /// -------+--------+------------------+------------+-----------> |
| 331 | /// A B C D |
| 332 | /// \endcode |
| 333 | /// We get the ranges [-inf, A - 1], [D + 1, +inf], [B + 1, C - 1]. |
| 334 | /// The \p ArgT is used to determine the min and max of the type that is |
| 335 | /// used as "-inf" and "+inf". |
| 336 | void applyOnOutOfRange(BasicValueFactory &BVF, QualType ArgT, |
| 337 | const RangeApplyFunction &F) const; |
| 338 | /// Call a function on the intervals of the range or the complementary |
| 339 | /// range. |
| 340 | void applyOnRange(RangeKind Kind, BasicValueFactory &BVF, QualType ArgT, |
| 341 | const RangeApplyFunction &F) const { |
| 342 | switch (Kind) { |
| 343 | case OutOfRange: |
| 344 | applyOnOutOfRange(BVF, ArgT, F); |
| 345 | break; |
| 346 | case WithinRange: |
| 347 | applyOnWithinRange(BVF, ArgT, F); |
| 348 | break; |
| 349 | }; |
| 350 | } |
| 351 | }; |
| 352 | |
| 353 | /// Check relation of an argument to another. |
| 354 | class ComparisonConstraint : public ValueConstraint { |
| 355 | BinaryOperator::Opcode Opcode; |
| 356 | ArgNo OtherArgN; |
| 357 | |
| 358 | public: |
| 359 | ComparisonConstraint(ArgNo ArgN, BinaryOperator::Opcode Opcode, |
| 360 | ArgNo OtherArgN) |
| 361 | : ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {} |
| 362 | ArgNo getOtherArgNo() const { return OtherArgN; } |
| 363 | BinaryOperator::Opcode getOpcode() const { return Opcode; } |
| 364 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 365 | const Summary &Summary, |
| 366 | CheckerContext &C) const override; |
| 367 | }; |
| 368 | |
| 369 | /// Check null or non-null-ness of an argument that is of pointer type. |
| 370 | class NullnessConstraint : public ValueConstraint { |
| 371 | using ValueConstraint::ValueConstraint; |
| 372 | // This variable has a role when we negate the constraint. |
| 373 | bool CannotBeNull = true; |
| 374 | |
| 375 | public: |
| 376 | NullnessConstraint(ArgNo ArgN, bool CannotBeNull = true) |
| 377 | : ValueConstraint(ArgN), CannotBeNull(CannotBeNull) {} |
| 378 | |
| 379 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 380 | const Summary &Summary, |
| 381 | CheckerContext &C) const override; |
| 382 | |
| 383 | void describe(DescriptionKind DK, const CallEvent &Call, |
| 384 | ProgramStateRef State, const Summary &Summary, |
| 385 | llvm::raw_ostream &Out) const override; |
| 386 | |
| 387 | bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, |
| 388 | const Summary &Summary, |
| 389 | llvm::raw_ostream &Out) const override; |
| 390 | |
| 391 | ValueConstraintPtr negate() const override { |
| 392 | NullnessConstraint Tmp(*this); |
| 393 | Tmp.CannotBeNull = !this->CannotBeNull; |
| 394 | return std::make_shared<NullnessConstraint>(args&: Tmp); |
| 395 | } |
| 396 | |
| 397 | protected: |
| 398 | bool checkSpecificValidity(const FunctionDecl *FD) const override { |
| 399 | const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); |
| 400 | assert(ValidArg && |
| 401 | "This constraint should be applied only on a pointer type" ); |
| 402 | return ValidArg; |
| 403 | } |
| 404 | }; |
| 405 | |
| 406 | /// Check null or non-null-ness of an argument that is of pointer type. |
| 407 | /// The argument is meant to be a buffer that has a size constraint, and it |
| 408 | /// is allowed to have a NULL value if the size is 0. The size can depend on |
| 409 | /// 1 or 2 additional arguments, if one of these is 0 the buffer is allowed to |
| 410 | /// be NULL. Otherwise, the buffer pointer must be non-null. This is useful |
| 411 | /// for functions like `fread` which have this special property. |
| 412 | class BufferNullnessConstraint : public ValueConstraint { |
| 413 | using ValueConstraint::ValueConstraint; |
| 414 | ArgNo SizeArg1N; |
| 415 | std::optional<ArgNo> SizeArg2N; |
| 416 | // This variable has a role when we negate the constraint. |
| 417 | bool CannotBeNull = true; |
| 418 | |
| 419 | public: |
| 420 | BufferNullnessConstraint(ArgNo ArgN, ArgNo SizeArg1N, |
| 421 | std::optional<ArgNo> SizeArg2N, |
| 422 | bool CannotBeNull = true) |
| 423 | : ValueConstraint(ArgN), SizeArg1N(SizeArg1N), SizeArg2N(SizeArg2N), |
| 424 | CannotBeNull(CannotBeNull) {} |
| 425 | |
| 426 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 427 | const Summary &Summary, |
| 428 | CheckerContext &C) const override; |
| 429 | |
| 430 | void describe(DescriptionKind DK, const CallEvent &Call, |
| 431 | ProgramStateRef State, const Summary &Summary, |
| 432 | llvm::raw_ostream &Out) const override; |
| 433 | |
| 434 | bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, |
| 435 | const Summary &Summary, |
| 436 | llvm::raw_ostream &Out) const override; |
| 437 | |
| 438 | ValueConstraintPtr negate() const override { |
| 439 | BufferNullnessConstraint Tmp(*this); |
| 440 | Tmp.CannotBeNull = !this->CannotBeNull; |
| 441 | return std::make_shared<BufferNullnessConstraint>(args&: Tmp); |
| 442 | } |
| 443 | |
| 444 | protected: |
| 445 | bool checkSpecificValidity(const FunctionDecl *FD) const override { |
| 446 | const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); |
| 447 | assert(ValidArg && |
| 448 | "This constraint should be applied only on a pointer type" ); |
| 449 | return ValidArg; |
| 450 | } |
| 451 | }; |
| 452 | |
| 453 | // Represents a buffer argument with an additional size constraint. The |
| 454 | // constraint may be a concrete value, or a symbolic value in an argument. |
| 455 | // Example 1. Concrete value as the minimum buffer size. |
| 456 | // char *asctime_r(const struct tm *restrict tm, char *restrict buf); |
| 457 | // // `buf` size must be at least 26 bytes according the POSIX standard. |
| 458 | // Example 2. Argument as a buffer size. |
| 459 | // ctime_s(char *buffer, rsize_t bufsz, const time_t *time); |
| 460 | // Example 3. The size is computed as a multiplication of other args. |
| 461 | // size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); |
| 462 | // // Here, ptr is the buffer, and its minimum size is `size * nmemb`. |
| 463 | class BufferSizeConstraint : public ValueConstraint { |
| 464 | // The concrete value which is the minimum size for the buffer. |
| 465 | std::optional<llvm::APSInt> ConcreteSize; |
| 466 | // The argument which holds the size of the buffer. |
| 467 | std::optional<ArgNo> SizeArgN; |
| 468 | // The argument which is a multiplier to size. This is set in case of |
| 469 | // `fread` like functions where the size is computed as a multiplication of |
| 470 | // two arguments. |
| 471 | std::optional<ArgNo> SizeMultiplierArgN; |
| 472 | // The operator we use in apply. This is negated in negate(). |
| 473 | BinaryOperator::Opcode Op = BO_LE; |
| 474 | |
| 475 | public: |
| 476 | BufferSizeConstraint(ArgNo Buffer, llvm::APSInt BufMinSize) |
| 477 | : ValueConstraint(Buffer), ConcreteSize(BufMinSize) {} |
| 478 | BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize) |
| 479 | : ValueConstraint(Buffer), SizeArgN(BufSize) {} |
| 480 | BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize, ArgNo BufSizeMultiplier) |
| 481 | : ValueConstraint(Buffer), SizeArgN(BufSize), |
| 482 | SizeMultiplierArgN(BufSizeMultiplier) {} |
| 483 | |
| 484 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 485 | const Summary &Summary, |
| 486 | CheckerContext &C) const override; |
| 487 | |
| 488 | void describe(DescriptionKind DK, const CallEvent &Call, |
| 489 | ProgramStateRef State, const Summary &Summary, |
| 490 | llvm::raw_ostream &Out) const override; |
| 491 | |
| 492 | bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, |
| 493 | const Summary &Summary, |
| 494 | llvm::raw_ostream &Out) const override; |
| 495 | |
| 496 | std::vector<ArgNo> getArgsToTrack() const override { |
| 497 | std::vector<ArgNo> Result{ArgN}; |
| 498 | if (SizeArgN) |
| 499 | Result.push_back(x: *SizeArgN); |
| 500 | if (SizeMultiplierArgN) |
| 501 | Result.push_back(x: *SizeMultiplierArgN); |
| 502 | return Result; |
| 503 | } |
| 504 | |
| 505 | ValueConstraintPtr negate() const override { |
| 506 | BufferSizeConstraint Tmp(*this); |
| 507 | Tmp.Op = BinaryOperator::negateComparisonOp(Opc: Op); |
| 508 | return std::make_shared<BufferSizeConstraint>(args&: Tmp); |
| 509 | } |
| 510 | |
| 511 | protected: |
| 512 | bool checkSpecificValidity(const FunctionDecl *FD) const override { |
| 513 | const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); |
| 514 | assert(ValidArg && |
| 515 | "This constraint should be applied only on a pointer type" ); |
| 516 | return ValidArg; |
| 517 | } |
| 518 | }; |
| 519 | |
| 520 | /// The complete list of constraints that defines a single branch. |
| 521 | using ConstraintSet = std::vector<ValueConstraintPtr>; |
| 522 | |
| 523 | /// Define how a function affects the system variable 'errno'. |
| 524 | /// This works together with the \c ErrnoModeling and \c ErrnoChecker classes. |
| 525 | /// Currently 3 use cases exist: success, failure, irrelevant. |
| 526 | /// In the future the failure case can be customized to set \c errno to a |
| 527 | /// more specific constraint (for example > 0), or new case can be added |
| 528 | /// for functions which require check of \c errno in both success and failure |
| 529 | /// case. |
| 530 | class ErrnoConstraintBase { |
| 531 | public: |
| 532 | /// Apply specific state changes related to the errno variable. |
| 533 | virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 534 | const Summary &Summary, |
| 535 | CheckerContext &C) const = 0; |
| 536 | /// Get a description about what happens with 'errno' here and how it causes |
| 537 | /// a later bug report created by ErrnoChecker. |
| 538 | /// Empty return value means that 'errno' related bug may not happen from |
| 539 | /// the current analyzed function. |
| 540 | virtual std::string describe(CheckerContext &C) const { return "" ; } |
| 541 | |
| 542 | virtual ~ErrnoConstraintBase() {} |
| 543 | |
| 544 | protected: |
| 545 | ErrnoConstraintBase() = default; |
| 546 | |
| 547 | /// This is used for conjure symbol for errno to differentiate from the |
| 548 | /// original call expression (same expression is used for the errno symbol). |
| 549 | static int Tag; |
| 550 | }; |
| 551 | |
| 552 | /// Reset errno constraints to irrelevant. |
| 553 | /// This is applicable to functions that may change 'errno' and are not |
| 554 | /// modeled elsewhere. |
| 555 | class ResetErrnoConstraint : public ErrnoConstraintBase { |
| 556 | public: |
| 557 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 558 | const Summary &Summary, |
| 559 | CheckerContext &C) const override { |
| 560 | return errno_modeling::setErrnoState(State, EState: errno_modeling::Irrelevant); |
| 561 | } |
| 562 | }; |
| 563 | |
| 564 | /// Do not change errno constraints. |
| 565 | /// This is applicable to functions that are modeled in another checker |
| 566 | /// and the already set errno constraints should not be changed in the |
| 567 | /// post-call event. |
| 568 | class NoErrnoConstraint : public ErrnoConstraintBase { |
| 569 | public: |
| 570 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 571 | const Summary &Summary, |
| 572 | CheckerContext &C) const override { |
| 573 | return State; |
| 574 | } |
| 575 | }; |
| 576 | |
| 577 | /// Set errno constraint at failure cases of standard functions. |
| 578 | /// Failure case: 'errno' becomes not equal to 0 and may or may not be checked |
| 579 | /// by the program. \c ErrnoChecker does not emit a bug report after such a |
| 580 | /// function call. |
| 581 | class FailureErrnoConstraint : public ErrnoConstraintBase { |
| 582 | public: |
| 583 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 584 | const Summary &Summary, |
| 585 | CheckerContext &C) const override { |
| 586 | SValBuilder &SVB = C.getSValBuilder(); |
| 587 | NonLoc ErrnoSVal = SVB.conjureSymbolVal(call: Call, type: C.getASTContext().IntTy, |
| 588 | visitCount: C.blockCount(), symbolTag: &Tag) |
| 589 | .castAs<NonLoc>(); |
| 590 | return errno_modeling::setErrnoForStdFailure(State, C, ErrnoSym: ErrnoSVal); |
| 591 | } |
| 592 | }; |
| 593 | |
| 594 | /// Set errno constraint at success cases of standard functions. |
| 595 | /// Success case: 'errno' is not allowed to be used because the value is |
| 596 | /// undefined after successful call. |
| 597 | /// \c ErrnoChecker can emit bug report after such a function call if errno |
| 598 | /// is used. |
| 599 | class SuccessErrnoConstraint : public ErrnoConstraintBase { |
| 600 | public: |
| 601 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 602 | const Summary &Summary, |
| 603 | CheckerContext &C) const override { |
| 604 | return errno_modeling::setErrnoForStdSuccess(State, C); |
| 605 | } |
| 606 | |
| 607 | std::string describe(CheckerContext &C) const override { |
| 608 | return "'errno' becomes undefined after the call" ; |
| 609 | } |
| 610 | }; |
| 611 | |
| 612 | /// Set errno constraint at functions that indicate failure only with 'errno'. |
| 613 | /// In this case 'errno' is required to be observed. |
| 614 | /// \c ErrnoChecker can emit bug report after such a function call if errno |
| 615 | /// is overwritten without a read before. |
| 616 | class ErrnoMustBeCheckedConstraint : public ErrnoConstraintBase { |
| 617 | public: |
| 618 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, |
| 619 | const Summary &Summary, |
| 620 | CheckerContext &C) const override { |
| 621 | return errno_modeling::setErrnoStdMustBeChecked(State, C, |
| 622 | Elem: Call.getCFGElementRef()); |
| 623 | } |
| 624 | |
| 625 | std::string describe(CheckerContext &C) const override { |
| 626 | return "reading 'errno' is required to find out if the call has failed" ; |
| 627 | } |
| 628 | }; |
| 629 | |
| 630 | /// A single branch of a function summary. |
| 631 | /// |
| 632 | /// A branch is defined by a series of constraints - "assumptions" - |
| 633 | /// that together form a single possible outcome of invoking the function. |
| 634 | /// When static analyzer considers a branch, it tries to introduce |
| 635 | /// a child node in the Exploded Graph. The child node has to include |
| 636 | /// constraints that define the branch. If the constraints contradict |
| 637 | /// existing constraints in the state, the node is not created and the branch |
| 638 | /// is dropped; otherwise it's queued for future exploration. |
| 639 | /// The branch is accompanied by a note text that may be displayed |
| 640 | /// to the user when a bug is found on a path that takes this branch. |
| 641 | /// |
| 642 | /// For example, consider the branches in `isalpha(x)`: |
| 643 | /// Branch 1) |
| 644 | /// x is in range ['A', 'Z'] or in ['a', 'z'] |
| 645 | /// then the return value is not 0. (I.e. out-of-range [0, 0]) |
| 646 | /// and the note may say "Assuming the character is alphabetical" |
| 647 | /// Branch 2) |
| 648 | /// x is out-of-range ['A', 'Z'] and out-of-range ['a', 'z'] |
| 649 | /// then the return value is 0 |
| 650 | /// and the note may say "Assuming the character is non-alphabetical". |
| 651 | class SummaryCase { |
| 652 | ConstraintSet Constraints; |
| 653 | const ErrnoConstraintBase &ErrnoConstraint; |
| 654 | StringRef Note; |
| 655 | |
| 656 | public: |
| 657 | SummaryCase(ConstraintSet &&Constraints, const ErrnoConstraintBase &ErrnoC, |
| 658 | StringRef Note) |
| 659 | : Constraints(std::move(Constraints)), ErrnoConstraint(ErrnoC), |
| 660 | Note(Note) {} |
| 661 | |
| 662 | SummaryCase(const ConstraintSet &Constraints, |
| 663 | const ErrnoConstraintBase &ErrnoC, StringRef Note) |
| 664 | : Constraints(Constraints), ErrnoConstraint(ErrnoC), Note(Note) {} |
| 665 | |
| 666 | const ConstraintSet &getConstraints() const { return Constraints; } |
| 667 | const ErrnoConstraintBase &getErrnoConstraint() const { |
| 668 | return ErrnoConstraint; |
| 669 | } |
| 670 | StringRef getNote() const { return Note; } |
| 671 | }; |
| 672 | |
| 673 | using ArgTypes = ArrayRef<std::optional<QualType>>; |
| 674 | using RetType = std::optional<QualType>; |
| 675 | |
| 676 | // A placeholder type, we use it whenever we do not care about the concrete |
| 677 | // type in a Signature. |
| 678 | const QualType Irrelevant{}; |
| 679 | bool static isIrrelevant(QualType T) { return T.isNull(); } |
| 680 | |
| 681 | // The signature of a function we want to describe with a summary. This is a |
| 682 | // concessive signature, meaning there may be irrelevant types in the |
| 683 | // signature which we do not check against a function with concrete types. |
| 684 | // All types in the spec need to be canonical. |
| 685 | class Signature { |
| 686 | using ArgQualTypes = std::vector<QualType>; |
| 687 | ArgQualTypes ArgTys; |
| 688 | QualType RetTy; |
| 689 | // True if any component type is not found by lookup. |
| 690 | bool Invalid = false; |
| 691 | |
| 692 | public: |
| 693 | // Construct a signature from optional types. If any of the optional types |
| 694 | // are not set then the signature will be invalid. |
| 695 | Signature(ArgTypes ArgTys, RetType RetTy) { |
| 696 | for (std::optional<QualType> Arg : ArgTys) { |
| 697 | if (!Arg) { |
| 698 | Invalid = true; |
| 699 | return; |
| 700 | } else { |
| 701 | assertArgTypeSuitableForSignature(T: *Arg); |
| 702 | this->ArgTys.push_back(x: *Arg); |
| 703 | } |
| 704 | } |
| 705 | if (!RetTy) { |
| 706 | Invalid = true; |
| 707 | return; |
| 708 | } else { |
| 709 | assertRetTypeSuitableForSignature(T: *RetTy); |
| 710 | this->RetTy = *RetTy; |
| 711 | } |
| 712 | } |
| 713 | |
| 714 | bool isInvalid() const { return Invalid; } |
| 715 | bool matches(const FunctionDecl *FD) const; |
| 716 | |
| 717 | private: |
| 718 | static void assertArgTypeSuitableForSignature(QualType T) { |
| 719 | assert((T.isNull() || !T->isVoidType()) && |
| 720 | "We should have no void types in the spec" ); |
| 721 | assert((T.isNull() || T.isCanonical()) && |
| 722 | "We should only have canonical types in the spec" ); |
| 723 | } |
| 724 | static void assertRetTypeSuitableForSignature(QualType T) { |
| 725 | assert((T.isNull() || T.isCanonical()) && |
| 726 | "We should only have canonical types in the spec" ); |
| 727 | } |
| 728 | }; |
| 729 | |
| 730 | static QualType getArgType(const FunctionDecl *FD, ArgNo ArgN) { |
| 731 | assert(FD && "Function must be set" ); |
| 732 | QualType T = (ArgN == Ret) |
| 733 | ? FD->getReturnType().getCanonicalType() |
| 734 | : FD->getParamDecl(i: ArgN)->getType().getCanonicalType(); |
| 735 | return T; |
| 736 | } |
| 737 | |
| 738 | using SummaryCases = std::vector<SummaryCase>; |
| 739 | |
| 740 | /// A summary includes information about |
| 741 | /// * function prototype (signature) |
| 742 | /// * approach to invalidation, |
| 743 | /// * a list of branches - so, a list of list of ranges, |
| 744 | /// * a list of argument constraints, that must be true on every branch. |
| 745 | /// If these constraints are not satisfied that means a fatal error |
| 746 | /// usually resulting in undefined behaviour. |
| 747 | /// |
| 748 | /// Application of a summary: |
| 749 | /// The signature and argument constraints together contain information |
| 750 | /// about which functions are handled by the summary. The signature can use |
| 751 | /// "wildcards", i.e. Irrelevant types. Irrelevant type of a parameter in |
| 752 | /// a signature means that type is not compared to the type of the parameter |
| 753 | /// in the found FunctionDecl. Argument constraints may specify additional |
| 754 | /// rules for the given parameter's type, those rules are checked once the |
| 755 | /// signature is matched. |
| 756 | class Summary { |
| 757 | const InvalidationKind InvalidationKd; |
| 758 | SummaryCases Cases; |
| 759 | ConstraintSet ArgConstraints; |
| 760 | |
| 761 | // The function to which the summary applies. This is set after lookup and |
| 762 | // match to the signature. |
| 763 | const FunctionDecl *FD = nullptr; |
| 764 | |
| 765 | public: |
| 766 | Summary(InvalidationKind InvalidationKd) : InvalidationKd(InvalidationKd) {} |
| 767 | |
| 768 | Summary &Case(ConstraintSet &&CS, const ErrnoConstraintBase &ErrnoC, |
| 769 | StringRef Note = "" ) { |
| 770 | Cases.push_back(x: SummaryCase(std::move(CS), ErrnoC, Note)); |
| 771 | return *this; |
| 772 | } |
| 773 | Summary &Case(const ConstraintSet &CS, const ErrnoConstraintBase &ErrnoC, |
| 774 | StringRef Note = "" ) { |
| 775 | Cases.push_back(x: SummaryCase(CS, ErrnoC, Note)); |
| 776 | return *this; |
| 777 | } |
| 778 | Summary &ArgConstraint(ValueConstraintPtr VC) { |
| 779 | assert(VC->getArgNo() != Ret && |
| 780 | "Arg constraint should not refer to the return value" ); |
| 781 | ArgConstraints.push_back(x: VC); |
| 782 | return *this; |
| 783 | } |
| 784 | |
| 785 | InvalidationKind getInvalidationKd() const { return InvalidationKd; } |
| 786 | const SummaryCases &getCases() const { return Cases; } |
| 787 | const ConstraintSet &getArgConstraints() const { return ArgConstraints; } |
| 788 | |
| 789 | QualType getArgType(ArgNo ArgN) const { |
| 790 | return StdLibraryFunctionsChecker::getArgType(FD, ArgN); |
| 791 | } |
| 792 | |
| 793 | // Returns true if the summary should be applied to the given function. |
| 794 | // And if yes then store the function declaration. |
| 795 | bool matchesAndSet(const Signature &Sign, const FunctionDecl *FD) { |
| 796 | bool Result = Sign.matches(FD) && validateByConstraints(FD); |
| 797 | if (Result) { |
| 798 | assert(!this->FD && "FD must not be set more than once" ); |
| 799 | this->FD = FD; |
| 800 | } |
| 801 | return Result; |
| 802 | } |
| 803 | |
| 804 | private: |
| 805 | // Once we know the exact type of the function then do validation check on |
| 806 | // all the given constraints. |
| 807 | bool validateByConstraints(const FunctionDecl *FD) const { |
| 808 | for (const SummaryCase &Case : Cases) |
| 809 | for (const ValueConstraintPtr &Constraint : Case.getConstraints()) |
| 810 | if (!Constraint->checkValidity(FD)) |
| 811 | return false; |
| 812 | for (const ValueConstraintPtr &Constraint : ArgConstraints) |
| 813 | if (!Constraint->checkValidity(FD)) |
| 814 | return false; |
| 815 | return true; |
| 816 | } |
| 817 | }; |
| 818 | |
| 819 | // The map of all functions supported by the checker. It is initialized |
| 820 | // lazily, and it doesn't change after initialization. |
| 821 | using FunctionSummaryMapType = llvm::DenseMap<const FunctionDecl *, Summary>; |
| 822 | mutable FunctionSummaryMapType FunctionSummaryMap; |
| 823 | |
| 824 | const BugType BT_InvalidArg{this, "Function call with invalid argument" }; |
| 825 | mutable bool SummariesInitialized = false; |
| 826 | |
| 827 | static SVal getArgSVal(const CallEvent &Call, ArgNo ArgN) { |
| 828 | return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(Index: ArgN); |
| 829 | } |
| 830 | static std::string getFunctionName(const CallEvent &Call) { |
| 831 | assert(Call.getDecl() && |
| 832 | "Call was found by a summary, should have declaration" ); |
| 833 | return cast<NamedDecl>(Val: Call.getDecl())->getNameAsString(); |
| 834 | } |
| 835 | |
| 836 | public: |
| 837 | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; |
| 838 | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; |
| 839 | bool evalCall(const CallEvent &Call, CheckerContext &C) const; |
| 840 | |
| 841 | CheckerNameRef CheckName; |
| 842 | bool AddTestFunctions = false; |
| 843 | |
| 844 | bool DisplayLoadedSummaries = false; |
| 845 | bool ModelPOSIX = false; |
| 846 | bool ShouldAssumeControlledEnvironment = false; |
| 847 | |
| 848 | private: |
| 849 | std::optional<Summary> findFunctionSummary(const FunctionDecl *FD, |
| 850 | CheckerContext &C) const; |
| 851 | std::optional<Summary> findFunctionSummary(const CallEvent &Call, |
| 852 | CheckerContext &C) const; |
| 853 | |
| 854 | void initFunctionSummaries(CheckerContext &C) const; |
| 855 | |
| 856 | void reportBug(const CallEvent &Call, ExplodedNode *N, |
| 857 | const ValueConstraint *VC, const ValueConstraint *NegatedVC, |
| 858 | const Summary &Summary, CheckerContext &C) const { |
| 859 | assert(Call.getDecl() && |
| 860 | "Function found in summary must have a declaration available" ); |
| 861 | SmallString<256> Msg; |
| 862 | llvm::raw_svector_ostream MsgOs(Msg); |
| 863 | |
| 864 | MsgOs << "The " ; |
| 865 | printArgDesc(VC->getArgNo(), Out&: MsgOs); |
| 866 | MsgOs << " to '" << getFunctionName(Call) << "' " ; |
| 867 | bool ValuesPrinted = |
| 868 | NegatedVC->describeArgumentValue(Call, State: N->getState(), Summary, Out&: MsgOs); |
| 869 | if (ValuesPrinted) |
| 870 | MsgOs << " but " ; |
| 871 | else |
| 872 | MsgOs << "is out of the accepted range; It " ; |
| 873 | VC->describe(DK: ValueConstraint::Violation, Call, State: C.getState(), Summary, |
| 874 | Out&: MsgOs); |
| 875 | Msg[0] = toupper(c: Msg[0]); |
| 876 | auto R = std::make_unique<PathSensitiveBugReport>(args: BT_InvalidArg, args&: Msg, args&: N); |
| 877 | |
| 878 | for (ArgNo ArgN : VC->getArgsToTrack()) { |
| 879 | bugreporter::trackExpressionValue(N, E: Call.getArgExpr(Index: ArgN), R&: *R); |
| 880 | R->markInteresting(V: Call.getArgSVal(Index: ArgN)); |
| 881 | // All tracked arguments are important, highlight them. |
| 882 | R->addRange(R: Call.getArgSourceRange(Index: ArgN)); |
| 883 | } |
| 884 | |
| 885 | C.emitReport(R: std::move(R)); |
| 886 | } |
| 887 | |
| 888 | /// These are the errno constraints that can be passed to summary cases. |
| 889 | /// One of these should fit for a single summary case. |
| 890 | /// Usually if a failure return value exists for function, that function |
| 891 | /// needs different cases for success and failure with different errno |
| 892 | /// constraints (and different return value constraints). |
| 893 | const NoErrnoConstraint ErrnoUnchanged{}; |
| 894 | const ResetErrnoConstraint ErrnoIrrelevant{}; |
| 895 | const ErrnoMustBeCheckedConstraint ErrnoMustBeChecked{}; |
| 896 | const SuccessErrnoConstraint ErrnoMustNotBeChecked{}; |
| 897 | const FailureErrnoConstraint ErrnoNEZeroIrrelevant{}; |
| 898 | }; |
| 899 | |
| 900 | int StdLibraryFunctionsChecker::ErrnoConstraintBase::Tag = 0; |
| 901 | |
| 902 | const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret = |
| 903 | std::numeric_limits<ArgNo>::max(); |
| 904 | |
| 905 | static BasicValueFactory &getBVF(ProgramStateRef State) { |
| 906 | ProgramStateManager &Mgr = State->getStateManager(); |
| 907 | SValBuilder &SVB = Mgr.getSValBuilder(); |
| 908 | return SVB.getBasicValueFactory(); |
| 909 | } |
| 910 | |
| 911 | } // end of anonymous namespace |
| 912 | |
| 913 | void StdLibraryFunctionsChecker::printArgDesc( |
| 914 | StdLibraryFunctionsChecker::ArgNo ArgN, llvm::raw_ostream &Out) { |
| 915 | Out << std::to_string(val: ArgN + 1); |
| 916 | Out << llvm::getOrdinalSuffix(Val: ArgN + 1); |
| 917 | Out << " argument" ; |
| 918 | } |
| 919 | |
| 920 | void StdLibraryFunctionsChecker::printArgValueInfo(ArgNo ArgN, |
| 921 | ProgramStateRef State, |
| 922 | const CallEvent &Call, |
| 923 | llvm::raw_ostream &Out) { |
| 924 | if (const llvm::APSInt *Val = |
| 925 | State->getStateManager().getSValBuilder().getKnownValue( |
| 926 | state: State, val: getArgSVal(Call, ArgN))) |
| 927 | Out << " (which is " << *Val << ")" ; |
| 928 | } |
| 929 | |
| 930 | void StdLibraryFunctionsChecker::appendInsideRangeDesc(llvm::APSInt RMin, |
| 931 | llvm::APSInt RMax, |
| 932 | QualType ArgT, |
| 933 | BasicValueFactory &BVF, |
| 934 | llvm::raw_ostream &Out) { |
| 935 | if (RMin.isZero() && RMax.isZero()) |
| 936 | Out << "zero" ; |
| 937 | else if (RMin == RMax) |
| 938 | Out << RMin; |
| 939 | else if (RMin == BVF.getMinValue(T: ArgT)) { |
| 940 | if (RMax == -1) |
| 941 | Out << "< 0" ; |
| 942 | else |
| 943 | Out << "<= " << RMax; |
| 944 | } else if (RMax == BVF.getMaxValue(T: ArgT)) { |
| 945 | if (RMin.isOne()) |
| 946 | Out << "> 0" ; |
| 947 | else |
| 948 | Out << ">= " << RMin; |
| 949 | } else if (RMin.isNegative() == RMax.isNegative() && |
| 950 | RMin.getLimitedValue() == RMax.getLimitedValue() - 1) { |
| 951 | Out << RMin << " or " << RMax; |
| 952 | } else { |
| 953 | Out << "between " << RMin << " and " << RMax; |
| 954 | } |
| 955 | } |
| 956 | |
| 957 | void StdLibraryFunctionsChecker::appendOutOfRangeDesc(llvm::APSInt RMin, |
| 958 | llvm::APSInt RMax, |
| 959 | QualType ArgT, |
| 960 | BasicValueFactory &BVF, |
| 961 | llvm::raw_ostream &Out) { |
| 962 | if (RMin.isZero() && RMax.isZero()) |
| 963 | Out << "nonzero" ; |
| 964 | else if (RMin == RMax) { |
| 965 | Out << "not equal to " << RMin; |
| 966 | } else if (RMin == BVF.getMinValue(T: ArgT)) { |
| 967 | if (RMax == -1) |
| 968 | Out << ">= 0" ; |
| 969 | else |
| 970 | Out << "> " << RMax; |
| 971 | } else if (RMax == BVF.getMaxValue(T: ArgT)) { |
| 972 | if (RMin.isOne()) |
| 973 | Out << "<= 0" ; |
| 974 | else |
| 975 | Out << "< " << RMin; |
| 976 | } else if (RMin.isNegative() == RMax.isNegative() && |
| 977 | RMin.getLimitedValue() == RMax.getLimitedValue() - 1) { |
| 978 | Out << "not " << RMin << " and not " << RMax; |
| 979 | } else { |
| 980 | Out << "not between " << RMin << " and " << RMax; |
| 981 | } |
| 982 | } |
| 983 | |
| 984 | void StdLibraryFunctionsChecker::RangeConstraint::applyOnWithinRange( |
| 985 | BasicValueFactory &BVF, QualType ArgT, const RangeApplyFunction &F) const { |
| 986 | if (Ranges.empty()) |
| 987 | return; |
| 988 | |
| 989 | for (auto [Start, End] : getRanges()) { |
| 990 | const llvm::APSInt &Min = BVF.getValue(X: Start, T: ArgT); |
| 991 | const llvm::APSInt &Max = BVF.getValue(X: End, T: ArgT); |
| 992 | assert(Min <= Max); |
| 993 | if (!F(Min, Max)) |
| 994 | return; |
| 995 | } |
| 996 | } |
| 997 | |
| 998 | void StdLibraryFunctionsChecker::RangeConstraint::applyOnOutOfRange( |
| 999 | BasicValueFactory &BVF, QualType ArgT, const RangeApplyFunction &F) const { |
| 1000 | if (Ranges.empty()) |
| 1001 | return; |
| 1002 | |
| 1003 | const IntRangeVector &R = getRanges(); |
| 1004 | size_t E = R.size(); |
| 1005 | |
| 1006 | const llvm::APSInt &MinusInf = BVF.getMinValue(T: ArgT); |
| 1007 | const llvm::APSInt &PlusInf = BVF.getMaxValue(T: ArgT); |
| 1008 | |
| 1009 | const llvm::APSInt &RangeLeft = BVF.getValue(X: R[0].first - 1ULL, T: ArgT); |
| 1010 | const llvm::APSInt &RangeRight = BVF.getValue(X: R[E - 1].second + 1ULL, T: ArgT); |
| 1011 | |
| 1012 | // Iterate over the "holes" between intervals. |
| 1013 | for (size_t I = 1; I != E; ++I) { |
| 1014 | const llvm::APSInt &Min = BVF.getValue(X: R[I - 1].second + 1ULL, T: ArgT); |
| 1015 | const llvm::APSInt &Max = BVF.getValue(X: R[I].first - 1ULL, T: ArgT); |
| 1016 | if (Min <= Max) { |
| 1017 | if (!F(Min, Max)) |
| 1018 | return; |
| 1019 | } |
| 1020 | } |
| 1021 | // Check the interval [T_MIN, min(R) - 1]. |
| 1022 | if (RangeLeft != PlusInf) { |
| 1023 | assert(MinusInf <= RangeLeft); |
| 1024 | if (!F(MinusInf, RangeLeft)) |
| 1025 | return; |
| 1026 | } |
| 1027 | // Check the interval [max(R) + 1, T_MAX], |
| 1028 | if (RangeRight != MinusInf) { |
| 1029 | assert(RangeRight <= PlusInf); |
| 1030 | if (!F(RangeRight, PlusInf)) |
| 1031 | return; |
| 1032 | } |
| 1033 | } |
| 1034 | |
| 1035 | ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::apply( |
| 1036 | ProgramStateRef State, const CallEvent &Call, const Summary &Summary, |
| 1037 | CheckerContext &C) const { |
| 1038 | ConstraintManager &CM = C.getConstraintManager(); |
| 1039 | SVal V = getArgSVal(Call, ArgN: getArgNo()); |
| 1040 | QualType T = Summary.getArgType(ArgN: getArgNo()); |
| 1041 | |
| 1042 | if (auto N = V.getAs<NonLoc>()) { |
| 1043 | auto ExcludeRangeFromArg = [&](const llvm::APSInt &Min, |
| 1044 | const llvm::APSInt &Max) { |
| 1045 | State = CM.assumeInclusiveRange(State, Value: *N, From: Min, To: Max, InBound: false); |
| 1046 | return static_cast<bool>(State); |
| 1047 | }; |
| 1048 | // "OutOfRange R" is handled by excluding all ranges in R. |
| 1049 | // "WithinRange R" is treated as "OutOfRange [T_MIN, T_MAX] \ R". |
| 1050 | applyOnRange(Kind: negateKind(K: Kind), BVF&: C.getSValBuilder().getBasicValueFactory(), ArgT: T, |
| 1051 | F: ExcludeRangeFromArg); |
| 1052 | } |
| 1053 | |
| 1054 | return State; |
| 1055 | } |
| 1056 | |
| 1057 | void StdLibraryFunctionsChecker::RangeConstraint::describe( |
| 1058 | DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, |
| 1059 | const Summary &Summary, llvm::raw_ostream &Out) const { |
| 1060 | |
| 1061 | BasicValueFactory &BVF = getBVF(State); |
| 1062 | QualType T = Summary.getArgType(ArgN: getArgNo()); |
| 1063 | |
| 1064 | Out << ((DK == Violation) ? "should be " : "is " ); |
| 1065 | if (!Description.empty()) { |
| 1066 | Out << Description; |
| 1067 | } else { |
| 1068 | unsigned I = Ranges.size(); |
| 1069 | if (Kind == WithinRange) { |
| 1070 | for (const std::pair<RangeInt, RangeInt> &R : Ranges) { |
| 1071 | appendInsideRangeDesc(RMin: BVF.getValue(X: R.first, T), |
| 1072 | RMax: BVF.getValue(X: R.second, T), ArgT: T, BVF, Out); |
| 1073 | if (--I > 0) |
| 1074 | Out << " or " ; |
| 1075 | } |
| 1076 | } else { |
| 1077 | for (const std::pair<RangeInt, RangeInt> &R : Ranges) { |
| 1078 | appendOutOfRangeDesc(RMin: BVF.getValue(X: R.first, T), |
| 1079 | RMax: BVF.getValue(X: R.second, T), ArgT: T, BVF, Out); |
| 1080 | if (--I > 0) |
| 1081 | Out << " and " ; |
| 1082 | } |
| 1083 | } |
| 1084 | } |
| 1085 | } |
| 1086 | |
| 1087 | bool StdLibraryFunctionsChecker::RangeConstraint::describeArgumentValue( |
| 1088 | const CallEvent &Call, ProgramStateRef State, const Summary &Summary, |
| 1089 | llvm::raw_ostream &Out) const { |
| 1090 | unsigned int NRanges = 0; |
| 1091 | bool HaveAllRanges = true; |
| 1092 | |
| 1093 | ProgramStateManager &Mgr = State->getStateManager(); |
| 1094 | BasicValueFactory &BVF = Mgr.getSValBuilder().getBasicValueFactory(); |
| 1095 | ConstraintManager &CM = Mgr.getConstraintManager(); |
| 1096 | SVal V = getArgSVal(Call, ArgN: getArgNo()); |
| 1097 | |
| 1098 | if (auto N = V.getAs<NonLoc>()) { |
| 1099 | if (const llvm::APSInt *Int = N->getAsInteger()) { |
| 1100 | Out << "is " ; |
| 1101 | Out << *Int; |
| 1102 | return true; |
| 1103 | } |
| 1104 | QualType T = Summary.getArgType(ArgN: getArgNo()); |
| 1105 | SmallString<128> MoreInfo; |
| 1106 | llvm::raw_svector_ostream MoreInfoOs(MoreInfo); |
| 1107 | auto ApplyF = [&](const llvm::APSInt &Min, const llvm::APSInt &Max) { |
| 1108 | if (CM.assumeInclusiveRange(State, Value: *N, From: Min, To: Max, InBound: true)) { |
| 1109 | if (NRanges > 0) |
| 1110 | MoreInfoOs << " or " ; |
| 1111 | appendInsideRangeDesc(RMin: Min, RMax: Max, ArgT: T, BVF, Out&: MoreInfoOs); |
| 1112 | ++NRanges; |
| 1113 | } else { |
| 1114 | HaveAllRanges = false; |
| 1115 | } |
| 1116 | return true; |
| 1117 | }; |
| 1118 | |
| 1119 | applyOnRange(Kind, BVF, ArgT: T, F: ApplyF); |
| 1120 | assert(NRanges > 0); |
| 1121 | if (!HaveAllRanges || NRanges == 1) { |
| 1122 | Out << "is " ; |
| 1123 | Out << MoreInfo; |
| 1124 | return true; |
| 1125 | } |
| 1126 | } |
| 1127 | return false; |
| 1128 | } |
| 1129 | |
| 1130 | ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( |
| 1131 | ProgramStateRef State, const CallEvent &Call, const Summary &Summary, |
| 1132 | CheckerContext &C) const { |
| 1133 | |
| 1134 | ProgramStateManager &Mgr = State->getStateManager(); |
| 1135 | SValBuilder &SVB = Mgr.getSValBuilder(); |
| 1136 | QualType CondT = SVB.getConditionType(); |
| 1137 | QualType T = Summary.getArgType(ArgN: getArgNo()); |
| 1138 | SVal V = getArgSVal(Call, ArgN: getArgNo()); |
| 1139 | |
| 1140 | BinaryOperator::Opcode Op = getOpcode(); |
| 1141 | ArgNo OtherArg = getOtherArgNo(); |
| 1142 | SVal OtherV = getArgSVal(Call, ArgN: OtherArg); |
| 1143 | QualType OtherT = Summary.getArgType(ArgN: OtherArg); |
| 1144 | // Note: we avoid integral promotion for comparison. |
| 1145 | OtherV = SVB.evalCast(V: OtherV, CastTy: T, OriginalTy: OtherT); |
| 1146 | if (auto CompV = SVB.evalBinOp(state: State, op: Op, lhs: V, rhs: OtherV, type: CondT) |
| 1147 | .getAs<DefinedOrUnknownSVal>()) |
| 1148 | State = State->assume(Cond: *CompV, Assumption: true); |
| 1149 | return State; |
| 1150 | } |
| 1151 | |
| 1152 | ProgramStateRef StdLibraryFunctionsChecker::NullnessConstraint::apply( |
| 1153 | ProgramStateRef State, const CallEvent &Call, const Summary &Summary, |
| 1154 | CheckerContext &C) const { |
| 1155 | SVal V = getArgSVal(Call, ArgN: getArgNo()); |
| 1156 | if (V.isUndef()) |
| 1157 | return State; |
| 1158 | |
| 1159 | DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>(); |
| 1160 | if (!isa<Loc>(Val: L)) |
| 1161 | return State; |
| 1162 | |
| 1163 | return State->assume(Cond: L, Assumption: CannotBeNull); |
| 1164 | } |
| 1165 | |
| 1166 | void StdLibraryFunctionsChecker::NullnessConstraint::describe( |
| 1167 | DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, |
| 1168 | const Summary &Summary, llvm::raw_ostream &Out) const { |
| 1169 | assert(CannotBeNull && |
| 1170 | "'describe' is not implemented when the value must be NULL" ); |
| 1171 | if (DK == Violation) |
| 1172 | Out << "should not be NULL" ; |
| 1173 | else |
| 1174 | Out << "is not NULL" ; |
| 1175 | } |
| 1176 | |
| 1177 | bool StdLibraryFunctionsChecker::NullnessConstraint::describeArgumentValue( |
| 1178 | const CallEvent &Call, ProgramStateRef State, const Summary &Summary, |
| 1179 | llvm::raw_ostream &Out) const { |
| 1180 | assert(!CannotBeNull && "'describeArgumentValue' is not implemented when the " |
| 1181 | "value must be non-NULL" ); |
| 1182 | Out << "is NULL" ; |
| 1183 | return true; |
| 1184 | } |
| 1185 | |
| 1186 | ProgramStateRef StdLibraryFunctionsChecker::BufferNullnessConstraint::apply( |
| 1187 | ProgramStateRef State, const CallEvent &Call, const Summary &Summary, |
| 1188 | CheckerContext &C) const { |
| 1189 | SVal V = getArgSVal(Call, ArgN: getArgNo()); |
| 1190 | if (V.isUndef()) |
| 1191 | return State; |
| 1192 | DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>(); |
| 1193 | if (!isa<Loc>(Val: L)) |
| 1194 | return State; |
| 1195 | |
| 1196 | std::optional<DefinedOrUnknownSVal> SizeArg1 = |
| 1197 | getArgSVal(Call, ArgN: SizeArg1N).getAs<DefinedOrUnknownSVal>(); |
| 1198 | std::optional<DefinedOrUnknownSVal> SizeArg2; |
| 1199 | if (SizeArg2N) |
| 1200 | SizeArg2 = getArgSVal(Call, ArgN: *SizeArg2N).getAs<DefinedOrUnknownSVal>(); |
| 1201 | |
| 1202 | auto IsArgZero = [State](std::optional<DefinedOrUnknownSVal> Val) { |
| 1203 | if (!Val) |
| 1204 | return false; |
| 1205 | auto [IsNonNull, IsNull] = State->assume(Cond: *Val); |
| 1206 | return IsNull && !IsNonNull; |
| 1207 | }; |
| 1208 | |
| 1209 | if (IsArgZero(SizeArg1) || IsArgZero(SizeArg2)) |
| 1210 | return State; |
| 1211 | |
| 1212 | return State->assume(Cond: L, Assumption: CannotBeNull); |
| 1213 | } |
| 1214 | |
| 1215 | void StdLibraryFunctionsChecker::BufferNullnessConstraint::describe( |
| 1216 | DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, |
| 1217 | const Summary &Summary, llvm::raw_ostream &Out) const { |
| 1218 | assert(CannotBeNull && |
| 1219 | "'describe' is not implemented when the buffer must be NULL" ); |
| 1220 | if (DK == Violation) |
| 1221 | Out << "should not be NULL" ; |
| 1222 | else |
| 1223 | Out << "is not NULL" ; |
| 1224 | } |
| 1225 | |
| 1226 | bool StdLibraryFunctionsChecker::BufferNullnessConstraint:: |
| 1227 | describeArgumentValue(const CallEvent &Call, ProgramStateRef State, |
| 1228 | const Summary &Summary, |
| 1229 | llvm::raw_ostream &Out) const { |
| 1230 | assert(!CannotBeNull && "'describeArgumentValue' is not implemented when the " |
| 1231 | "buffer must be non-NULL" ); |
| 1232 | Out << "is NULL" ; |
| 1233 | return true; |
| 1234 | } |
| 1235 | |
| 1236 | ProgramStateRef StdLibraryFunctionsChecker::BufferSizeConstraint::apply( |
| 1237 | ProgramStateRef State, const CallEvent &Call, const Summary &Summary, |
| 1238 | CheckerContext &C) const { |
| 1239 | SValBuilder &SvalBuilder = C.getSValBuilder(); |
| 1240 | // The buffer argument. |
| 1241 | SVal BufV = getArgSVal(Call, ArgN: getArgNo()); |
| 1242 | |
| 1243 | // Get the size constraint. |
| 1244 | const SVal SizeV = [this, &State, &Call, &Summary, &SvalBuilder]() { |
| 1245 | if (ConcreteSize) { |
| 1246 | return SVal(SvalBuilder.makeIntVal(integer: *ConcreteSize)); |
| 1247 | } |
| 1248 | assert(SizeArgN && "The constraint must be either a concrete value or " |
| 1249 | "encoded in an argument." ); |
| 1250 | // The size argument. |
| 1251 | SVal SizeV = getArgSVal(Call, ArgN: *SizeArgN); |
| 1252 | // Multiply with another argument if given. |
| 1253 | if (SizeMultiplierArgN) { |
| 1254 | SVal SizeMulV = getArgSVal(Call, ArgN: *SizeMultiplierArgN); |
| 1255 | SizeV = SvalBuilder.evalBinOp(state: State, op: BO_Mul, lhs: SizeV, rhs: SizeMulV, |
| 1256 | type: Summary.getArgType(ArgN: *SizeArgN)); |
| 1257 | } |
| 1258 | return SizeV; |
| 1259 | }(); |
| 1260 | |
| 1261 | // The dynamic size of the buffer argument, got from the analyzer engine. |
| 1262 | SVal BufDynSize = getDynamicExtentWithOffset(State, BufV); |
| 1263 | |
| 1264 | SVal Feasible = SvalBuilder.evalBinOp(state: State, op: Op, lhs: SizeV, rhs: BufDynSize, |
| 1265 | type: SvalBuilder.getContext().BoolTy); |
| 1266 | if (auto F = Feasible.getAs<DefinedOrUnknownSVal>()) |
| 1267 | return State->assume(Cond: *F, Assumption: true); |
| 1268 | |
| 1269 | // We can get here only if the size argument or the dynamic size is |
| 1270 | // undefined. But the dynamic size should never be undefined, only |
| 1271 | // unknown. So, here, the size of the argument is undefined, i.e. we |
| 1272 | // cannot apply the constraint. Actually, other checkers like |
| 1273 | // CallAndMessage should catch this situation earlier, because we call a |
| 1274 | // function with an uninitialized argument. |
| 1275 | llvm_unreachable("Size argument or the dynamic size is Undefined" ); |
| 1276 | } |
| 1277 | |
| 1278 | void StdLibraryFunctionsChecker::BufferSizeConstraint::describe( |
| 1279 | DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, |
| 1280 | const Summary &Summary, llvm::raw_ostream &Out) const { |
| 1281 | Out << ((DK == Violation) ? "should be " : "is " ); |
| 1282 | Out << "a buffer with size equal to or greater than " ; |
| 1283 | if (ConcreteSize) { |
| 1284 | Out << *ConcreteSize; |
| 1285 | } else if (SizeArgN) { |
| 1286 | Out << "the value of the " ; |
| 1287 | printArgDesc(ArgN: *SizeArgN, Out); |
| 1288 | printArgValueInfo(ArgN: *SizeArgN, State, Call, Out); |
| 1289 | if (SizeMultiplierArgN) { |
| 1290 | Out << " times the " ; |
| 1291 | printArgDesc(ArgN: *SizeMultiplierArgN, Out); |
| 1292 | printArgValueInfo(ArgN: *SizeMultiplierArgN, State, Call, Out); |
| 1293 | } |
| 1294 | } |
| 1295 | } |
| 1296 | |
| 1297 | bool StdLibraryFunctionsChecker::BufferSizeConstraint::describeArgumentValue( |
| 1298 | const CallEvent &Call, ProgramStateRef State, const Summary &Summary, |
| 1299 | llvm::raw_ostream &Out) const { |
| 1300 | SVal BufV = getArgSVal(Call, ArgN: getArgNo()); |
| 1301 | SVal BufDynSize = getDynamicExtentWithOffset(State, BufV); |
| 1302 | if (const llvm::APSInt *Val = |
| 1303 | State->getStateManager().getSValBuilder().getKnownValue(state: State, |
| 1304 | val: BufDynSize)) { |
| 1305 | Out << "is a buffer with size " << *Val; |
| 1306 | return true; |
| 1307 | } |
| 1308 | return false; |
| 1309 | } |
| 1310 | |
| 1311 | void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, |
| 1312 | CheckerContext &C) const { |
| 1313 | std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); |
| 1314 | if (!FoundSummary) |
| 1315 | return; |
| 1316 | |
| 1317 | const Summary &Summary = *FoundSummary; |
| 1318 | ProgramStateRef State = C.getState(); |
| 1319 | |
| 1320 | ProgramStateRef NewState = State; |
| 1321 | ExplodedNode *NewNode = C.getPredecessor(); |
| 1322 | for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) { |
| 1323 | ValueConstraintPtr NegatedConstraint = Constraint->negate(); |
| 1324 | ProgramStateRef SuccessSt = Constraint->apply(State: NewState, Call, Summary, C); |
| 1325 | ProgramStateRef FailureSt = |
| 1326 | NegatedConstraint->apply(State: NewState, Call, Summary, C); |
| 1327 | // The argument constraint is not satisfied. |
| 1328 | if (FailureSt && !SuccessSt) { |
| 1329 | if (ExplodedNode *N = C.generateErrorNode(State, Pred: NewNode)) |
| 1330 | reportBug(Call, N, VC: Constraint.get(), NegatedVC: NegatedConstraint.get(), Summary, |
| 1331 | C); |
| 1332 | break; |
| 1333 | } |
| 1334 | // We will apply the constraint even if we cannot reason about the |
| 1335 | // argument. This means both SuccessSt and FailureSt can be true. If we |
| 1336 | // weren't applying the constraint that would mean that symbolic |
| 1337 | // execution continues on a code whose behaviour is undefined. |
| 1338 | assert(SuccessSt); |
| 1339 | NewState = SuccessSt; |
| 1340 | if (NewState != State) { |
| 1341 | SmallString<128> Msg; |
| 1342 | llvm::raw_svector_ostream Os(Msg); |
| 1343 | Os << "Assuming that the " ; |
| 1344 | printArgDesc(ArgN: Constraint->getArgNo(), Out&: Os); |
| 1345 | Os << " to '" ; |
| 1346 | Os << getFunctionName(Call); |
| 1347 | Os << "' " ; |
| 1348 | Constraint->describe(DK: ValueConstraint::Assumption, Call, State: NewState, Summary, |
| 1349 | Out&: Os); |
| 1350 | const auto ArgSVal = Call.getArgSVal(Index: Constraint->getArgNo()); |
| 1351 | NewNode = C.addTransition( |
| 1352 | State: NewState, Pred: NewNode, |
| 1353 | Tag: C.getNoteTag(Cb: [Msg = std::move(Msg), ArgSVal]( |
| 1354 | PathSensitiveBugReport &BR, llvm::raw_ostream &OS) { |
| 1355 | if (BR.isInteresting(V: ArgSVal)) |
| 1356 | OS << Msg; |
| 1357 | })); |
| 1358 | } |
| 1359 | } |
| 1360 | } |
| 1361 | |
| 1362 | void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, |
| 1363 | CheckerContext &C) const { |
| 1364 | std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); |
| 1365 | if (!FoundSummary) |
| 1366 | return; |
| 1367 | |
| 1368 | // Now apply the constraints. |
| 1369 | const Summary &Summary = *FoundSummary; |
| 1370 | ProgramStateRef State = C.getState(); |
| 1371 | ExplodedNode *Node = C.getPredecessor(); |
| 1372 | |
| 1373 | // Apply case/branch specifications. |
| 1374 | for (const SummaryCase &Case : Summary.getCases()) { |
| 1375 | ProgramStateRef NewState = State; |
| 1376 | for (const ValueConstraintPtr &Constraint : Case.getConstraints()) { |
| 1377 | NewState = Constraint->apply(State: NewState, Call, Summary, C); |
| 1378 | if (!NewState) |
| 1379 | break; |
| 1380 | } |
| 1381 | |
| 1382 | if (NewState) |
| 1383 | NewState = Case.getErrnoConstraint().apply(State: NewState, Call, Summary, C); |
| 1384 | |
| 1385 | if (!NewState) |
| 1386 | continue; |
| 1387 | |
| 1388 | // Here it's possible that NewState == State, e.g. when other checkers |
| 1389 | // already applied the same constraints (or stricter ones). |
| 1390 | // Still add these note tags, the other checker should add only its |
| 1391 | // specialized note tags. These general note tags are handled always by |
| 1392 | // StdLibraryFunctionsChecker. |
| 1393 | |
| 1394 | ExplodedNode *Pred = Node; |
| 1395 | DeclarationName FunctionName = |
| 1396 | cast<NamedDecl>(Val: Call.getDecl())->getDeclName(); |
| 1397 | |
| 1398 | std::string ErrnoNote = Case.getErrnoConstraint().describe(C); |
| 1399 | std::string CaseNote; |
| 1400 | if (Case.getNote().empty()) { |
| 1401 | if (!ErrnoNote.empty()) |
| 1402 | ErrnoNote = |
| 1403 | llvm::formatv(Fmt: "After calling '{0}' {1}" , Vals&: FunctionName, Vals&: ErrnoNote); |
| 1404 | } else { |
| 1405 | // Disable formatv() validation as the case note may not always have the |
| 1406 | // {0} placeholder for function name. |
| 1407 | CaseNote = |
| 1408 | llvm::formatv(Validate: false, Fmt: Case.getNote().str().c_str(), Vals&: FunctionName); |
| 1409 | } |
| 1410 | const SVal RV = Call.getReturnValue(); |
| 1411 | |
| 1412 | if (Summary.getInvalidationKd() == EvalCallAsPure) { |
| 1413 | // Do not expect that errno is interesting (the "pure" functions do not |
| 1414 | // affect it). |
| 1415 | if (!CaseNote.empty()) { |
| 1416 | const NoteTag *Tag = C.getNoteTag( |
| 1417 | Cb: [Node, CaseNote, RV](PathSensitiveBugReport &BR) -> std::string { |
| 1418 | // Try to omit the note if we know in advance which branch is |
| 1419 | // taken (this means, only one branch exists). |
| 1420 | // This check is performed inside the lambda, after other |
| 1421 | // (or this) checkers had a chance to add other successors. |
| 1422 | // Dereferencing the saved node object is valid because it's part |
| 1423 | // of a bug report call sequence. |
| 1424 | // FIXME: This check is not exact. We may be here after a state |
| 1425 | // split that was performed by another checker (and can not find |
| 1426 | // the successors). This is why this check is only used in the |
| 1427 | // EvalCallAsPure case. |
| 1428 | if (BR.isInteresting(V: RV) && Node->succ_size() > 1) |
| 1429 | return CaseNote; |
| 1430 | return "" ; |
| 1431 | }); |
| 1432 | Pred = C.addTransition(State: NewState, Pred, Tag); |
| 1433 | } |
| 1434 | } else { |
| 1435 | if (!CaseNote.empty() || !ErrnoNote.empty()) { |
| 1436 | const NoteTag *Tag = |
| 1437 | C.getNoteTag(Cb: [CaseNote, ErrnoNote, |
| 1438 | RV](PathSensitiveBugReport &BR) -> std::string { |
| 1439 | // If 'errno' is interesting, show the user a note about the case |
| 1440 | // (what happened at the function call) and about how 'errno' |
| 1441 | // causes the problem. ErrnoChecker sets the errno (but not RV) to |
| 1442 | // interesting. |
| 1443 | // If only the return value is interesting, show only the case |
| 1444 | // note. |
| 1445 | std::optional<Loc> ErrnoLoc = |
| 1446 | errno_modeling::getErrnoLoc(State: BR.getErrorNode()->getState()); |
| 1447 | bool ErrnoImportant = !ErrnoNote.empty() && ErrnoLoc && |
| 1448 | BR.isInteresting(R: ErrnoLoc->getAsRegion()); |
| 1449 | if (ErrnoImportant) { |
| 1450 | BR.markNotInteresting(R: ErrnoLoc->getAsRegion()); |
| 1451 | if (CaseNote.empty()) |
| 1452 | return ErrnoNote; |
| 1453 | return llvm::formatv(Fmt: "{0}; {1}" , Vals: CaseNote, Vals: ErrnoNote); |
| 1454 | } else { |
| 1455 | if (BR.isInteresting(V: RV)) |
| 1456 | return CaseNote; |
| 1457 | } |
| 1458 | return "" ; |
| 1459 | }); |
| 1460 | Pred = C.addTransition(State: NewState, Pred, Tag); |
| 1461 | } |
| 1462 | } |
| 1463 | |
| 1464 | // Add the transition if no note tag was added. |
| 1465 | if (Pred == Node && NewState != State) |
| 1466 | C.addTransition(State: NewState); |
| 1467 | } |
| 1468 | } |
| 1469 | |
| 1470 | bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, |
| 1471 | CheckerContext &C) const { |
| 1472 | std::optional<Summary> FoundSummary = findFunctionSummary(Call, C); |
| 1473 | if (!FoundSummary) |
| 1474 | return false; |
| 1475 | |
| 1476 | const Summary &Summary = *FoundSummary; |
| 1477 | switch (Summary.getInvalidationKd()) { |
| 1478 | case EvalCallAsPure: { |
| 1479 | ProgramStateRef State = C.getState(); |
| 1480 | const LocationContext *LC = C.getLocationContext(); |
| 1481 | const auto *CE = cast<CallExpr>(Val: Call.getOriginExpr()); |
| 1482 | SVal V = C.getSValBuilder().conjureSymbolVal(call: Call, visitCount: C.blockCount()); |
| 1483 | State = State->BindExpr(S: CE, LCtx: LC, V); |
| 1484 | |
| 1485 | C.addTransition(State); |
| 1486 | |
| 1487 | return true; |
| 1488 | } |
| 1489 | case NoEvalCall: |
| 1490 | // Summary tells us to avoid performing eval::Call. The function is possibly |
| 1491 | // evaluated by another checker, or evaluated conservatively. |
| 1492 | return false; |
| 1493 | } |
| 1494 | llvm_unreachable("Unknown invalidation kind!" ); |
| 1495 | } |
| 1496 | |
| 1497 | bool StdLibraryFunctionsChecker::Signature::matches( |
| 1498 | const FunctionDecl *FD) const { |
| 1499 | assert(!isInvalid()); |
| 1500 | // Check the number of arguments. |
| 1501 | if (FD->param_size() != ArgTys.size()) |
| 1502 | return false; |
| 1503 | |
| 1504 | // The "restrict" keyword is illegal in C++, however, many libc |
| 1505 | // implementations use the "__restrict" compiler intrinsic in functions |
| 1506 | // prototypes. The "__restrict" keyword qualifies a type as a restricted type |
| 1507 | // even in C++. |
| 1508 | // In case of any non-C99 languages, we don't want to match based on the |
| 1509 | // restrict qualifier because we cannot know if the given libc implementation |
| 1510 | // qualifies the paramter type or not. |
| 1511 | auto RemoveRestrict = [&FD](QualType T) { |
| 1512 | if (!FD->getASTContext().getLangOpts().C99) |
| 1513 | T.removeLocalRestrict(); |
| 1514 | return T; |
| 1515 | }; |
| 1516 | |
| 1517 | // Check the return type. |
| 1518 | if (!isIrrelevant(T: RetTy)) { |
| 1519 | QualType FDRetTy = RemoveRestrict(FD->getReturnType().getCanonicalType()); |
| 1520 | if (RetTy != FDRetTy) |
| 1521 | return false; |
| 1522 | } |
| 1523 | |
| 1524 | // Check the argument types. |
| 1525 | for (auto [Idx, ArgTy] : llvm::enumerate(First: ArgTys)) { |
| 1526 | if (isIrrelevant(T: ArgTy)) |
| 1527 | continue; |
| 1528 | QualType FDArgTy = |
| 1529 | RemoveRestrict(FD->getParamDecl(i: Idx)->getType().getCanonicalType()); |
| 1530 | if (ArgTy != FDArgTy) |
| 1531 | return false; |
| 1532 | } |
| 1533 | |
| 1534 | return true; |
| 1535 | } |
| 1536 | |
| 1537 | std::optional<StdLibraryFunctionsChecker::Summary> |
| 1538 | StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD, |
| 1539 | CheckerContext &C) const { |
| 1540 | if (!FD) |
| 1541 | return std::nullopt; |
| 1542 | |
| 1543 | initFunctionSummaries(C); |
| 1544 | |
| 1545 | auto FSMI = FunctionSummaryMap.find(Val: FD->getCanonicalDecl()); |
| 1546 | if (FSMI == FunctionSummaryMap.end()) |
| 1547 | return std::nullopt; |
| 1548 | return FSMI->second; |
| 1549 | } |
| 1550 | |
| 1551 | std::optional<StdLibraryFunctionsChecker::Summary> |
| 1552 | StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call, |
| 1553 | CheckerContext &C) const { |
| 1554 | const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Val: Call.getDecl()); |
| 1555 | if (!FD) |
| 1556 | return std::nullopt; |
| 1557 | return findFunctionSummary(FD, C); |
| 1558 | } |
| 1559 | |
| 1560 | void StdLibraryFunctionsChecker::initFunctionSummaries( |
| 1561 | CheckerContext &C) const { |
| 1562 | if (SummariesInitialized) |
| 1563 | return; |
| 1564 | SummariesInitialized = true; |
| 1565 | |
| 1566 | SValBuilder &SVB = C.getSValBuilder(); |
| 1567 | BasicValueFactory &BVF = SVB.getBasicValueFactory(); |
| 1568 | const ASTContext &ACtx = BVF.getContext(); |
| 1569 | Preprocessor &PP = C.getPreprocessor(); |
| 1570 | |
| 1571 | // Helper class to lookup a type by its name. |
| 1572 | class LookupType { |
| 1573 | const ASTContext &ACtx; |
| 1574 | |
| 1575 | public: |
| 1576 | LookupType(const ASTContext &ACtx) : ACtx(ACtx) {} |
| 1577 | |
| 1578 | // Find the type. If not found then the optional is not set. |
| 1579 | std::optional<QualType> operator()(StringRef Name) { |
| 1580 | IdentifierInfo &II = ACtx.Idents.get(Name); |
| 1581 | auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(Name: &II); |
| 1582 | if (LookupRes.empty()) |
| 1583 | return std::nullopt; |
| 1584 | |
| 1585 | // Prioritize typedef declarations. |
| 1586 | // This is needed in case of C struct typedefs. E.g.: |
| 1587 | // typedef struct FILE FILE; |
| 1588 | // In this case, we have a RecordDecl 'struct FILE' with the name 'FILE' |
| 1589 | // and we have a TypedefDecl with the name 'FILE'. |
| 1590 | for (Decl *D : LookupRes) |
| 1591 | if (auto *TD = dyn_cast<TypedefNameDecl>(Val: D)) |
| 1592 | return ACtx.getTypeDeclType(Decl: TD).getCanonicalType(); |
| 1593 | |
| 1594 | // Find the first TypeDecl. |
| 1595 | // There maybe cases when a function has the same name as a struct. |
| 1596 | // E.g. in POSIX: `struct stat` and the function `stat()`: |
| 1597 | // int stat(const char *restrict path, struct stat *restrict buf); |
| 1598 | for (Decl *D : LookupRes) |
| 1599 | if (auto *TD = dyn_cast<TypeDecl>(Val: D)) |
| 1600 | return ACtx.getTypeDeclType(Decl: TD).getCanonicalType(); |
| 1601 | return std::nullopt; |
| 1602 | } |
| 1603 | } lookupTy(ACtx); |
| 1604 | |
| 1605 | // Below are auxiliary classes to handle optional types that we get as a |
| 1606 | // result of the lookup. |
| 1607 | class GetRestrictTy { |
| 1608 | const ASTContext &ACtx; |
| 1609 | |
| 1610 | public: |
| 1611 | GetRestrictTy(const ASTContext &ACtx) : ACtx(ACtx) {} |
| 1612 | QualType operator()(QualType Ty) { |
| 1613 | return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(T: Ty) : Ty; |
| 1614 | } |
| 1615 | std::optional<QualType> operator()(std::optional<QualType> Ty) { |
| 1616 | if (Ty) |
| 1617 | return operator()(Ty: *Ty); |
| 1618 | return std::nullopt; |
| 1619 | } |
| 1620 | } getRestrictTy(ACtx); |
| 1621 | class GetPointerTy { |
| 1622 | const ASTContext &ACtx; |
| 1623 | |
| 1624 | public: |
| 1625 | GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {} |
| 1626 | QualType operator()(QualType Ty) { return ACtx.getPointerType(T: Ty); } |
| 1627 | std::optional<QualType> operator()(std::optional<QualType> Ty) { |
| 1628 | if (Ty) |
| 1629 | return operator()(Ty: *Ty); |
| 1630 | return std::nullopt; |
| 1631 | } |
| 1632 | } getPointerTy(ACtx); |
| 1633 | class { |
| 1634 | public: |
| 1635 | std::optional<QualType> operator()(std::optional<QualType> Ty) { |
| 1636 | return Ty ? std::optional<QualType>(Ty->withConst()) : std::nullopt; |
| 1637 | } |
| 1638 | QualType operator()(QualType Ty) { return Ty.withConst(); } |
| 1639 | } getConstTy; |
| 1640 | class GetMaxValue { |
| 1641 | BasicValueFactory &BVF; |
| 1642 | |
| 1643 | public: |
| 1644 | GetMaxValue(BasicValueFactory &BVF) : BVF(BVF) {} |
| 1645 | std::optional<RangeInt> operator()(QualType Ty) { |
| 1646 | return BVF.getMaxValue(T: Ty)->getLimitedValue(); |
| 1647 | } |
| 1648 | std::optional<RangeInt> operator()(std::optional<QualType> Ty) { |
| 1649 | if (Ty) { |
| 1650 | return operator()(Ty: *Ty); |
| 1651 | } |
| 1652 | return std::nullopt; |
| 1653 | } |
| 1654 | } getMaxValue(BVF); |
| 1655 | |
| 1656 | // These types are useful for writing specifications quickly, |
| 1657 | // New specifications should probably introduce more types. |
| 1658 | // Some types are hard to obtain from the AST, eg. "ssize_t". |
| 1659 | // In such cases it should be possible to provide multiple variants |
| 1660 | // of function summary for common cases (eg. ssize_t could be int or long |
| 1661 | // or long long, so three summary variants would be enough). |
| 1662 | // Of course, function variants are also useful for C++ overloads. |
| 1663 | const QualType VoidTy = ACtx.VoidTy; |
| 1664 | const QualType CharTy = ACtx.CharTy; |
| 1665 | const QualType WCharTy = ACtx.WCharTy; |
| 1666 | const QualType IntTy = ACtx.IntTy; |
| 1667 | const QualType UnsignedIntTy = ACtx.UnsignedIntTy; |
| 1668 | const QualType LongTy = ACtx.LongTy; |
| 1669 | const QualType SizeTy = ACtx.getSizeType(); |
| 1670 | |
| 1671 | const QualType VoidPtrTy = getPointerTy(VoidTy); // void * |
| 1672 | const QualType IntPtrTy = getPointerTy(IntTy); // int * |
| 1673 | const QualType UnsignedIntPtrTy = |
| 1674 | getPointerTy(UnsignedIntTy); // unsigned int * |
| 1675 | const QualType VoidPtrRestrictTy = getRestrictTy(VoidPtrTy); |
| 1676 | const QualType ConstVoidPtrTy = |
| 1677 | getPointerTy(getConstTy(VoidTy)); // const void * |
| 1678 | const QualType CharPtrTy = getPointerTy(CharTy); // char * |
| 1679 | const QualType CharPtrRestrictTy = getRestrictTy(CharPtrTy); |
| 1680 | const QualType ConstCharPtrTy = |
| 1681 | getPointerTy(getConstTy(CharTy)); // const char * |
| 1682 | const QualType ConstCharPtrRestrictTy = getRestrictTy(ConstCharPtrTy); |
| 1683 | const QualType Wchar_tPtrTy = getPointerTy(WCharTy); // wchar_t * |
| 1684 | const QualType ConstWchar_tPtrTy = |
| 1685 | getPointerTy(getConstTy(WCharTy)); // const wchar_t * |
| 1686 | const QualType ConstVoidPtrRestrictTy = getRestrictTy(ConstVoidPtrTy); |
| 1687 | const QualType SizePtrTy = getPointerTy(SizeTy); |
| 1688 | const QualType SizePtrRestrictTy = getRestrictTy(SizePtrTy); |
| 1689 | |
| 1690 | const RangeInt IntMax = BVF.getMaxValue(T: IntTy)->getLimitedValue(); |
| 1691 | const RangeInt UnsignedIntMax = |
| 1692 | BVF.getMaxValue(T: UnsignedIntTy)->getLimitedValue(); |
| 1693 | const RangeInt LongMax = BVF.getMaxValue(T: LongTy)->getLimitedValue(); |
| 1694 | const RangeInt SizeMax = BVF.getMaxValue(T: SizeTy)->getLimitedValue(); |
| 1695 | |
| 1696 | // Set UCharRangeMax to min of int or uchar maximum value. |
| 1697 | // The C standard states that the arguments of functions like isalpha must |
| 1698 | // be representable as an unsigned char. Their type is 'int', so the max |
| 1699 | // value of the argument should be min(UCharMax, IntMax). This just happen |
| 1700 | // to be true for commonly used and well tested instruction set |
| 1701 | // architectures, but not for others. |
| 1702 | const RangeInt UCharRangeMax = |
| 1703 | std::min(a: BVF.getMaxValue(T: ACtx.UnsignedCharTy)->getLimitedValue(), b: IntMax); |
| 1704 | |
| 1705 | // Get platform dependent values of some macros. |
| 1706 | // Try our best to parse this from the Preprocessor, otherwise fallback to a |
| 1707 | // default value (what is found in a library header). |
| 1708 | const auto EOFv = tryExpandAsInteger(Macro: "EOF" , PP).value_or(u: -1); |
| 1709 | const auto AT_FDCWDv = tryExpandAsInteger(Macro: "AT_FDCWD" , PP).value_or(u: -100); |
| 1710 | |
| 1711 | // Auxiliary class to aid adding summaries to the summary map. |
| 1712 | struct AddToFunctionSummaryMap { |
| 1713 | const ASTContext &ACtx; |
| 1714 | FunctionSummaryMapType ⤅ |
| 1715 | bool DisplayLoadedSummaries; |
| 1716 | AddToFunctionSummaryMap(const ASTContext &ACtx, FunctionSummaryMapType &FSM, |
| 1717 | bool DisplayLoadedSummaries) |
| 1718 | : ACtx(ACtx), Map(FSM), DisplayLoadedSummaries(DisplayLoadedSummaries) { |
| 1719 | } |
| 1720 | |
| 1721 | // Add a summary to a FunctionDecl found by lookup. The lookup is performed |
| 1722 | // by the given Name, and in the global scope. The summary will be attached |
| 1723 | // to the found FunctionDecl only if the signatures match. |
| 1724 | // |
| 1725 | // Returns true if the summary has been added, false otherwise. |
| 1726 | bool operator()(StringRef Name, Signature Sign, Summary Sum) { |
| 1727 | if (Sign.isInvalid()) |
| 1728 | return false; |
| 1729 | IdentifierInfo &II = ACtx.Idents.get(Name); |
| 1730 | auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(Name: &II); |
| 1731 | if (LookupRes.empty()) |
| 1732 | return false; |
| 1733 | for (Decl *D : LookupRes) { |
| 1734 | if (auto *FD = dyn_cast<FunctionDecl>(Val: D)) { |
| 1735 | if (Sum.matchesAndSet(Sign, FD)) { |
| 1736 | auto Res = Map.insert(KV: {FD->getCanonicalDecl(), Sum}); |
| 1737 | assert(Res.second && "Function already has a summary set!" ); |
| 1738 | (void)Res; |
| 1739 | if (DisplayLoadedSummaries) { |
| 1740 | llvm::errs() << "Loaded summary for: " ; |
| 1741 | FD->print(Out&: llvm::errs()); |
| 1742 | llvm::errs() << "\n" ; |
| 1743 | } |
| 1744 | return true; |
| 1745 | } |
| 1746 | } |
| 1747 | } |
| 1748 | return false; |
| 1749 | } |
| 1750 | // Add the same summary for different names with the Signature explicitly |
| 1751 | // given. |
| 1752 | void operator()(ArrayRef<StringRef> Names, Signature Sign, Summary Sum) { |
| 1753 | for (StringRef Name : Names) |
| 1754 | operator()(Name, Sign, Sum); |
| 1755 | } |
| 1756 | } addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries); |
| 1757 | |
| 1758 | // Below are helpers functions to create the summaries. |
| 1759 | auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, IntRangeVector Ranges, |
| 1760 | StringRef Desc = "" ) { |
| 1761 | return std::make_shared<RangeConstraint>(args&: ArgN, args&: Kind, args&: Ranges, args&: Desc); |
| 1762 | }; |
| 1763 | auto BufferSize = [](auto... Args) { |
| 1764 | return std::make_shared<BufferSizeConstraint>(Args...); |
| 1765 | }; |
| 1766 | struct { |
| 1767 | auto operator()(RangeKind Kind, IntRangeVector Ranges) { |
| 1768 | return std::make_shared<RangeConstraint>(args: Ret, args&: Kind, args&: Ranges); |
| 1769 | } |
| 1770 | auto operator()(BinaryOperator::Opcode Op, ArgNo OtherArgN) { |
| 1771 | return std::make_shared<ComparisonConstraint>(args: Ret, args&: Op, args&: OtherArgN); |
| 1772 | } |
| 1773 | } ReturnValueCondition; |
| 1774 | struct { |
| 1775 | auto operator()(RangeInt b, RangeInt e) { |
| 1776 | return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}}; |
| 1777 | } |
| 1778 | auto operator()(RangeInt b, std::optional<RangeInt> e) { |
| 1779 | if (e) |
| 1780 | return IntRangeVector{std::pair<RangeInt, RangeInt>{b, *e}}; |
| 1781 | return IntRangeVector{}; |
| 1782 | } |
| 1783 | auto operator()(std::pair<RangeInt, RangeInt> i0, |
| 1784 | std::pair<RangeInt, std::optional<RangeInt>> i1) { |
| 1785 | if (i1.second) |
| 1786 | return IntRangeVector{i0, {i1.first, *(i1.second)}}; |
| 1787 | return IntRangeVector{i0}; |
| 1788 | } |
| 1789 | } Range; |
| 1790 | auto SingleValue = [](RangeInt v) { |
| 1791 | return IntRangeVector{std::pair<RangeInt, RangeInt>{v, v}}; |
| 1792 | }; |
| 1793 | auto LessThanOrEq = BO_LE; |
| 1794 | auto NotNull = [&](ArgNo ArgN) { |
| 1795 | return std::make_shared<NullnessConstraint>(args&: ArgN); |
| 1796 | }; |
| 1797 | auto IsNull = [&](ArgNo ArgN) { |
| 1798 | return std::make_shared<NullnessConstraint>(args&: ArgN, args: false); |
| 1799 | }; |
| 1800 | auto NotNullBuffer = [&](ArgNo ArgN, ArgNo SizeArg1N, |
| 1801 | std::optional<ArgNo> SizeArg2N = std::nullopt) { |
| 1802 | return std::make_shared<BufferNullnessConstraint>(args&: ArgN, args&: SizeArg1N, |
| 1803 | args&: SizeArg2N); |
| 1804 | }; |
| 1805 | |
| 1806 | std::optional<QualType> FileTy = lookupTy("FILE" ); |
| 1807 | std::optional<QualType> FilePtrTy = getPointerTy(FileTy); |
| 1808 | std::optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy); |
| 1809 | |
| 1810 | std::optional<QualType> FPosTTy = lookupTy("fpos_t" ); |
| 1811 | std::optional<QualType> FPosTPtrTy = getPointerTy(FPosTTy); |
| 1812 | std::optional<QualType> ConstFPosTPtrTy = getPointerTy(getConstTy(FPosTTy)); |
| 1813 | std::optional<QualType> FPosTPtrRestrictTy = getRestrictTy(FPosTPtrTy); |
| 1814 | |
| 1815 | constexpr llvm::StringLiteral GenericSuccessMsg( |
| 1816 | "Assuming that '{0}' is successful" ); |
| 1817 | constexpr llvm::StringLiteral GenericFailureMsg("Assuming that '{0}' fails" ); |
| 1818 | |
| 1819 | // We are finally ready to define specifications for all supported functions. |
| 1820 | // |
| 1821 | // Argument ranges should always cover all variants. If return value |
| 1822 | // is completely unknown, omit it from the respective range set. |
| 1823 | // |
| 1824 | // Every item in the list of range sets represents a particular |
| 1825 | // execution path the analyzer would need to explore once |
| 1826 | // the call is modeled - a new program state is constructed |
| 1827 | // for every range set, and each range line in the range set |
| 1828 | // corresponds to a specific constraint within this state. |
| 1829 | |
| 1830 | // The isascii() family of functions. |
| 1831 | // The behavior is undefined if the value of the argument is not |
| 1832 | // representable as unsigned char or is not equal to EOF. See e.g. C99 |
| 1833 | // 7.4.1.2 The isalpha function (p: 181-182). |
| 1834 | addToFunctionSummaryMap( |
| 1835 | "isalnum" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1836 | Summary(EvalCallAsPure) |
| 1837 | // Boils down to isupper() or islower() or isdigit(). |
| 1838 | .Case(CS: {ArgumentCondition(0U, WithinRange, |
| 1839 | {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}}), |
| 1840 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1841 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is alphanumeric" ) |
| 1842 | // The locale-specific range. |
| 1843 | // No post-condition. We are completely unaware of |
| 1844 | // locale-specific return values. |
| 1845 | .Case(CS: {ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, |
| 1846 | ErrnoC: ErrnoIrrelevant) |
| 1847 | .Case( |
| 1848 | CS: {ArgumentCondition( |
| 1849 | 0U, OutOfRange, |
| 1850 | {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), |
| 1851 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1852 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is non-alphanumeric" ) |
| 1853 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, |
| 1854 | {{EOFv, EOFv}, {0, UCharRangeMax}}, |
| 1855 | "an unsigned char value or EOF" ))); |
| 1856 | addToFunctionSummaryMap( |
| 1857 | "isalpha" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1858 | Summary(EvalCallAsPure) |
| 1859 | .Case(CS: {ArgumentCondition(0U, WithinRange, {{'A', 'Z'}, {'a', 'z'}}), |
| 1860 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1861 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is alphabetical" ) |
| 1862 | // The locale-specific range. |
| 1863 | .Case(CS: {ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, |
| 1864 | ErrnoC: ErrnoIrrelevant) |
| 1865 | .Case(CS: {ArgumentCondition( |
| 1866 | 0U, OutOfRange, |
| 1867 | {{'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), |
| 1868 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1869 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is non-alphabetical" )); |
| 1870 | addToFunctionSummaryMap( |
| 1871 | "isascii" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1872 | Summary(EvalCallAsPure) |
| 1873 | .Case(CS: {ArgumentCondition(0U, WithinRange, Range(0, 127)), |
| 1874 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1875 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is an ASCII character" ) |
| 1876 | .Case(CS: {ArgumentCondition(0U, OutOfRange, Range(0, 127)), |
| 1877 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1878 | ErrnoC: ErrnoIrrelevant, |
| 1879 | Note: "Assuming the character is not an ASCII character" )); |
| 1880 | addToFunctionSummaryMap( |
| 1881 | "isblank" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1882 | Summary(EvalCallAsPure) |
| 1883 | .Case(CS: {ArgumentCondition(0U, WithinRange, {{'\t', '\t'}, {' ', ' '}}), |
| 1884 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1885 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is a blank character" ) |
| 1886 | .Case(CS: {ArgumentCondition(0U, OutOfRange, {{'\t', '\t'}, {' ', ' '}}), |
| 1887 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1888 | ErrnoC: ErrnoIrrelevant, |
| 1889 | Note: "Assuming the character is not a blank character" )); |
| 1890 | addToFunctionSummaryMap( |
| 1891 | "iscntrl" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1892 | Summary(EvalCallAsPure) |
| 1893 | .Case(CS: {ArgumentCondition(0U, WithinRange, {{0, 32}, {127, 127}}), |
| 1894 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1895 | ErrnoC: ErrnoIrrelevant, |
| 1896 | Note: "Assuming the character is a control character" ) |
| 1897 | .Case(CS: {ArgumentCondition(0U, OutOfRange, {{0, 32}, {127, 127}}), |
| 1898 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1899 | ErrnoC: ErrnoIrrelevant, |
| 1900 | Note: "Assuming the character is not a control character" )); |
| 1901 | addToFunctionSummaryMap( |
| 1902 | "isdigit" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1903 | Summary(EvalCallAsPure) |
| 1904 | .Case(CS: {ArgumentCondition(0U, WithinRange, Range('0', '9')), |
| 1905 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1906 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is a digit" ) |
| 1907 | .Case(CS: {ArgumentCondition(0U, OutOfRange, Range('0', '9')), |
| 1908 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1909 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is not a digit" )); |
| 1910 | addToFunctionSummaryMap( |
| 1911 | "isgraph" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1912 | Summary(EvalCallAsPure) |
| 1913 | .Case(CS: {ArgumentCondition(0U, WithinRange, Range(33, 126)), |
| 1914 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1915 | ErrnoC: ErrnoIrrelevant, |
| 1916 | Note: "Assuming the character has graphical representation" ) |
| 1917 | .Case( |
| 1918 | CS: {ArgumentCondition(0U, OutOfRange, Range(33, 126)), |
| 1919 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1920 | ErrnoC: ErrnoIrrelevant, |
| 1921 | Note: "Assuming the character does not have graphical representation" )); |
| 1922 | addToFunctionSummaryMap( |
| 1923 | "islower" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1924 | Summary(EvalCallAsPure) |
| 1925 | // Is certainly lowercase. |
| 1926 | .Case(CS: {ArgumentCondition(0U, WithinRange, Range('a', 'z')), |
| 1927 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1928 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is a lowercase letter" ) |
| 1929 | // Is ascii but not lowercase. |
| 1930 | .Case(CS: {ArgumentCondition(0U, WithinRange, Range(0, 127)), |
| 1931 | ArgumentCondition(0U, OutOfRange, Range('a', 'z')), |
| 1932 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1933 | ErrnoC: ErrnoIrrelevant, |
| 1934 | Note: "Assuming the character is not a lowercase letter" ) |
| 1935 | // The locale-specific range. |
| 1936 | .Case(CS: {ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, |
| 1937 | ErrnoC: ErrnoIrrelevant) |
| 1938 | // Is not an unsigned char. |
| 1939 | .Case(CS: {ArgumentCondition(0U, OutOfRange, Range(0, UCharRangeMax)), |
| 1940 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1941 | ErrnoC: ErrnoIrrelevant)); |
| 1942 | addToFunctionSummaryMap( |
| 1943 | "isprint" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1944 | Summary(EvalCallAsPure) |
| 1945 | .Case(CS: {ArgumentCondition(0U, WithinRange, Range(32, 126)), |
| 1946 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1947 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is printable" ) |
| 1948 | .Case(CS: {ArgumentCondition(0U, OutOfRange, Range(32, 126)), |
| 1949 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1950 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is non-printable" )); |
| 1951 | addToFunctionSummaryMap( |
| 1952 | "ispunct" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1953 | Summary(EvalCallAsPure) |
| 1954 | .Case(CS: {ArgumentCondition( |
| 1955 | 0U, WithinRange, |
| 1956 | {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), |
| 1957 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1958 | ErrnoC: ErrnoIrrelevant, Note: "Assuming the character is a punctuation mark" ) |
| 1959 | .Case(CS: {ArgumentCondition( |
| 1960 | 0U, OutOfRange, |
| 1961 | {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), |
| 1962 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1963 | ErrnoC: ErrnoIrrelevant, |
| 1964 | Note: "Assuming the character is not a punctuation mark" )); |
| 1965 | addToFunctionSummaryMap( |
| 1966 | "isspace" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1967 | Summary(EvalCallAsPure) |
| 1968 | // Space, '\f', '\n', '\r', '\t', '\v'. |
| 1969 | .Case(CS: {ArgumentCondition(0U, WithinRange, {{9, 13}, {' ', ' '}}), |
| 1970 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1971 | ErrnoC: ErrnoIrrelevant, |
| 1972 | Note: "Assuming the character is a whitespace character" ) |
| 1973 | // The locale-specific range. |
| 1974 | .Case(CS: {ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, |
| 1975 | ErrnoC: ErrnoIrrelevant) |
| 1976 | .Case(CS: {ArgumentCondition(0U, OutOfRange, |
| 1977 | {{9, 13}, {' ', ' '}, {128, UCharRangeMax}}), |
| 1978 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1979 | ErrnoC: ErrnoIrrelevant, |
| 1980 | Note: "Assuming the character is not a whitespace character" )); |
| 1981 | addToFunctionSummaryMap( |
| 1982 | "isupper" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 1983 | Summary(EvalCallAsPure) |
| 1984 | // Is certainly uppercase. |
| 1985 | .Case(CS: {ArgumentCondition(0U, WithinRange, Range('A', 'Z')), |
| 1986 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 1987 | ErrnoC: ErrnoIrrelevant, |
| 1988 | Note: "Assuming the character is an uppercase letter" ) |
| 1989 | // The locale-specific range. |
| 1990 | .Case(CS: {ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}, |
| 1991 | ErrnoC: ErrnoIrrelevant) |
| 1992 | // Other. |
| 1993 | .Case(CS: {ArgumentCondition(0U, OutOfRange, |
| 1994 | {{'A', 'Z'}, {128, UCharRangeMax}}), |
| 1995 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 1996 | ErrnoC: ErrnoIrrelevant, |
| 1997 | Note: "Assuming the character is not an uppercase letter" )); |
| 1998 | addToFunctionSummaryMap( |
| 1999 | "isxdigit" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 2000 | Summary(EvalCallAsPure) |
| 2001 | .Case(CS: {ArgumentCondition(0U, WithinRange, |
| 2002 | {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), |
| 2003 | ReturnValueCondition(OutOfRange, SingleValue(0))}, |
| 2004 | ErrnoC: ErrnoIrrelevant, |
| 2005 | Note: "Assuming the character is a hexadecimal digit" ) |
| 2006 | .Case(CS: {ArgumentCondition(0U, OutOfRange, |
| 2007 | {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), |
| 2008 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 2009 | ErrnoC: ErrnoIrrelevant, |
| 2010 | Note: "Assuming the character is not a hexadecimal digit" )); |
| 2011 | addToFunctionSummaryMap( |
| 2012 | "toupper" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 2013 | Summary(EvalCallAsPure) |
| 2014 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, |
| 2015 | {{EOFv, EOFv}, {0, UCharRangeMax}}, |
| 2016 | "an unsigned char value or EOF" ))); |
| 2017 | addToFunctionSummaryMap( |
| 2018 | "tolower" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 2019 | Summary(EvalCallAsPure) |
| 2020 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, |
| 2021 | {{EOFv, EOFv}, {0, UCharRangeMax}}, |
| 2022 | "an unsigned char value or EOF" ))); |
| 2023 | addToFunctionSummaryMap( |
| 2024 | "toascii" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 2025 | Summary(EvalCallAsPure) |
| 2026 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, |
| 2027 | {{EOFv, EOFv}, {0, UCharRangeMax}}, |
| 2028 | "an unsigned char value or EOF" ))); |
| 2029 | |
| 2030 | addToFunctionSummaryMap( |
| 2031 | "getchar" , Signature(ArgTypes{}, RetType{IntTy}), |
| 2032 | Summary(NoEvalCall) |
| 2033 | .Case(CS: {ReturnValueCondition(WithinRange, |
| 2034 | {{EOFv, EOFv}, {0, UCharRangeMax}})}, |
| 2035 | ErrnoC: ErrnoIrrelevant)); |
| 2036 | |
| 2037 | // read()-like functions that never return more than buffer size. |
| 2038 | auto FreadSummary = |
| 2039 | Summary(NoEvalCall) |
| 2040 | .Case(CS: {ArgumentCondition(1U, WithinRange, Range(1, SizeMax)), |
| 2041 | ArgumentCondition(2U, WithinRange, Range(1, SizeMax)), |
| 2042 | ReturnValueCondition(BO_LT, ArgNo(2)), |
| 2043 | ReturnValueCondition(WithinRange, Range(0, SizeMax))}, |
| 2044 | ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2045 | .Case(CS: {ArgumentCondition(1U, WithinRange, Range(1, SizeMax)), |
| 2046 | ReturnValueCondition(BO_EQ, ArgNo(2)), |
| 2047 | ReturnValueCondition(WithinRange, Range(0, SizeMax))}, |
| 2048 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2049 | .Case(CS: {ArgumentCondition(1U, WithinRange, SingleValue(0)), |
| 2050 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 2051 | ErrnoC: ErrnoMustNotBeChecked, |
| 2052 | Note: "Assuming that argument 'size' to '{0}' is 0" ) |
| 2053 | .ArgConstraint(VC: NotNullBuffer(ArgNo(0), ArgNo(1), ArgNo(2))) |
| 2054 | .ArgConstraint(VC: NotNull(ArgNo(3))) |
| 2055 | .ArgConstraint(VC: BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), |
| 2056 | /*BufSizeMultiplier=*/ArgNo(2))); |
| 2057 | |
| 2058 | // size_t fread(void *restrict ptr, size_t size, size_t nitems, |
| 2059 | // FILE *restrict stream); |
| 2060 | addToFunctionSummaryMap( |
| 2061 | "fread" , |
| 2062 | Signature(ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, FilePtrRestrictTy}, |
| 2063 | RetType{SizeTy}), |
| 2064 | FreadSummary); |
| 2065 | // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, |
| 2066 | // FILE *restrict stream); |
| 2067 | addToFunctionSummaryMap("fwrite" , |
| 2068 | Signature(ArgTypes{ConstVoidPtrRestrictTy, SizeTy, |
| 2069 | SizeTy, FilePtrRestrictTy}, |
| 2070 | RetType{SizeTy}), |
| 2071 | FreadSummary); |
| 2072 | |
| 2073 | std::optional<QualType> Ssize_tTy = lookupTy("ssize_t" ); |
| 2074 | std::optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy); |
| 2075 | |
| 2076 | auto ReadSummary = |
| 2077 | Summary(NoEvalCall) |
| 2078 | .Case(CS: {ReturnValueCondition(LessThanOrEq, ArgNo(2)), |
| 2079 | ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}, |
| 2080 | ErrnoC: ErrnoIrrelevant); |
| 2081 | |
| 2082 | // FIXME these are actually defined by POSIX and not by the C standard, we |
| 2083 | // should handle them together with the rest of the POSIX functions. |
| 2084 | // ssize_t read(int fildes, void *buf, size_t nbyte); |
| 2085 | addToFunctionSummaryMap( |
| 2086 | "read" , Signature(ArgTypes{IntTy, VoidPtrTy, SizeTy}, RetType{Ssize_tTy}), |
| 2087 | ReadSummary); |
| 2088 | // ssize_t write(int fildes, const void *buf, size_t nbyte); |
| 2089 | addToFunctionSummaryMap( |
| 2090 | "write" , |
| 2091 | Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy}, RetType{Ssize_tTy}), |
| 2092 | ReadSummary); |
| 2093 | |
| 2094 | auto GetLineSummary = |
| 2095 | Summary(NoEvalCall) |
| 2096 | .Case(CS: {ReturnValueCondition(WithinRange, |
| 2097 | Range({-1, -1}, {1, Ssize_tMax}))}, |
| 2098 | ErrnoC: ErrnoIrrelevant); |
| 2099 | |
| 2100 | QualType CharPtrPtrRestrictTy = getRestrictTy(getPointerTy(CharPtrTy)); |
| 2101 | |
| 2102 | // getline()-like functions either fail or read at least the delimiter. |
| 2103 | // FIXME these are actually defined by POSIX and not by the C standard, we |
| 2104 | // should handle them together with the rest of the POSIX functions. |
| 2105 | // ssize_t getline(char **restrict lineptr, size_t *restrict n, |
| 2106 | // FILE *restrict stream); |
| 2107 | addToFunctionSummaryMap( |
| 2108 | "getline" , |
| 2109 | Signature( |
| 2110 | ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, FilePtrRestrictTy}, |
| 2111 | RetType{Ssize_tTy}), |
| 2112 | GetLineSummary); |
| 2113 | // ssize_t getdelim(char **restrict lineptr, size_t *restrict n, |
| 2114 | // int delimiter, FILE *restrict stream); |
| 2115 | addToFunctionSummaryMap( |
| 2116 | "getdelim" , |
| 2117 | Signature(ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, IntTy, |
| 2118 | FilePtrRestrictTy}, |
| 2119 | RetType{Ssize_tTy}), |
| 2120 | GetLineSummary); |
| 2121 | |
| 2122 | { |
| 2123 | Summary GetenvSummary = |
| 2124 | Summary(NoEvalCall) |
| 2125 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2126 | .Case(CS: {NotNull(Ret)}, ErrnoC: ErrnoIrrelevant, |
| 2127 | Note: "Assuming the environment variable exists" ); |
| 2128 | // In untrusted environments the envvar might not exist. |
| 2129 | if (!ShouldAssumeControlledEnvironment) |
| 2130 | GetenvSummary.Case(CS: {NotNull(Ret)->negate()}, ErrnoC: ErrnoIrrelevant, |
| 2131 | Note: "Assuming the environment variable does not exist" ); |
| 2132 | |
| 2133 | // char *getenv(const char *name); |
| 2134 | addToFunctionSummaryMap( |
| 2135 | "getenv" , Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}), |
| 2136 | std::move(GetenvSummary)); |
| 2137 | } |
| 2138 | |
| 2139 | if (!ModelPOSIX) { |
| 2140 | // Without POSIX use of 'errno' is not specified (in these cases). |
| 2141 | // Add these functions without 'errno' checks. |
| 2142 | addToFunctionSummaryMap( |
| 2143 | {"getc" , "fgetc" }, Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), |
| 2144 | Summary(NoEvalCall) |
| 2145 | .Case(CS: {ReturnValueCondition(WithinRange, |
| 2146 | {{EOFv, EOFv}, {0, UCharRangeMax}})}, |
| 2147 | ErrnoC: ErrnoIrrelevant) |
| 2148 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2149 | } else { |
| 2150 | const auto ReturnsZeroOrMinusOne = |
| 2151 | ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))}; |
| 2152 | const auto ReturnsZero = |
| 2153 | ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(0))}; |
| 2154 | const auto ReturnsMinusOne = |
| 2155 | ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(-1))}; |
| 2156 | const auto ReturnsEOF = |
| 2157 | ConstraintSet{ReturnValueCondition(WithinRange, SingleValue(EOFv))}; |
| 2158 | const auto ReturnsNonnegative = |
| 2159 | ConstraintSet{ReturnValueCondition(WithinRange, Range(0, IntMax))}; |
| 2160 | const auto ReturnsNonZero = |
| 2161 | ConstraintSet{ReturnValueCondition(OutOfRange, SingleValue(0))}; |
| 2162 | const auto ReturnsFileDescriptor = |
| 2163 | ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))}; |
| 2164 | const auto &ReturnsValidFileDescriptor = ReturnsNonnegative; |
| 2165 | |
| 2166 | auto ValidFileDescriptorOrAtFdcwd = [&](ArgNo ArgN) { |
| 2167 | return std::make_shared<RangeConstraint>( |
| 2168 | args&: ArgN, args: WithinRange, args: Range({AT_FDCWDv, AT_FDCWDv}, {0, IntMax}), |
| 2169 | args: "a valid file descriptor or AT_FDCWD" ); |
| 2170 | }; |
| 2171 | |
| 2172 | // FILE *fopen(const char *restrict pathname, const char *restrict mode); |
| 2173 | addToFunctionSummaryMap( |
| 2174 | "fopen" , |
| 2175 | Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy}, |
| 2176 | RetType{FilePtrTy}), |
| 2177 | Summary(NoEvalCall) |
| 2178 | .Case(CS: {NotNull(Ret)}, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2179 | .Case(CS: {IsNull(Ret)}, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2180 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2181 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2182 | |
| 2183 | // FILE *fdopen(int fd, const char *mode); |
| 2184 | addToFunctionSummaryMap( |
| 2185 | "fdopen" , |
| 2186 | Signature(ArgTypes{IntTy, ConstCharPtrTy}, RetType{FilePtrTy}), |
| 2187 | Summary(NoEvalCall) |
| 2188 | .Case(CS: {NotNull(Ret)}, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2189 | .Case(CS: {IsNull(Ret)}, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2190 | .ArgConstraint(VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 2191 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2192 | |
| 2193 | // FILE *tmpfile(void); |
| 2194 | addToFunctionSummaryMap( |
| 2195 | "tmpfile" , Signature(ArgTypes{}, RetType{FilePtrTy}), |
| 2196 | Summary(NoEvalCall) |
| 2197 | .Case(CS: {NotNull(Ret)}, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2198 | .Case(CS: {IsNull(Ret)}, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg)); |
| 2199 | |
| 2200 | // FILE *freopen(const char *restrict pathname, const char *restrict mode, |
| 2201 | // FILE *restrict stream); |
| 2202 | addToFunctionSummaryMap( |
| 2203 | "freopen" , |
| 2204 | Signature(ArgTypes{ConstCharPtrRestrictTy, ConstCharPtrRestrictTy, |
| 2205 | FilePtrRestrictTy}, |
| 2206 | RetType{FilePtrTy}), |
| 2207 | Summary(NoEvalCall) |
| 2208 | .Case(CS: {ReturnValueCondition(BO_EQ, ArgNo(2))}, |
| 2209 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2210 | .Case(CS: {IsNull(Ret)}, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2211 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 2212 | .ArgConstraint(VC: NotNull(ArgNo(2)))); |
| 2213 | |
| 2214 | // FILE *popen(const char *command, const char *type); |
| 2215 | addToFunctionSummaryMap( |
| 2216 | "popen" , |
| 2217 | Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{FilePtrTy}), |
| 2218 | Summary(NoEvalCall) |
| 2219 | .Case(CS: {NotNull(Ret)}, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2220 | .Case(CS: {IsNull(Ret)}, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2221 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2222 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2223 | |
| 2224 | // int fclose(FILE *stream); |
| 2225 | addToFunctionSummaryMap( |
| 2226 | "fclose" , Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), |
| 2227 | Summary(NoEvalCall) |
| 2228 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2229 | .Case(CS: ReturnsEOF, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2230 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2231 | |
| 2232 | // int pclose(FILE *stream); |
| 2233 | addToFunctionSummaryMap( |
| 2234 | "pclose" , Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), |
| 2235 | Summary(NoEvalCall) |
| 2236 | .Case(CS: {ReturnValueCondition(WithinRange, {{0, IntMax}})}, |
| 2237 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2238 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2239 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2240 | |
| 2241 | std::optional<QualType> Off_tTy = lookupTy("off_t" ); |
| 2242 | std::optional<RangeInt> Off_tMax = getMaxValue(Off_tTy); |
| 2243 | |
| 2244 | // int fgetc(FILE *stream); |
| 2245 | // 'getc' is the same as 'fgetc' but may be a macro |
| 2246 | addToFunctionSummaryMap( |
| 2247 | {"getc" , "fgetc" }, Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), |
| 2248 | Summary(NoEvalCall) |
| 2249 | .Case(CS: {ReturnValueCondition(WithinRange, {{0, UCharRangeMax}})}, |
| 2250 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2251 | .Case(CS: {ReturnValueCondition(WithinRange, SingleValue(EOFv))}, |
| 2252 | ErrnoC: ErrnoIrrelevant, Note: GenericFailureMsg) |
| 2253 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2254 | |
| 2255 | // int fputc(int c, FILE *stream); |
| 2256 | // 'putc' is the same as 'fputc' but may be a macro |
| 2257 | addToFunctionSummaryMap( |
| 2258 | {"putc" , "fputc" }, |
| 2259 | Signature(ArgTypes{IntTy, FilePtrTy}, RetType{IntTy}), |
| 2260 | Summary(NoEvalCall) |
| 2261 | .Case(CS: {ArgumentCondition(0, WithinRange, Range(0, UCharRangeMax)), |
| 2262 | ReturnValueCondition(BO_EQ, ArgNo(0))}, |
| 2263 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2264 | .Case(CS: {ArgumentCondition(0, OutOfRange, Range(0, UCharRangeMax)), |
| 2265 | ReturnValueCondition(WithinRange, Range(0, UCharRangeMax))}, |
| 2266 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2267 | .Case(CS: {ReturnValueCondition(WithinRange, SingleValue(EOFv))}, |
| 2268 | ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2269 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2270 | |
| 2271 | // char *fgets(char *restrict s, int n, FILE *restrict stream); |
| 2272 | addToFunctionSummaryMap( |
| 2273 | "fgets" , |
| 2274 | Signature(ArgTypes{CharPtrRestrictTy, IntTy, FilePtrRestrictTy}, |
| 2275 | RetType{CharPtrTy}), |
| 2276 | Summary(NoEvalCall) |
| 2277 | .Case(CS: {ReturnValueCondition(BO_EQ, ArgNo(0))}, |
| 2278 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2279 | .Case(CS: {IsNull(Ret)}, ErrnoC: ErrnoIrrelevant, Note: GenericFailureMsg) |
| 2280 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2281 | .ArgConstraint(VC: ArgumentCondition(1, WithinRange, Range(0, IntMax))) |
| 2282 | .ArgConstraint( |
| 2283 | VC: BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1))) |
| 2284 | .ArgConstraint(VC: NotNull(ArgNo(2)))); |
| 2285 | |
| 2286 | // int fputs(const char *restrict s, FILE *restrict stream); |
| 2287 | addToFunctionSummaryMap( |
| 2288 | "fputs" , |
| 2289 | Signature(ArgTypes{ConstCharPtrRestrictTy, FilePtrRestrictTy}, |
| 2290 | RetType{IntTy}), |
| 2291 | Summary(NoEvalCall) |
| 2292 | .Case(CS: ReturnsNonnegative, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2293 | .Case(CS: {ReturnValueCondition(WithinRange, SingleValue(EOFv))}, |
| 2294 | ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2295 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2296 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2297 | |
| 2298 | // int ungetc(int c, FILE *stream); |
| 2299 | addToFunctionSummaryMap( |
| 2300 | "ungetc" , Signature(ArgTypes{IntTy, FilePtrTy}, RetType{IntTy}), |
| 2301 | Summary(NoEvalCall) |
| 2302 | .Case(CS: {ReturnValueCondition(BO_EQ, ArgNo(0)), |
| 2303 | ArgumentCondition(0, WithinRange, {{0, UCharRangeMax}})}, |
| 2304 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2305 | .Case(CS: {ReturnValueCondition(WithinRange, SingleValue(EOFv)), |
| 2306 | ArgumentCondition(0, WithinRange, SingleValue(EOFv))}, |
| 2307 | ErrnoC: ErrnoNEZeroIrrelevant, |
| 2308 | Note: "Assuming that 'ungetc' fails because EOF was passed as " |
| 2309 | "character" ) |
| 2310 | .Case(CS: {ReturnValueCondition(WithinRange, SingleValue(EOFv)), |
| 2311 | ArgumentCondition(0, WithinRange, {{0, UCharRangeMax}})}, |
| 2312 | ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2313 | .ArgConstraint(VC: ArgumentCondition( |
| 2314 | 0, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}})) |
| 2315 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2316 | |
| 2317 | // int fseek(FILE *stream, long offset, int whence); |
| 2318 | // FIXME: It can be possible to get the 'SEEK_' values (like EOFv) and use |
| 2319 | // these for condition of arg 2. |
| 2320 | // Now the range [0,2] is used (the `SEEK_*` constants are usually 0,1,2). |
| 2321 | addToFunctionSummaryMap( |
| 2322 | "fseek" , Signature(ArgTypes{FilePtrTy, LongTy, IntTy}, RetType{IntTy}), |
| 2323 | Summary(NoEvalCall) |
| 2324 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2325 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2326 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2327 | .ArgConstraint(VC: ArgumentCondition(2, WithinRange, {{0, 2}}))); |
| 2328 | |
| 2329 | // int fseeko(FILE *stream, off_t offset, int whence); |
| 2330 | addToFunctionSummaryMap( |
| 2331 | "fseeko" , |
| 2332 | Signature(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}), |
| 2333 | Summary(NoEvalCall) |
| 2334 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2335 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2336 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2337 | .ArgConstraint(VC: ArgumentCondition(2, WithinRange, {{0, 2}}))); |
| 2338 | |
| 2339 | // int fgetpos(FILE *restrict stream, fpos_t *restrict pos); |
| 2340 | // From 'The Open Group Base Specifications Issue 7, 2018 edition': |
| 2341 | // "The fgetpos() function shall not change the setting of errno if |
| 2342 | // successful." |
| 2343 | addToFunctionSummaryMap( |
| 2344 | "fgetpos" , |
| 2345 | Signature(ArgTypes{FilePtrRestrictTy, FPosTPtrRestrictTy}, |
| 2346 | RetType{IntTy}), |
| 2347 | Summary(NoEvalCall) |
| 2348 | .Case(CS: ReturnsZero, ErrnoC: ErrnoUnchanged, Note: GenericSuccessMsg) |
| 2349 | .Case(CS: ReturnsNonZero, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2350 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2351 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2352 | |
| 2353 | // int fsetpos(FILE *stream, const fpos_t *pos); |
| 2354 | // From 'The Open Group Base Specifications Issue 7, 2018 edition': |
| 2355 | // "The fsetpos() function shall not change the setting of errno if |
| 2356 | // successful." |
| 2357 | addToFunctionSummaryMap( |
| 2358 | "fsetpos" , |
| 2359 | Signature(ArgTypes{FilePtrTy, ConstFPosTPtrTy}, RetType{IntTy}), |
| 2360 | Summary(NoEvalCall) |
| 2361 | .Case(CS: ReturnsZero, ErrnoC: ErrnoUnchanged, Note: GenericSuccessMsg) |
| 2362 | .Case(CS: ReturnsNonZero, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2363 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2364 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2365 | |
| 2366 | // int fflush(FILE *stream); |
| 2367 | addToFunctionSummaryMap( |
| 2368 | "fflush" , Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), |
| 2369 | Summary(NoEvalCall) |
| 2370 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2371 | .Case(CS: ReturnsEOF, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg)); |
| 2372 | |
| 2373 | // long ftell(FILE *stream); |
| 2374 | // From 'The Open Group Base Specifications Issue 7, 2018 edition': |
| 2375 | // "The ftell() function shall not change the setting of errno if |
| 2376 | // successful." |
| 2377 | addToFunctionSummaryMap( |
| 2378 | "ftell" , Signature(ArgTypes{FilePtrTy}, RetType{LongTy}), |
| 2379 | Summary(NoEvalCall) |
| 2380 | .Case(CS: {ReturnValueCondition(WithinRange, Range(0, LongMax))}, |
| 2381 | ErrnoC: ErrnoUnchanged, Note: GenericSuccessMsg) |
| 2382 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2383 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2384 | |
| 2385 | // off_t ftello(FILE *stream); |
| 2386 | addToFunctionSummaryMap( |
| 2387 | "ftello" , Signature(ArgTypes{FilePtrTy}, RetType{Off_tTy}), |
| 2388 | Summary(NoEvalCall) |
| 2389 | .Case(CS: {ReturnValueCondition(WithinRange, Range(0, Off_tMax))}, |
| 2390 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2391 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2392 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2393 | |
| 2394 | // int fileno(FILE *stream); |
| 2395 | // According to POSIX 'fileno' may fail and set 'errno'. |
| 2396 | // But in Linux it may fail only if the specified file pointer is invalid. |
| 2397 | // At many places 'fileno' is used without check for failure and a failure |
| 2398 | // case here would produce a large amount of likely false positive warnings. |
| 2399 | // To avoid this, we assume here that it does not fail. |
| 2400 | addToFunctionSummaryMap( |
| 2401 | "fileno" , Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), |
| 2402 | Summary(NoEvalCall) |
| 2403 | .Case(CS: ReturnsValidFileDescriptor, ErrnoC: ErrnoUnchanged, Note: GenericSuccessMsg) |
| 2404 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2405 | |
| 2406 | // void rewind(FILE *stream); |
| 2407 | // This function indicates error only by setting of 'errno'. |
| 2408 | addToFunctionSummaryMap("rewind" , |
| 2409 | Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}), |
| 2410 | Summary(NoEvalCall) |
| 2411 | .Case(CS: {}, ErrnoC: ErrnoMustBeChecked) |
| 2412 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2413 | |
| 2414 | // void clearerr(FILE *stream); |
| 2415 | addToFunctionSummaryMap( |
| 2416 | "clearerr" , Signature(ArgTypes{FilePtrTy}, RetType{VoidTy}), |
| 2417 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2418 | |
| 2419 | // int feof(FILE *stream); |
| 2420 | addToFunctionSummaryMap( |
| 2421 | "feof" , Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), |
| 2422 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2423 | |
| 2424 | // int ferror(FILE *stream); |
| 2425 | addToFunctionSummaryMap( |
| 2426 | "ferror" , Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), |
| 2427 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2428 | |
| 2429 | // long a64l(const char *str64); |
| 2430 | addToFunctionSummaryMap( |
| 2431 | "a64l" , Signature(ArgTypes{ConstCharPtrTy}, RetType{LongTy}), |
| 2432 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2433 | |
| 2434 | // char *l64a(long value); |
| 2435 | addToFunctionSummaryMap("l64a" , |
| 2436 | Signature(ArgTypes{LongTy}, RetType{CharPtrTy}), |
| 2437 | Summary(NoEvalCall) |
| 2438 | .ArgConstraint(VC: ArgumentCondition( |
| 2439 | 0, WithinRange, Range(0, LongMax)))); |
| 2440 | |
| 2441 | // int open(const char *path, int oflag, ...); |
| 2442 | addToFunctionSummaryMap( |
| 2443 | "open" , Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}), |
| 2444 | Summary(NoEvalCall) |
| 2445 | .Case(CS: ReturnsValidFileDescriptor, ErrnoC: ErrnoMustNotBeChecked, |
| 2446 | Note: GenericSuccessMsg) |
| 2447 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2448 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2449 | |
| 2450 | // int openat(int fd, const char *path, int oflag, ...); |
| 2451 | addToFunctionSummaryMap( |
| 2452 | "openat" , |
| 2453 | Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}), |
| 2454 | Summary(NoEvalCall) |
| 2455 | .Case(CS: ReturnsValidFileDescriptor, ErrnoC: ErrnoMustNotBeChecked, |
| 2456 | Note: GenericSuccessMsg) |
| 2457 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2458 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(0))) |
| 2459 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2460 | |
| 2461 | // int access(const char *pathname, int amode); |
| 2462 | addToFunctionSummaryMap( |
| 2463 | "access" , Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}), |
| 2464 | Summary(NoEvalCall) |
| 2465 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2466 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2467 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2468 | |
| 2469 | // int faccessat(int dirfd, const char *pathname, int mode, int flags); |
| 2470 | addToFunctionSummaryMap( |
| 2471 | "faccessat" , |
| 2472 | Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy}, |
| 2473 | RetType{IntTy}), |
| 2474 | Summary(NoEvalCall) |
| 2475 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2476 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2477 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(0))) |
| 2478 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2479 | |
| 2480 | // int dup(int fildes); |
| 2481 | addToFunctionSummaryMap( |
| 2482 | "dup" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 2483 | Summary(NoEvalCall) |
| 2484 | .Case(CS: ReturnsValidFileDescriptor, ErrnoC: ErrnoMustNotBeChecked, |
| 2485 | Note: GenericSuccessMsg) |
| 2486 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2487 | .ArgConstraint( |
| 2488 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 2489 | |
| 2490 | // int dup2(int fildes1, int filedes2); |
| 2491 | addToFunctionSummaryMap( |
| 2492 | "dup2" , Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), |
| 2493 | Summary(NoEvalCall) |
| 2494 | .Case(CS: ReturnsValidFileDescriptor, ErrnoC: ErrnoMustNotBeChecked, |
| 2495 | Note: GenericSuccessMsg) |
| 2496 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2497 | .ArgConstraint(VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 2498 | .ArgConstraint( |
| 2499 | VC: ArgumentCondition(1, WithinRange, Range(0, IntMax)))); |
| 2500 | |
| 2501 | // int fdatasync(int fildes); |
| 2502 | addToFunctionSummaryMap( |
| 2503 | "fdatasync" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 2504 | Summary(NoEvalCall) |
| 2505 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2506 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2507 | .ArgConstraint( |
| 2508 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 2509 | |
| 2510 | // int fnmatch(const char *pattern, const char *string, int flags); |
| 2511 | addToFunctionSummaryMap( |
| 2512 | "fnmatch" , |
| 2513 | Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy}, |
| 2514 | RetType{IntTy}), |
| 2515 | Summary(NoEvalCall) |
| 2516 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2517 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2518 | |
| 2519 | // int fsync(int fildes); |
| 2520 | addToFunctionSummaryMap( |
| 2521 | "fsync" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 2522 | Summary(NoEvalCall) |
| 2523 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2524 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2525 | .ArgConstraint( |
| 2526 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 2527 | |
| 2528 | // int truncate(const char *path, off_t length); |
| 2529 | addToFunctionSummaryMap( |
| 2530 | "truncate" , |
| 2531 | Signature(ArgTypes{ConstCharPtrTy, Off_tTy}, RetType{IntTy}), |
| 2532 | Summary(NoEvalCall) |
| 2533 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2534 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2535 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2536 | |
| 2537 | // int symlink(const char *oldpath, const char *newpath); |
| 2538 | addToFunctionSummaryMap( |
| 2539 | "symlink" , |
| 2540 | Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}), |
| 2541 | Summary(NoEvalCall) |
| 2542 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2543 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2544 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2545 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2546 | |
| 2547 | // int symlinkat(const char *oldpath, int newdirfd, const char *newpath); |
| 2548 | addToFunctionSummaryMap( |
| 2549 | "symlinkat" , |
| 2550 | Signature(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy}, |
| 2551 | RetType{IntTy}), |
| 2552 | Summary(NoEvalCall) |
| 2553 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2554 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2555 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2556 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(1))) |
| 2557 | .ArgConstraint(VC: NotNull(ArgNo(2)))); |
| 2558 | |
| 2559 | // int lockf(int fd, int cmd, off_t len); |
| 2560 | addToFunctionSummaryMap( |
| 2561 | "lockf" , Signature(ArgTypes{IntTy, IntTy, Off_tTy}, RetType{IntTy}), |
| 2562 | Summary(NoEvalCall) |
| 2563 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2564 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2565 | .ArgConstraint( |
| 2566 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 2567 | |
| 2568 | std::optional<QualType> Mode_tTy = lookupTy("mode_t" ); |
| 2569 | |
| 2570 | // int creat(const char *pathname, mode_t mode); |
| 2571 | addToFunctionSummaryMap( |
| 2572 | "creat" , Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), |
| 2573 | Summary(NoEvalCall) |
| 2574 | .Case(CS: ReturnsValidFileDescriptor, ErrnoC: ErrnoMustNotBeChecked, |
| 2575 | Note: GenericSuccessMsg) |
| 2576 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2577 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2578 | |
| 2579 | // unsigned int sleep(unsigned int seconds); |
| 2580 | addToFunctionSummaryMap( |
| 2581 | "sleep" , Signature(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}), |
| 2582 | Summary(NoEvalCall) |
| 2583 | .ArgConstraint( |
| 2584 | VC: ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); |
| 2585 | |
| 2586 | std::optional<QualType> DirTy = lookupTy("DIR" ); |
| 2587 | std::optional<QualType> DirPtrTy = getPointerTy(DirTy); |
| 2588 | |
| 2589 | // int dirfd(DIR *dirp); |
| 2590 | addToFunctionSummaryMap( |
| 2591 | "dirfd" , Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), |
| 2592 | Summary(NoEvalCall) |
| 2593 | .Case(CS: ReturnsValidFileDescriptor, ErrnoC: ErrnoMustNotBeChecked, |
| 2594 | Note: GenericSuccessMsg) |
| 2595 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2596 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2597 | |
| 2598 | // unsigned int alarm(unsigned int seconds); |
| 2599 | addToFunctionSummaryMap( |
| 2600 | "alarm" , Signature(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}), |
| 2601 | Summary(NoEvalCall) |
| 2602 | .ArgConstraint( |
| 2603 | VC: ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); |
| 2604 | |
| 2605 | // int closedir(DIR *dir); |
| 2606 | addToFunctionSummaryMap( |
| 2607 | "closedir" , Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), |
| 2608 | Summary(NoEvalCall) |
| 2609 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2610 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2611 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2612 | |
| 2613 | // char *strdup(const char *s); |
| 2614 | addToFunctionSummaryMap( |
| 2615 | "strdup" , Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}), |
| 2616 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2617 | |
| 2618 | // char *strndup(const char *s, size_t n); |
| 2619 | addToFunctionSummaryMap( |
| 2620 | "strndup" , |
| 2621 | Signature(ArgTypes{ConstCharPtrTy, SizeTy}, RetType{CharPtrTy}), |
| 2622 | Summary(NoEvalCall) |
| 2623 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2624 | .ArgConstraint( |
| 2625 | VC: ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); |
| 2626 | |
| 2627 | // wchar_t *wcsdup(const wchar_t *s); |
| 2628 | addToFunctionSummaryMap( |
| 2629 | "wcsdup" , Signature(ArgTypes{ConstWchar_tPtrTy}, RetType{Wchar_tPtrTy}), |
| 2630 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2631 | |
| 2632 | // int mkstemp(char *template); |
| 2633 | addToFunctionSummaryMap( |
| 2634 | "mkstemp" , Signature(ArgTypes{CharPtrTy}, RetType{IntTy}), |
| 2635 | Summary(NoEvalCall) |
| 2636 | .Case(CS: ReturnsValidFileDescriptor, ErrnoC: ErrnoMustNotBeChecked, |
| 2637 | Note: GenericSuccessMsg) |
| 2638 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2639 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2640 | |
| 2641 | // char *mkdtemp(char *template); |
| 2642 | addToFunctionSummaryMap( |
| 2643 | "mkdtemp" , Signature(ArgTypes{CharPtrTy}, RetType{CharPtrTy}), |
| 2644 | Summary(NoEvalCall) |
| 2645 | .Case(CS: {ReturnValueCondition(BO_EQ, ArgNo(0))}, |
| 2646 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2647 | .Case(CS: {IsNull(Ret)}, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2648 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2649 | |
| 2650 | // char *getcwd(char *buf, size_t size); |
| 2651 | addToFunctionSummaryMap( |
| 2652 | "getcwd" , Signature(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}), |
| 2653 | Summary(NoEvalCall) |
| 2654 | .Case(CS: {NotNull(0), |
| 2655 | ArgumentCondition(1, WithinRange, Range(1, SizeMax)), |
| 2656 | ReturnValueCondition(BO_EQ, ArgNo(0))}, |
| 2657 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2658 | .Case(CS: {NotNull(0), |
| 2659 | ArgumentCondition(1, WithinRange, SingleValue(0)), |
| 2660 | IsNull(Ret)}, |
| 2661 | ErrnoC: ErrnoNEZeroIrrelevant, Note: "Assuming that argument 'size' is 0" ) |
| 2662 | .Case(CS: {NotNull(0), |
| 2663 | ArgumentCondition(1, WithinRange, Range(1, SizeMax)), |
| 2664 | IsNull(Ret)}, |
| 2665 | ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2666 | .Case(CS: {IsNull(0), NotNull(Ret)}, ErrnoC: ErrnoMustNotBeChecked, |
| 2667 | Note: GenericSuccessMsg) |
| 2668 | .Case(CS: {IsNull(0), IsNull(Ret)}, ErrnoC: ErrnoNEZeroIrrelevant, |
| 2669 | Note: GenericFailureMsg) |
| 2670 | .ArgConstraint( |
| 2671 | VC: BufferSize(/*Buffer*/ ArgNo(0), /*BufSize*/ ArgNo(1))) |
| 2672 | .ArgConstraint( |
| 2673 | VC: ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); |
| 2674 | |
| 2675 | // int mkdir(const char *pathname, mode_t mode); |
| 2676 | addToFunctionSummaryMap( |
| 2677 | "mkdir" , Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), |
| 2678 | Summary(NoEvalCall) |
| 2679 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2680 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2681 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2682 | |
| 2683 | // int mkdirat(int dirfd, const char *pathname, mode_t mode); |
| 2684 | addToFunctionSummaryMap( |
| 2685 | "mkdirat" , |
| 2686 | Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), |
| 2687 | Summary(NoEvalCall) |
| 2688 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2689 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2690 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(0))) |
| 2691 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2692 | |
| 2693 | std::optional<QualType> Dev_tTy = lookupTy("dev_t" ); |
| 2694 | |
| 2695 | // int mknod(const char *pathname, mode_t mode, dev_t dev); |
| 2696 | addToFunctionSummaryMap( |
| 2697 | "mknod" , |
| 2698 | Signature(ArgTypes{ConstCharPtrTy, Mode_tTy, Dev_tTy}, RetType{IntTy}), |
| 2699 | Summary(NoEvalCall) |
| 2700 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2701 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2702 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2703 | |
| 2704 | // int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev); |
| 2705 | addToFunctionSummaryMap( |
| 2706 | "mknodat" , |
| 2707 | Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, Dev_tTy}, |
| 2708 | RetType{IntTy}), |
| 2709 | Summary(NoEvalCall) |
| 2710 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2711 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2712 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(0))) |
| 2713 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2714 | |
| 2715 | // int chmod(const char *path, mode_t mode); |
| 2716 | addToFunctionSummaryMap( |
| 2717 | "chmod" , Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), |
| 2718 | Summary(NoEvalCall) |
| 2719 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2720 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2721 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2722 | |
| 2723 | // int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags); |
| 2724 | addToFunctionSummaryMap( |
| 2725 | "fchmodat" , |
| 2726 | Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, IntTy}, |
| 2727 | RetType{IntTy}), |
| 2728 | Summary(NoEvalCall) |
| 2729 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2730 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2731 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(0))) |
| 2732 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2733 | |
| 2734 | // int fchmod(int fildes, mode_t mode); |
| 2735 | addToFunctionSummaryMap( |
| 2736 | "fchmod" , Signature(ArgTypes{IntTy, Mode_tTy}, RetType{IntTy}), |
| 2737 | Summary(NoEvalCall) |
| 2738 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2739 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2740 | .ArgConstraint( |
| 2741 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 2742 | |
| 2743 | std::optional<QualType> Uid_tTy = lookupTy("uid_t" ); |
| 2744 | std::optional<QualType> Gid_tTy = lookupTy("gid_t" ); |
| 2745 | |
| 2746 | // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, |
| 2747 | // int flags); |
| 2748 | addToFunctionSummaryMap( |
| 2749 | "fchownat" , |
| 2750 | Signature(ArgTypes{IntTy, ConstCharPtrTy, Uid_tTy, Gid_tTy, IntTy}, |
| 2751 | RetType{IntTy}), |
| 2752 | Summary(NoEvalCall) |
| 2753 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2754 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2755 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(0))) |
| 2756 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2757 | |
| 2758 | // int chown(const char *path, uid_t owner, gid_t group); |
| 2759 | addToFunctionSummaryMap( |
| 2760 | "chown" , |
| 2761 | Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), |
| 2762 | Summary(NoEvalCall) |
| 2763 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2764 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2765 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2766 | |
| 2767 | // int lchown(const char *path, uid_t owner, gid_t group); |
| 2768 | addToFunctionSummaryMap( |
| 2769 | "lchown" , |
| 2770 | Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), |
| 2771 | Summary(NoEvalCall) |
| 2772 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2773 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2774 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2775 | |
| 2776 | // int fchown(int fildes, uid_t owner, gid_t group); |
| 2777 | addToFunctionSummaryMap( |
| 2778 | "fchown" , Signature(ArgTypes{IntTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), |
| 2779 | Summary(NoEvalCall) |
| 2780 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2781 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2782 | .ArgConstraint( |
| 2783 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 2784 | |
| 2785 | // int rmdir(const char *pathname); |
| 2786 | addToFunctionSummaryMap( |
| 2787 | "rmdir" , Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), |
| 2788 | Summary(NoEvalCall) |
| 2789 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2790 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2791 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2792 | |
| 2793 | // int chdir(const char *path); |
| 2794 | addToFunctionSummaryMap( |
| 2795 | "chdir" , Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), |
| 2796 | Summary(NoEvalCall) |
| 2797 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2798 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2799 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2800 | |
| 2801 | // int link(const char *oldpath, const char *newpath); |
| 2802 | addToFunctionSummaryMap( |
| 2803 | "link" , |
| 2804 | Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}), |
| 2805 | Summary(NoEvalCall) |
| 2806 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2807 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2808 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2809 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2810 | |
| 2811 | // int linkat(int fd1, const char *path1, int fd2, const char *path2, |
| 2812 | // int flag); |
| 2813 | addToFunctionSummaryMap( |
| 2814 | "linkat" , |
| 2815 | Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy}, |
| 2816 | RetType{IntTy}), |
| 2817 | Summary(NoEvalCall) |
| 2818 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2819 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2820 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(0))) |
| 2821 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 2822 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(2))) |
| 2823 | .ArgConstraint(VC: NotNull(ArgNo(3)))); |
| 2824 | |
| 2825 | // int unlink(const char *pathname); |
| 2826 | addToFunctionSummaryMap( |
| 2827 | "unlink" , Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), |
| 2828 | Summary(NoEvalCall) |
| 2829 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2830 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2831 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2832 | |
| 2833 | // int unlinkat(int fd, const char *path, int flag); |
| 2834 | addToFunctionSummaryMap( |
| 2835 | "unlinkat" , |
| 2836 | Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}), |
| 2837 | Summary(NoEvalCall) |
| 2838 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2839 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2840 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(0))) |
| 2841 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2842 | |
| 2843 | std::optional<QualType> StructStatTy = lookupTy("stat" ); |
| 2844 | std::optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy); |
| 2845 | std::optional<QualType> StructStatPtrRestrictTy = |
| 2846 | getRestrictTy(StructStatPtrTy); |
| 2847 | |
| 2848 | // int fstat(int fd, struct stat *statbuf); |
| 2849 | addToFunctionSummaryMap( |
| 2850 | "fstat" , Signature(ArgTypes{IntTy, StructStatPtrTy}, RetType{IntTy}), |
| 2851 | Summary(NoEvalCall) |
| 2852 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2853 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2854 | .ArgConstraint(VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 2855 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2856 | |
| 2857 | // int stat(const char *restrict path, struct stat *restrict buf); |
| 2858 | addToFunctionSummaryMap( |
| 2859 | "stat" , |
| 2860 | Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy}, |
| 2861 | RetType{IntTy}), |
| 2862 | Summary(NoEvalCall) |
| 2863 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2864 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2865 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2866 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2867 | |
| 2868 | // int lstat(const char *restrict path, struct stat *restrict buf); |
| 2869 | addToFunctionSummaryMap( |
| 2870 | "lstat" , |
| 2871 | Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy}, |
| 2872 | RetType{IntTy}), |
| 2873 | Summary(NoEvalCall) |
| 2874 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2875 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2876 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 2877 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 2878 | |
| 2879 | // int fstatat(int fd, const char *restrict path, |
| 2880 | // struct stat *restrict buf, int flag); |
| 2881 | addToFunctionSummaryMap( |
| 2882 | "fstatat" , |
| 2883 | Signature(ArgTypes{IntTy, ConstCharPtrRestrictTy, |
| 2884 | StructStatPtrRestrictTy, IntTy}, |
| 2885 | RetType{IntTy}), |
| 2886 | Summary(NoEvalCall) |
| 2887 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2888 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2889 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(0))) |
| 2890 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 2891 | .ArgConstraint(VC: NotNull(ArgNo(2)))); |
| 2892 | |
| 2893 | // DIR *opendir(const char *name); |
| 2894 | addToFunctionSummaryMap( |
| 2895 | "opendir" , Signature(ArgTypes{ConstCharPtrTy}, RetType{DirPtrTy}), |
| 2896 | Summary(NoEvalCall) |
| 2897 | .Case(CS: {NotNull(Ret)}, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2898 | .Case(CS: {IsNull(Ret)}, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2899 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2900 | |
| 2901 | // DIR *fdopendir(int fd); |
| 2902 | addToFunctionSummaryMap( |
| 2903 | "fdopendir" , Signature(ArgTypes{IntTy}, RetType{DirPtrTy}), |
| 2904 | Summary(NoEvalCall) |
| 2905 | .Case(CS: {NotNull(Ret)}, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2906 | .Case(CS: {IsNull(Ret)}, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2907 | .ArgConstraint( |
| 2908 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 2909 | |
| 2910 | // int isatty(int fildes); |
| 2911 | addToFunctionSummaryMap( |
| 2912 | "isatty" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 2913 | Summary(NoEvalCall) |
| 2914 | .Case(CS: {ReturnValueCondition(WithinRange, Range(0, 1))}, |
| 2915 | ErrnoC: ErrnoIrrelevant) |
| 2916 | .ArgConstraint( |
| 2917 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 2918 | |
| 2919 | // int close(int fildes); |
| 2920 | addToFunctionSummaryMap( |
| 2921 | "close" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 2922 | Summary(NoEvalCall) |
| 2923 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2924 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2925 | .ArgConstraint( |
| 2926 | VC: ArgumentCondition(0, WithinRange, Range(-1, IntMax)))); |
| 2927 | |
| 2928 | // long fpathconf(int fildes, int name); |
| 2929 | addToFunctionSummaryMap("fpathconf" , |
| 2930 | Signature(ArgTypes{IntTy, IntTy}, RetType{LongTy}), |
| 2931 | Summary(NoEvalCall) |
| 2932 | .ArgConstraint(VC: ArgumentCondition( |
| 2933 | 0, WithinRange, Range(0, IntMax)))); |
| 2934 | |
| 2935 | // long pathconf(const char *path, int name); |
| 2936 | addToFunctionSummaryMap( |
| 2937 | "pathconf" , Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{LongTy}), |
| 2938 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2939 | |
| 2940 | // void rewinddir(DIR *dir); |
| 2941 | addToFunctionSummaryMap( |
| 2942 | "rewinddir" , Signature(ArgTypes{DirPtrTy}, RetType{VoidTy}), |
| 2943 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2944 | |
| 2945 | // void seekdir(DIR *dirp, long loc); |
| 2946 | addToFunctionSummaryMap( |
| 2947 | "seekdir" , Signature(ArgTypes{DirPtrTy, LongTy}, RetType{VoidTy}), |
| 2948 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2949 | |
| 2950 | // int rand_r(unsigned int *seedp); |
| 2951 | addToFunctionSummaryMap( |
| 2952 | "rand_r" , Signature(ArgTypes{UnsignedIntPtrTy}, RetType{IntTy}), |
| 2953 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2954 | |
| 2955 | // void *mmap(void *addr, size_t length, int prot, int flags, int fd, |
| 2956 | // off_t offset); |
| 2957 | // FIXME: Improve for errno modeling. |
| 2958 | addToFunctionSummaryMap( |
| 2959 | "mmap" , |
| 2960 | Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off_tTy}, |
| 2961 | RetType{VoidPtrTy}), |
| 2962 | Summary(NoEvalCall) |
| 2963 | .ArgConstraint(VC: ArgumentCondition(1, WithinRange, Range(1, SizeMax))) |
| 2964 | .ArgConstraint( |
| 2965 | VC: ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); |
| 2966 | |
| 2967 | std::optional<QualType> Off64_tTy = lookupTy("off64_t" ); |
| 2968 | // void *mmap64(void *addr, size_t length, int prot, int flags, int fd, |
| 2969 | // off64_t offset); |
| 2970 | // FIXME: Improve for errno modeling. |
| 2971 | addToFunctionSummaryMap( |
| 2972 | "mmap64" , |
| 2973 | Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off64_tTy}, |
| 2974 | RetType{VoidPtrTy}), |
| 2975 | Summary(NoEvalCall) |
| 2976 | .ArgConstraint(VC: ArgumentCondition(1, WithinRange, Range(1, SizeMax))) |
| 2977 | .ArgConstraint( |
| 2978 | VC: ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); |
| 2979 | |
| 2980 | // int pipe(int fildes[2]); |
| 2981 | addToFunctionSummaryMap( |
| 2982 | "pipe" , Signature(ArgTypes{IntPtrTy}, RetType{IntTy}), |
| 2983 | Summary(NoEvalCall) |
| 2984 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 2985 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2986 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 2987 | |
| 2988 | // off_t lseek(int fildes, off_t offset, int whence); |
| 2989 | // In the first case we can not tell for sure if it failed or not. |
| 2990 | // A return value different from of the expected offset (that is unknown |
| 2991 | // here) may indicate failure. For this reason we do not enforce the errno |
| 2992 | // check (can cause false positive). |
| 2993 | addToFunctionSummaryMap( |
| 2994 | "lseek" , Signature(ArgTypes{IntTy, Off_tTy, IntTy}, RetType{Off_tTy}), |
| 2995 | Summary(NoEvalCall) |
| 2996 | .Case(CS: ReturnsNonnegative, ErrnoC: ErrnoIrrelevant) |
| 2997 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 2998 | .ArgConstraint( |
| 2999 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 3000 | |
| 3001 | // ssize_t readlink(const char *restrict path, char *restrict buf, |
| 3002 | // size_t bufsize); |
| 3003 | addToFunctionSummaryMap( |
| 3004 | "readlink" , |
| 3005 | Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, |
| 3006 | RetType{Ssize_tTy}), |
| 3007 | Summary(NoEvalCall) |
| 3008 | .Case(CS: {ArgumentCondition(2, WithinRange, Range(1, IntMax)), |
| 3009 | ReturnValueCondition(LessThanOrEq, ArgNo(2)), |
| 3010 | ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, |
| 3011 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3012 | .Case(CS: {ArgumentCondition(2, WithinRange, SingleValue(0)), |
| 3013 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 3014 | ErrnoC: ErrnoMustNotBeChecked, |
| 3015 | Note: "Assuming that argument 'bufsize' is 0" ) |
| 3016 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3017 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 3018 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 3019 | .ArgConstraint(VC: BufferSize(/*Buffer=*/ArgNo(1), |
| 3020 | /*BufSize=*/ArgNo(2))) |
| 3021 | .ArgConstraint( |
| 3022 | VC: ArgumentCondition(2, WithinRange, Range(0, SizeMax)))); |
| 3023 | |
| 3024 | // ssize_t readlinkat(int fd, const char *restrict path, |
| 3025 | // char *restrict buf, size_t bufsize); |
| 3026 | addToFunctionSummaryMap( |
| 3027 | "readlinkat" , |
| 3028 | Signature( |
| 3029 | ArgTypes{IntTy, ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, |
| 3030 | RetType{Ssize_tTy}), |
| 3031 | Summary(NoEvalCall) |
| 3032 | .Case(CS: {ArgumentCondition(3, WithinRange, Range(1, IntMax)), |
| 3033 | ReturnValueCondition(LessThanOrEq, ArgNo(3)), |
| 3034 | ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, |
| 3035 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3036 | .Case(CS: {ArgumentCondition(3, WithinRange, SingleValue(0)), |
| 3037 | ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 3038 | ErrnoC: ErrnoMustNotBeChecked, |
| 3039 | Note: "Assuming that argument 'bufsize' is 0" ) |
| 3040 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3041 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(0))) |
| 3042 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 3043 | .ArgConstraint(VC: NotNull(ArgNo(2))) |
| 3044 | .ArgConstraint(VC: BufferSize(/*Buffer=*/ArgNo(2), |
| 3045 | /*BufSize=*/ArgNo(3))) |
| 3046 | .ArgConstraint( |
| 3047 | VC: ArgumentCondition(3, WithinRange, Range(0, SizeMax)))); |
| 3048 | |
| 3049 | // int renameat(int olddirfd, const char *oldpath, int newdirfd, const char |
| 3050 | // *newpath); |
| 3051 | addToFunctionSummaryMap( |
| 3052 | "renameat" , |
| 3053 | Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy}, |
| 3054 | RetType{IntTy}), |
| 3055 | Summary(NoEvalCall) |
| 3056 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3057 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3058 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(0))) |
| 3059 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 3060 | .ArgConstraint(VC: ValidFileDescriptorOrAtFdcwd(ArgNo(2))) |
| 3061 | .ArgConstraint(VC: NotNull(ArgNo(3)))); |
| 3062 | |
| 3063 | // char *realpath(const char *restrict file_name, |
| 3064 | // char *restrict resolved_name); |
| 3065 | // FIXME: If the argument 'resolved_name' is not NULL, macro 'PATH_MAX' |
| 3066 | // should be defined in "limits.h" to guarrantee a success. |
| 3067 | addToFunctionSummaryMap( |
| 3068 | "realpath" , |
| 3069 | Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy}, |
| 3070 | RetType{CharPtrTy}), |
| 3071 | Summary(NoEvalCall) |
| 3072 | .Case(CS: {NotNull(Ret)}, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3073 | .Case(CS: {IsNull(Ret)}, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3074 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3075 | |
| 3076 | QualType CharPtrConstPtr = getPointerTy(getConstTy(CharPtrTy)); |
| 3077 | |
| 3078 | // int execv(const char *path, char *const argv[]); |
| 3079 | addToFunctionSummaryMap( |
| 3080 | "execv" , |
| 3081 | Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}), |
| 3082 | Summary(NoEvalCall) |
| 3083 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant) |
| 3084 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3085 | |
| 3086 | // int execvp(const char *file, char *const argv[]); |
| 3087 | addToFunctionSummaryMap( |
| 3088 | "execvp" , |
| 3089 | Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}), |
| 3090 | Summary(NoEvalCall) |
| 3091 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant) |
| 3092 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3093 | |
| 3094 | // int getopt(int argc, char * const argv[], const char *optstring); |
| 3095 | addToFunctionSummaryMap( |
| 3096 | "getopt" , |
| 3097 | Signature(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy}, |
| 3098 | RetType{IntTy}), |
| 3099 | Summary(NoEvalCall) |
| 3100 | .Case(CS: {ReturnValueCondition(WithinRange, Range(-1, UCharRangeMax))}, |
| 3101 | ErrnoC: ErrnoIrrelevant) |
| 3102 | .ArgConstraint(VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 3103 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 3104 | .ArgConstraint(VC: NotNull(ArgNo(2)))); |
| 3105 | |
| 3106 | std::optional<QualType> StructSockaddrTy = lookupTy("sockaddr" ); |
| 3107 | std::optional<QualType> StructSockaddrPtrTy = |
| 3108 | getPointerTy(StructSockaddrTy); |
| 3109 | std::optional<QualType> ConstStructSockaddrPtrTy = |
| 3110 | getPointerTy(getConstTy(StructSockaddrTy)); |
| 3111 | std::optional<QualType> StructSockaddrPtrRestrictTy = |
| 3112 | getRestrictTy(StructSockaddrPtrTy); |
| 3113 | std::optional<QualType> ConstStructSockaddrPtrRestrictTy = |
| 3114 | getRestrictTy(ConstStructSockaddrPtrTy); |
| 3115 | std::optional<QualType> Socklen_tTy = lookupTy("socklen_t" ); |
| 3116 | std::optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy); |
| 3117 | std::optional<QualType> Socklen_tPtrRestrictTy = |
| 3118 | getRestrictTy(Socklen_tPtrTy); |
| 3119 | std::optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy); |
| 3120 | |
| 3121 | // In 'socket.h' of some libc implementations with C99, sockaddr parameter |
| 3122 | // is a transparent union of the underlying sockaddr_ family of pointers |
| 3123 | // instead of being a pointer to struct sockaddr. In these cases, the |
| 3124 | // standardized signature will not match, thus we try to match with another |
| 3125 | // signature that has the joker Irrelevant type. We also remove those |
| 3126 | // constraints which require pointer types for the sockaddr param. |
| 3127 | |
| 3128 | // int socket(int domain, int type, int protocol); |
| 3129 | addToFunctionSummaryMap( |
| 3130 | "socket" , Signature(ArgTypes{IntTy, IntTy, IntTy}, RetType{IntTy}), |
| 3131 | Summary(NoEvalCall) |
| 3132 | .Case(CS: ReturnsValidFileDescriptor, ErrnoC: ErrnoMustNotBeChecked, |
| 3133 | Note: GenericSuccessMsg) |
| 3134 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg)); |
| 3135 | |
| 3136 | auto Accept = |
| 3137 | Summary(NoEvalCall) |
| 3138 | .Case(CS: ReturnsValidFileDescriptor, ErrnoC: ErrnoMustNotBeChecked, |
| 3139 | Note: GenericSuccessMsg) |
| 3140 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3141 | .ArgConstraint(VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))); |
| 3142 | if (!addToFunctionSummaryMap( |
| 3143 | "accept" , |
| 3144 | // int accept(int socket, struct sockaddr *restrict address, |
| 3145 | // socklen_t *restrict address_len); |
| 3146 | Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, |
| 3147 | Socklen_tPtrRestrictTy}, |
| 3148 | RetType{IntTy}), |
| 3149 | Accept)) |
| 3150 | addToFunctionSummaryMap( |
| 3151 | "accept" , |
| 3152 | Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, |
| 3153 | RetType{IntTy}), |
| 3154 | Accept); |
| 3155 | |
| 3156 | // int bind(int socket, const struct sockaddr *address, socklen_t |
| 3157 | // address_len); |
| 3158 | if (!addToFunctionSummaryMap( |
| 3159 | "bind" , |
| 3160 | Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy}, |
| 3161 | RetType{IntTy}), |
| 3162 | Summary(NoEvalCall) |
| 3163 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3164 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3165 | .ArgConstraint( |
| 3166 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 3167 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 3168 | .ArgConstraint( |
| 3169 | VC: BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2))) |
| 3170 | .ArgConstraint( |
| 3171 | VC: ArgumentCondition(2, WithinRange, Range(0, Socklen_tMax))))) |
| 3172 | // Do not add constraints on sockaddr. |
| 3173 | addToFunctionSummaryMap( |
| 3174 | "bind" , |
| 3175 | Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}), |
| 3176 | Summary(NoEvalCall) |
| 3177 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3178 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3179 | .ArgConstraint( |
| 3180 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 3181 | .ArgConstraint( |
| 3182 | VC: ArgumentCondition(2, WithinRange, Range(0, Socklen_tMax)))); |
| 3183 | |
| 3184 | // int getpeername(int socket, struct sockaddr *restrict address, |
| 3185 | // socklen_t *restrict address_len); |
| 3186 | if (!addToFunctionSummaryMap( |
| 3187 | "getpeername" , |
| 3188 | Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, |
| 3189 | Socklen_tPtrRestrictTy}, |
| 3190 | RetType{IntTy}), |
| 3191 | Summary(NoEvalCall) |
| 3192 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3193 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3194 | .ArgConstraint( |
| 3195 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 3196 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 3197 | .ArgConstraint(VC: NotNull(ArgNo(2))))) |
| 3198 | addToFunctionSummaryMap( |
| 3199 | "getpeername" , |
| 3200 | Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, |
| 3201 | RetType{IntTy}), |
| 3202 | Summary(NoEvalCall) |
| 3203 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3204 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3205 | .ArgConstraint( |
| 3206 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 3207 | |
| 3208 | // int getsockname(int socket, struct sockaddr *restrict address, |
| 3209 | // socklen_t *restrict address_len); |
| 3210 | if (!addToFunctionSummaryMap( |
| 3211 | "getsockname" , |
| 3212 | Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, |
| 3213 | Socklen_tPtrRestrictTy}, |
| 3214 | RetType{IntTy}), |
| 3215 | Summary(NoEvalCall) |
| 3216 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3217 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3218 | .ArgConstraint( |
| 3219 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 3220 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 3221 | .ArgConstraint(VC: NotNull(ArgNo(2))))) |
| 3222 | addToFunctionSummaryMap( |
| 3223 | "getsockname" , |
| 3224 | Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, |
| 3225 | RetType{IntTy}), |
| 3226 | Summary(NoEvalCall) |
| 3227 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3228 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3229 | .ArgConstraint( |
| 3230 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 3231 | |
| 3232 | // int connect(int socket, const struct sockaddr *address, socklen_t |
| 3233 | // address_len); |
| 3234 | if (!addToFunctionSummaryMap( |
| 3235 | "connect" , |
| 3236 | Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy}, |
| 3237 | RetType{IntTy}), |
| 3238 | Summary(NoEvalCall) |
| 3239 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3240 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3241 | .ArgConstraint( |
| 3242 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 3243 | .ArgConstraint(VC: NotNull(ArgNo(1))))) |
| 3244 | addToFunctionSummaryMap( |
| 3245 | "connect" , |
| 3246 | Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}), |
| 3247 | Summary(NoEvalCall) |
| 3248 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3249 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3250 | .ArgConstraint( |
| 3251 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 3252 | |
| 3253 | auto Recvfrom = |
| 3254 | Summary(NoEvalCall) |
| 3255 | .Case(CS: {ReturnValueCondition(LessThanOrEq, ArgNo(2)), |
| 3256 | ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, |
| 3257 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3258 | .Case(CS: {ReturnValueCondition(WithinRange, SingleValue(0)), |
| 3259 | ArgumentCondition(2, WithinRange, SingleValue(0))}, |
| 3260 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3261 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3262 | .ArgConstraint(VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 3263 | .ArgConstraint(VC: BufferSize(/*Buffer=*/ArgNo(1), |
| 3264 | /*BufSize=*/ArgNo(2))); |
| 3265 | if (!addToFunctionSummaryMap( |
| 3266 | "recvfrom" , |
| 3267 | // ssize_t recvfrom(int socket, void *restrict buffer, |
| 3268 | // size_t length, |
| 3269 | // int flags, struct sockaddr *restrict address, |
| 3270 | // socklen_t *restrict address_len); |
| 3271 | Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy, |
| 3272 | StructSockaddrPtrRestrictTy, |
| 3273 | Socklen_tPtrRestrictTy}, |
| 3274 | RetType{Ssize_tTy}), |
| 3275 | Recvfrom)) |
| 3276 | addToFunctionSummaryMap( |
| 3277 | "recvfrom" , |
| 3278 | Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy, |
| 3279 | Irrelevant, Socklen_tPtrRestrictTy}, |
| 3280 | RetType{Ssize_tTy}), |
| 3281 | Recvfrom); |
| 3282 | |
| 3283 | auto Sendto = |
| 3284 | Summary(NoEvalCall) |
| 3285 | .Case(CS: {ReturnValueCondition(LessThanOrEq, ArgNo(2)), |
| 3286 | ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, |
| 3287 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3288 | .Case(CS: {ReturnValueCondition(WithinRange, SingleValue(0)), |
| 3289 | ArgumentCondition(2, WithinRange, SingleValue(0))}, |
| 3290 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3291 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3292 | .ArgConstraint(VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 3293 | .ArgConstraint(VC: BufferSize(/*Buffer=*/ArgNo(1), |
| 3294 | /*BufSize=*/ArgNo(2))); |
| 3295 | if (!addToFunctionSummaryMap( |
| 3296 | "sendto" , |
| 3297 | // ssize_t sendto(int socket, const void *message, size_t length, |
| 3298 | // int flags, const struct sockaddr *dest_addr, |
| 3299 | // socklen_t dest_len); |
| 3300 | Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, |
| 3301 | ConstStructSockaddrPtrTy, Socklen_tTy}, |
| 3302 | RetType{Ssize_tTy}), |
| 3303 | Sendto)) |
| 3304 | addToFunctionSummaryMap( |
| 3305 | "sendto" , |
| 3306 | Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, Irrelevant, |
| 3307 | Socklen_tTy}, |
| 3308 | RetType{Ssize_tTy}), |
| 3309 | Sendto); |
| 3310 | |
| 3311 | // int listen(int sockfd, int backlog); |
| 3312 | addToFunctionSummaryMap( |
| 3313 | "listen" , Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), |
| 3314 | Summary(NoEvalCall) |
| 3315 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3316 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3317 | .ArgConstraint( |
| 3318 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 3319 | |
| 3320 | // ssize_t recv(int sockfd, void *buf, size_t len, int flags); |
| 3321 | addToFunctionSummaryMap( |
| 3322 | "recv" , |
| 3323 | Signature(ArgTypes{IntTy, VoidPtrTy, SizeTy, IntTy}, |
| 3324 | RetType{Ssize_tTy}), |
| 3325 | Summary(NoEvalCall) |
| 3326 | .Case(CS: {ReturnValueCondition(LessThanOrEq, ArgNo(2)), |
| 3327 | ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, |
| 3328 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3329 | .Case(CS: {ReturnValueCondition(WithinRange, SingleValue(0)), |
| 3330 | ArgumentCondition(2, WithinRange, SingleValue(0))}, |
| 3331 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3332 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3333 | .ArgConstraint(VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 3334 | .ArgConstraint(VC: BufferSize(/*Buffer=*/ArgNo(1), |
| 3335 | /*BufSize=*/ArgNo(2)))); |
| 3336 | |
| 3337 | std::optional<QualType> StructMsghdrTy = lookupTy("msghdr" ); |
| 3338 | std::optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); |
| 3339 | std::optional<QualType> ConstStructMsghdrPtrTy = |
| 3340 | getPointerTy(getConstTy(StructMsghdrTy)); |
| 3341 | |
| 3342 | // ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); |
| 3343 | addToFunctionSummaryMap( |
| 3344 | "recvmsg" , |
| 3345 | Signature(ArgTypes{IntTy, StructMsghdrPtrTy, IntTy}, |
| 3346 | RetType{Ssize_tTy}), |
| 3347 | Summary(NoEvalCall) |
| 3348 | .Case(CS: {ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, |
| 3349 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3350 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3351 | .ArgConstraint( |
| 3352 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 3353 | |
| 3354 | // ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); |
| 3355 | addToFunctionSummaryMap( |
| 3356 | "sendmsg" , |
| 3357 | Signature(ArgTypes{IntTy, ConstStructMsghdrPtrTy, IntTy}, |
| 3358 | RetType{Ssize_tTy}), |
| 3359 | Summary(NoEvalCall) |
| 3360 | .Case(CS: {ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, |
| 3361 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3362 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3363 | .ArgConstraint( |
| 3364 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 3365 | |
| 3366 | // int setsockopt(int socket, int level, int option_name, |
| 3367 | // const void *option_value, socklen_t option_len); |
| 3368 | addToFunctionSummaryMap( |
| 3369 | "setsockopt" , |
| 3370 | Signature(ArgTypes{IntTy, IntTy, IntTy, ConstVoidPtrTy, Socklen_tTy}, |
| 3371 | RetType{IntTy}), |
| 3372 | Summary(NoEvalCall) |
| 3373 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3374 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3375 | .ArgConstraint(VC: NotNullBuffer(ArgNo(3), ArgNo(4))) |
| 3376 | .ArgConstraint( |
| 3377 | VC: BufferSize(/*Buffer=*/ArgNo(3), /*BufSize=*/ArgNo(4))) |
| 3378 | .ArgConstraint( |
| 3379 | VC: ArgumentCondition(4, WithinRange, Range(0, Socklen_tMax)))); |
| 3380 | |
| 3381 | // int getsockopt(int socket, int level, int option_name, |
| 3382 | // void *restrict option_value, |
| 3383 | // socklen_t *restrict option_len); |
| 3384 | addToFunctionSummaryMap( |
| 3385 | "getsockopt" , |
| 3386 | Signature(ArgTypes{IntTy, IntTy, IntTy, VoidPtrRestrictTy, |
| 3387 | Socklen_tPtrRestrictTy}, |
| 3388 | RetType{IntTy}), |
| 3389 | Summary(NoEvalCall) |
| 3390 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3391 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3392 | .ArgConstraint(VC: NotNull(ArgNo(3))) |
| 3393 | .ArgConstraint(VC: NotNull(ArgNo(4)))); |
| 3394 | |
| 3395 | // ssize_t send(int sockfd, const void *buf, size_t len, int flags); |
| 3396 | addToFunctionSummaryMap( |
| 3397 | "send" , |
| 3398 | Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy}, |
| 3399 | RetType{Ssize_tTy}), |
| 3400 | Summary(NoEvalCall) |
| 3401 | .Case(CS: {ReturnValueCondition(LessThanOrEq, ArgNo(2)), |
| 3402 | ReturnValueCondition(WithinRange, Range(1, Ssize_tMax))}, |
| 3403 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3404 | .Case(CS: {ReturnValueCondition(WithinRange, SingleValue(0)), |
| 3405 | ArgumentCondition(2, WithinRange, SingleValue(0))}, |
| 3406 | ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3407 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3408 | .ArgConstraint(VC: ArgumentCondition(0, WithinRange, Range(0, IntMax))) |
| 3409 | .ArgConstraint(VC: BufferSize(/*Buffer=*/ArgNo(1), |
| 3410 | /*BufSize=*/ArgNo(2)))); |
| 3411 | |
| 3412 | // int socketpair(int domain, int type, int protocol, int sv[2]); |
| 3413 | addToFunctionSummaryMap( |
| 3414 | "socketpair" , |
| 3415 | Signature(ArgTypes{IntTy, IntTy, IntTy, IntPtrTy}, RetType{IntTy}), |
| 3416 | Summary(NoEvalCall) |
| 3417 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3418 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3419 | .ArgConstraint(VC: NotNull(ArgNo(3)))); |
| 3420 | |
| 3421 | // int shutdown(int socket, int how); |
| 3422 | addToFunctionSummaryMap( |
| 3423 | "shutdown" , Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), |
| 3424 | Summary(NoEvalCall) |
| 3425 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3426 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3427 | .ArgConstraint( |
| 3428 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 3429 | |
| 3430 | // int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, |
| 3431 | // char *restrict node, socklen_t nodelen, |
| 3432 | // char *restrict service, |
| 3433 | // socklen_t servicelen, int flags); |
| 3434 | // |
| 3435 | // This is defined in netdb.h. And contrary to 'socket.h', the sockaddr |
| 3436 | // parameter is never handled as a transparent union in netdb.h |
| 3437 | addToFunctionSummaryMap( |
| 3438 | "getnameinfo" , |
| 3439 | Signature(ArgTypes{ConstStructSockaddrPtrRestrictTy, Socklen_tTy, |
| 3440 | CharPtrRestrictTy, Socklen_tTy, CharPtrRestrictTy, |
| 3441 | Socklen_tTy, IntTy}, |
| 3442 | RetType{IntTy}), |
| 3443 | Summary(NoEvalCall) |
| 3444 | .ArgConstraint( |
| 3445 | VC: BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1))) |
| 3446 | .ArgConstraint( |
| 3447 | VC: ArgumentCondition(1, WithinRange, Range(0, Socklen_tMax))) |
| 3448 | .ArgConstraint( |
| 3449 | VC: BufferSize(/*Buffer=*/ArgNo(2), /*BufSize=*/ArgNo(3))) |
| 3450 | .ArgConstraint( |
| 3451 | VC: ArgumentCondition(3, WithinRange, Range(0, Socklen_tMax))) |
| 3452 | .ArgConstraint( |
| 3453 | VC: BufferSize(/*Buffer=*/ArgNo(4), /*BufSize=*/ArgNo(5))) |
| 3454 | .ArgConstraint( |
| 3455 | VC: ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax)))); |
| 3456 | |
| 3457 | std::optional<QualType> StructUtimbufTy = lookupTy("utimbuf" ); |
| 3458 | std::optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy); |
| 3459 | |
| 3460 | // int utime(const char *filename, struct utimbuf *buf); |
| 3461 | addToFunctionSummaryMap( |
| 3462 | "utime" , |
| 3463 | Signature(ArgTypes{ConstCharPtrTy, StructUtimbufPtrTy}, RetType{IntTy}), |
| 3464 | Summary(NoEvalCall) |
| 3465 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3466 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3467 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3468 | |
| 3469 | std::optional<QualType> StructTimespecTy = lookupTy("timespec" ); |
| 3470 | std::optional<QualType> StructTimespecPtrTy = |
| 3471 | getPointerTy(StructTimespecTy); |
| 3472 | std::optional<QualType> ConstStructTimespecPtrTy = |
| 3473 | getPointerTy(getConstTy(StructTimespecTy)); |
| 3474 | |
| 3475 | // int futimens(int fd, const struct timespec times[2]); |
| 3476 | addToFunctionSummaryMap( |
| 3477 | "futimens" , |
| 3478 | Signature(ArgTypes{IntTy, ConstStructTimespecPtrTy}, RetType{IntTy}), |
| 3479 | Summary(NoEvalCall) |
| 3480 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3481 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3482 | .ArgConstraint( |
| 3483 | VC: ArgumentCondition(0, WithinRange, Range(0, IntMax)))); |
| 3484 | |
| 3485 | // int utimensat(int dirfd, const char *pathname, |
| 3486 | // const struct timespec times[2], int flags); |
| 3487 | addToFunctionSummaryMap( |
| 3488 | "utimensat" , |
| 3489 | Signature( |
| 3490 | ArgTypes{IntTy, ConstCharPtrTy, ConstStructTimespecPtrTy, IntTy}, |
| 3491 | RetType{IntTy}), |
| 3492 | Summary(NoEvalCall) |
| 3493 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3494 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3495 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 3496 | |
| 3497 | std::optional<QualType> StructTimevalTy = lookupTy("timeval" ); |
| 3498 | std::optional<QualType> ConstStructTimevalPtrTy = |
| 3499 | getPointerTy(getConstTy(StructTimevalTy)); |
| 3500 | |
| 3501 | // int utimes(const char *filename, const struct timeval times[2]); |
| 3502 | addToFunctionSummaryMap( |
| 3503 | "utimes" , |
| 3504 | Signature(ArgTypes{ConstCharPtrTy, ConstStructTimevalPtrTy}, |
| 3505 | RetType{IntTy}), |
| 3506 | Summary(NoEvalCall) |
| 3507 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3508 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3509 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3510 | |
| 3511 | // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); |
| 3512 | addToFunctionSummaryMap( |
| 3513 | "nanosleep" , |
| 3514 | Signature(ArgTypes{ConstStructTimespecPtrTy, StructTimespecPtrTy}, |
| 3515 | RetType{IntTy}), |
| 3516 | Summary(NoEvalCall) |
| 3517 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3518 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3519 | .ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3520 | |
| 3521 | std::optional<QualType> Time_tTy = lookupTy("time_t" ); |
| 3522 | std::optional<QualType> ConstTime_tPtrTy = |
| 3523 | getPointerTy(getConstTy(Time_tTy)); |
| 3524 | std::optional<QualType> ConstTime_tPtrRestrictTy = |
| 3525 | getRestrictTy(ConstTime_tPtrTy); |
| 3526 | |
| 3527 | std::optional<QualType> StructTmTy = lookupTy("tm" ); |
| 3528 | std::optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy); |
| 3529 | std::optional<QualType> StructTmPtrRestrictTy = |
| 3530 | getRestrictTy(StructTmPtrTy); |
| 3531 | std::optional<QualType> ConstStructTmPtrTy = |
| 3532 | getPointerTy(getConstTy(StructTmTy)); |
| 3533 | std::optional<QualType> ConstStructTmPtrRestrictTy = |
| 3534 | getRestrictTy(ConstStructTmPtrTy); |
| 3535 | |
| 3536 | // struct tm * localtime(const time_t *tp); |
| 3537 | addToFunctionSummaryMap( |
| 3538 | "localtime" , |
| 3539 | Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), |
| 3540 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3541 | |
| 3542 | // struct tm *localtime_r(const time_t *restrict timer, |
| 3543 | // struct tm *restrict result); |
| 3544 | addToFunctionSummaryMap( |
| 3545 | "localtime_r" , |
| 3546 | Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy}, |
| 3547 | RetType{StructTmPtrTy}), |
| 3548 | Summary(NoEvalCall) |
| 3549 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 3550 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 3551 | |
| 3552 | // char *asctime_r(const struct tm *restrict tm, char *restrict buf); |
| 3553 | addToFunctionSummaryMap( |
| 3554 | "asctime_r" , |
| 3555 | Signature(ArgTypes{ConstStructTmPtrRestrictTy, CharPtrRestrictTy}, |
| 3556 | RetType{CharPtrTy}), |
| 3557 | Summary(NoEvalCall) |
| 3558 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 3559 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 3560 | .ArgConstraint(VC: BufferSize(/*Buffer=*/ArgNo(1), |
| 3561 | /*MinBufSize=*/BVF.getValue(X: 26, T: IntTy)))); |
| 3562 | |
| 3563 | // char *ctime_r(const time_t *timep, char *buf); |
| 3564 | addToFunctionSummaryMap( |
| 3565 | "ctime_r" , |
| 3566 | Signature(ArgTypes{ConstTime_tPtrTy, CharPtrTy}, RetType{CharPtrTy}), |
| 3567 | Summary(NoEvalCall) |
| 3568 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 3569 | .ArgConstraint(VC: NotNull(ArgNo(1))) |
| 3570 | .ArgConstraint(VC: BufferSize( |
| 3571 | /*Buffer=*/ArgNo(1), |
| 3572 | /*MinBufSize=*/BVF.getValue(X: 26, T: IntTy)))); |
| 3573 | |
| 3574 | // struct tm *gmtime_r(const time_t *restrict timer, |
| 3575 | // struct tm *restrict result); |
| 3576 | addToFunctionSummaryMap( |
| 3577 | "gmtime_r" , |
| 3578 | Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy}, |
| 3579 | RetType{StructTmPtrTy}), |
| 3580 | Summary(NoEvalCall) |
| 3581 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 3582 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 3583 | |
| 3584 | // struct tm * gmtime(const time_t *tp); |
| 3585 | addToFunctionSummaryMap( |
| 3586 | "gmtime" , Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), |
| 3587 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3588 | |
| 3589 | std::optional<QualType> Clockid_tTy = lookupTy("clockid_t" ); |
| 3590 | |
| 3591 | // int clock_gettime(clockid_t clock_id, struct timespec *tp); |
| 3592 | addToFunctionSummaryMap( |
| 3593 | "clock_gettime" , |
| 3594 | Signature(ArgTypes{Clockid_tTy, StructTimespecPtrTy}, RetType{IntTy}), |
| 3595 | Summary(NoEvalCall) |
| 3596 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3597 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3598 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 3599 | |
| 3600 | std::optional<QualType> StructItimervalTy = lookupTy("itimerval" ); |
| 3601 | std::optional<QualType> StructItimervalPtrTy = |
| 3602 | getPointerTy(StructItimervalTy); |
| 3603 | |
| 3604 | // int getitimer(int which, struct itimerval *curr_value); |
| 3605 | addToFunctionSummaryMap( |
| 3606 | "getitimer" , |
| 3607 | Signature(ArgTypes{IntTy, StructItimervalPtrTy}, RetType{IntTy}), |
| 3608 | Summary(NoEvalCall) |
| 3609 | .Case(CS: ReturnsZero, ErrnoC: ErrnoMustNotBeChecked, Note: GenericSuccessMsg) |
| 3610 | .Case(CS: ReturnsMinusOne, ErrnoC: ErrnoNEZeroIrrelevant, Note: GenericFailureMsg) |
| 3611 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 3612 | |
| 3613 | std::optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t" ); |
| 3614 | std::optional<QualType> Pthread_cond_tPtrTy = |
| 3615 | getPointerTy(Pthread_cond_tTy); |
| 3616 | std::optional<QualType> Pthread_tTy = lookupTy("pthread_t" ); |
| 3617 | std::optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy); |
| 3618 | std::optional<QualType> Pthread_tPtrRestrictTy = |
| 3619 | getRestrictTy(Pthread_tPtrTy); |
| 3620 | std::optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t" ); |
| 3621 | std::optional<QualType> Pthread_mutex_tPtrTy = |
| 3622 | getPointerTy(Pthread_mutex_tTy); |
| 3623 | std::optional<QualType> Pthread_mutex_tPtrRestrictTy = |
| 3624 | getRestrictTy(Pthread_mutex_tPtrTy); |
| 3625 | std::optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t" ); |
| 3626 | std::optional<QualType> Pthread_attr_tPtrTy = |
| 3627 | getPointerTy(Pthread_attr_tTy); |
| 3628 | std::optional<QualType> ConstPthread_attr_tPtrTy = |
| 3629 | getPointerTy(getConstTy(Pthread_attr_tTy)); |
| 3630 | std::optional<QualType> ConstPthread_attr_tPtrRestrictTy = |
| 3631 | getRestrictTy(ConstPthread_attr_tPtrTy); |
| 3632 | std::optional<QualType> Pthread_mutexattr_tTy = |
| 3633 | lookupTy("pthread_mutexattr_t" ); |
| 3634 | std::optional<QualType> ConstPthread_mutexattr_tPtrTy = |
| 3635 | getPointerTy(getConstTy(Pthread_mutexattr_tTy)); |
| 3636 | std::optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy = |
| 3637 | getRestrictTy(ConstPthread_mutexattr_tPtrTy); |
| 3638 | |
| 3639 | QualType PthreadStartRoutineTy = getPointerTy( |
| 3640 | ACtx.getFunctionType(/*ResultTy=*/VoidPtrTy, /*Args=*/VoidPtrTy, |
| 3641 | EPI: FunctionProtoType::ExtProtoInfo())); |
| 3642 | |
| 3643 | // int pthread_cond_signal(pthread_cond_t *cond); |
| 3644 | // int pthread_cond_broadcast(pthread_cond_t *cond); |
| 3645 | addToFunctionSummaryMap( |
| 3646 | {"pthread_cond_signal" , "pthread_cond_broadcast" }, |
| 3647 | Signature(ArgTypes{Pthread_cond_tPtrTy}, RetType{IntTy}), |
| 3648 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3649 | |
| 3650 | // int pthread_create(pthread_t *restrict thread, |
| 3651 | // const pthread_attr_t *restrict attr, |
| 3652 | // void *(*start_routine)(void*), void *restrict arg); |
| 3653 | addToFunctionSummaryMap( |
| 3654 | "pthread_create" , |
| 3655 | Signature(ArgTypes{Pthread_tPtrRestrictTy, |
| 3656 | ConstPthread_attr_tPtrRestrictTy, |
| 3657 | PthreadStartRoutineTy, VoidPtrRestrictTy}, |
| 3658 | RetType{IntTy}), |
| 3659 | Summary(NoEvalCall) |
| 3660 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 3661 | .ArgConstraint(VC: NotNull(ArgNo(2)))); |
| 3662 | |
| 3663 | // int pthread_attr_destroy(pthread_attr_t *attr); |
| 3664 | // int pthread_attr_init(pthread_attr_t *attr); |
| 3665 | addToFunctionSummaryMap( |
| 3666 | {"pthread_attr_destroy" , "pthread_attr_init" }, |
| 3667 | Signature(ArgTypes{Pthread_attr_tPtrTy}, RetType{IntTy}), |
| 3668 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3669 | |
| 3670 | // int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, |
| 3671 | // size_t *restrict stacksize); |
| 3672 | // int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, |
| 3673 | // size_t *restrict guardsize); |
| 3674 | addToFunctionSummaryMap( |
| 3675 | {"pthread_attr_getstacksize" , "pthread_attr_getguardsize" }, |
| 3676 | Signature(ArgTypes{ConstPthread_attr_tPtrRestrictTy, SizePtrRestrictTy}, |
| 3677 | RetType{IntTy}), |
| 3678 | Summary(NoEvalCall) |
| 3679 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 3680 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 3681 | |
| 3682 | // int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); |
| 3683 | // int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); |
| 3684 | addToFunctionSummaryMap( |
| 3685 | {"pthread_attr_setstacksize" , "pthread_attr_setguardsize" }, |
| 3686 | Signature(ArgTypes{Pthread_attr_tPtrTy, SizeTy}, RetType{IntTy}), |
| 3687 | Summary(NoEvalCall) |
| 3688 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 3689 | .ArgConstraint( |
| 3690 | VC: ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); |
| 3691 | |
| 3692 | // int pthread_mutex_init(pthread_mutex_t *restrict mutex, const |
| 3693 | // pthread_mutexattr_t *restrict attr); |
| 3694 | addToFunctionSummaryMap( |
| 3695 | "pthread_mutex_init" , |
| 3696 | Signature(ArgTypes{Pthread_mutex_tPtrRestrictTy, |
| 3697 | ConstPthread_mutexattr_tPtrRestrictTy}, |
| 3698 | RetType{IntTy}), |
| 3699 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3700 | |
| 3701 | // int pthread_mutex_destroy(pthread_mutex_t *mutex); |
| 3702 | // int pthread_mutex_lock(pthread_mutex_t *mutex); |
| 3703 | // int pthread_mutex_trylock(pthread_mutex_t *mutex); |
| 3704 | // int pthread_mutex_unlock(pthread_mutex_t *mutex); |
| 3705 | addToFunctionSummaryMap( |
| 3706 | {"pthread_mutex_destroy" , "pthread_mutex_lock" , "pthread_mutex_trylock" , |
| 3707 | "pthread_mutex_unlock" }, |
| 3708 | Signature(ArgTypes{Pthread_mutex_tPtrTy}, RetType{IntTy}), |
| 3709 | Summary(NoEvalCall).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3710 | } |
| 3711 | |
| 3712 | // Functions for testing. |
| 3713 | if (AddTestFunctions) { |
| 3714 | const RangeInt IntMin = BVF.getMinValue(T: IntTy)->getLimitedValue(); |
| 3715 | |
| 3716 | addToFunctionSummaryMap( |
| 3717 | "__not_null" , Signature(ArgTypes{IntPtrTy}, RetType{IntTy}), |
| 3718 | Summary(EvalCallAsPure).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3719 | |
| 3720 | addToFunctionSummaryMap( |
| 3721 | "__not_null_buffer" , |
| 3722 | Signature(ArgTypes{VoidPtrTy, IntTy, IntTy}, RetType{IntTy}), |
| 3723 | Summary(EvalCallAsPure) |
| 3724 | .ArgConstraint(VC: NotNullBuffer(ArgNo(0), ArgNo(1), ArgNo(2)))); |
| 3725 | |
| 3726 | // Test inside range constraints. |
| 3727 | addToFunctionSummaryMap( |
| 3728 | "__single_val_0" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3729 | Summary(EvalCallAsPure) |
| 3730 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, SingleValue(0)))); |
| 3731 | addToFunctionSummaryMap( |
| 3732 | "__single_val_1" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3733 | Summary(EvalCallAsPure) |
| 3734 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, SingleValue(1)))); |
| 3735 | addToFunctionSummaryMap( |
| 3736 | "__range_1_2" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3737 | Summary(EvalCallAsPure) |
| 3738 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, Range(1, 2)))); |
| 3739 | addToFunctionSummaryMap( |
| 3740 | "__range_m1_1" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3741 | Summary(EvalCallAsPure) |
| 3742 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, Range(-1, 1)))); |
| 3743 | addToFunctionSummaryMap( |
| 3744 | "__range_m2_m1" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3745 | Summary(EvalCallAsPure) |
| 3746 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, Range(-2, -1)))); |
| 3747 | addToFunctionSummaryMap( |
| 3748 | "__range_m10_10" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3749 | Summary(EvalCallAsPure) |
| 3750 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, Range(-10, 10)))); |
| 3751 | addToFunctionSummaryMap("__range_m1_inf" , |
| 3752 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3753 | Summary(EvalCallAsPure) |
| 3754 | .ArgConstraint(VC: ArgumentCondition( |
| 3755 | 0U, WithinRange, Range(-1, IntMax)))); |
| 3756 | addToFunctionSummaryMap("__range_0_inf" , |
| 3757 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3758 | Summary(EvalCallAsPure) |
| 3759 | .ArgConstraint(VC: ArgumentCondition( |
| 3760 | 0U, WithinRange, Range(0, IntMax)))); |
| 3761 | addToFunctionSummaryMap("__range_1_inf" , |
| 3762 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3763 | Summary(EvalCallAsPure) |
| 3764 | .ArgConstraint(VC: ArgumentCondition( |
| 3765 | 0U, WithinRange, Range(1, IntMax)))); |
| 3766 | addToFunctionSummaryMap("__range_minf_m1" , |
| 3767 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3768 | Summary(EvalCallAsPure) |
| 3769 | .ArgConstraint(VC: ArgumentCondition( |
| 3770 | 0U, WithinRange, Range(IntMin, -1)))); |
| 3771 | addToFunctionSummaryMap("__range_minf_0" , |
| 3772 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3773 | Summary(EvalCallAsPure) |
| 3774 | .ArgConstraint(VC: ArgumentCondition( |
| 3775 | 0U, WithinRange, Range(IntMin, 0)))); |
| 3776 | addToFunctionSummaryMap("__range_minf_1" , |
| 3777 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3778 | Summary(EvalCallAsPure) |
| 3779 | .ArgConstraint(VC: ArgumentCondition( |
| 3780 | 0U, WithinRange, Range(IntMin, 1)))); |
| 3781 | addToFunctionSummaryMap("__range_1_2__4_6" , |
| 3782 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3783 | Summary(EvalCallAsPure) |
| 3784 | .ArgConstraint(VC: ArgumentCondition( |
| 3785 | 0U, WithinRange, Range({1, 2}, {4, 6})))); |
| 3786 | addToFunctionSummaryMap( |
| 3787 | "__range_1_2__4_inf" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3788 | Summary(EvalCallAsPure) |
| 3789 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, |
| 3790 | Range({1, 2}, {4, IntMax})))); |
| 3791 | |
| 3792 | // Test out of range constraints. |
| 3793 | addToFunctionSummaryMap( |
| 3794 | "__single_val_out_0" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3795 | Summary(EvalCallAsPure) |
| 3796 | .ArgConstraint(VC: ArgumentCondition(0U, OutOfRange, SingleValue(0)))); |
| 3797 | addToFunctionSummaryMap( |
| 3798 | "__single_val_out_1" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3799 | Summary(EvalCallAsPure) |
| 3800 | .ArgConstraint(VC: ArgumentCondition(0U, OutOfRange, SingleValue(1)))); |
| 3801 | addToFunctionSummaryMap( |
| 3802 | "__range_out_1_2" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3803 | Summary(EvalCallAsPure) |
| 3804 | .ArgConstraint(VC: ArgumentCondition(0U, OutOfRange, Range(1, 2)))); |
| 3805 | addToFunctionSummaryMap( |
| 3806 | "__range_out_m1_1" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3807 | Summary(EvalCallAsPure) |
| 3808 | .ArgConstraint(VC: ArgumentCondition(0U, OutOfRange, Range(-1, 1)))); |
| 3809 | addToFunctionSummaryMap( |
| 3810 | "__range_out_m2_m1" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3811 | Summary(EvalCallAsPure) |
| 3812 | .ArgConstraint(VC: ArgumentCondition(0U, OutOfRange, Range(-2, -1)))); |
| 3813 | addToFunctionSummaryMap( |
| 3814 | "__range_out_m10_10" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3815 | Summary(EvalCallAsPure) |
| 3816 | .ArgConstraint(VC: ArgumentCondition(0U, OutOfRange, Range(-10, 10)))); |
| 3817 | addToFunctionSummaryMap("__range_out_m1_inf" , |
| 3818 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3819 | Summary(EvalCallAsPure) |
| 3820 | .ArgConstraint(VC: ArgumentCondition( |
| 3821 | 0U, OutOfRange, Range(-1, IntMax)))); |
| 3822 | addToFunctionSummaryMap("__range_out_0_inf" , |
| 3823 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3824 | Summary(EvalCallAsPure) |
| 3825 | .ArgConstraint(VC: ArgumentCondition( |
| 3826 | 0U, OutOfRange, Range(0, IntMax)))); |
| 3827 | addToFunctionSummaryMap("__range_out_1_inf" , |
| 3828 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3829 | Summary(EvalCallAsPure) |
| 3830 | .ArgConstraint(VC: ArgumentCondition( |
| 3831 | 0U, OutOfRange, Range(1, IntMax)))); |
| 3832 | addToFunctionSummaryMap("__range_out_minf_m1" , |
| 3833 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3834 | Summary(EvalCallAsPure) |
| 3835 | .ArgConstraint(VC: ArgumentCondition( |
| 3836 | 0U, OutOfRange, Range(IntMin, -1)))); |
| 3837 | addToFunctionSummaryMap("__range_out_minf_0" , |
| 3838 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3839 | Summary(EvalCallAsPure) |
| 3840 | .ArgConstraint(VC: ArgumentCondition( |
| 3841 | 0U, OutOfRange, Range(IntMin, 0)))); |
| 3842 | addToFunctionSummaryMap("__range_out_minf_1" , |
| 3843 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3844 | Summary(EvalCallAsPure) |
| 3845 | .ArgConstraint(VC: ArgumentCondition( |
| 3846 | 0U, OutOfRange, Range(IntMin, 1)))); |
| 3847 | addToFunctionSummaryMap("__range_out_1_2__4_6" , |
| 3848 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3849 | Summary(EvalCallAsPure) |
| 3850 | .ArgConstraint(VC: ArgumentCondition( |
| 3851 | 0U, OutOfRange, Range({1, 2}, {4, 6})))); |
| 3852 | addToFunctionSummaryMap( |
| 3853 | "__range_out_1_2__4_inf" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3854 | Summary(EvalCallAsPure) |
| 3855 | .ArgConstraint( |
| 3856 | VC: ArgumentCondition(0U, OutOfRange, Range({1, 2}, {4, IntMax})))); |
| 3857 | |
| 3858 | // Test range kind. |
| 3859 | addToFunctionSummaryMap( |
| 3860 | "__within" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3861 | Summary(EvalCallAsPure) |
| 3862 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, SingleValue(1)))); |
| 3863 | addToFunctionSummaryMap( |
| 3864 | "__out_of" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3865 | Summary(EvalCallAsPure) |
| 3866 | .ArgConstraint(VC: ArgumentCondition(0U, OutOfRange, SingleValue(1)))); |
| 3867 | |
| 3868 | addToFunctionSummaryMap( |
| 3869 | "__two_constrained_args" , |
| 3870 | Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), |
| 3871 | Summary(EvalCallAsPure) |
| 3872 | .ArgConstraint(VC: ArgumentCondition(0U, WithinRange, SingleValue(1))) |
| 3873 | .ArgConstraint(VC: ArgumentCondition(1U, WithinRange, SingleValue(1)))); |
| 3874 | addToFunctionSummaryMap( |
| 3875 | "__arg_constrained_twice" , Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3876 | Summary(EvalCallAsPure) |
| 3877 | .ArgConstraint(VC: ArgumentCondition(0U, OutOfRange, SingleValue(1))) |
| 3878 | .ArgConstraint(VC: ArgumentCondition(0U, OutOfRange, SingleValue(2)))); |
| 3879 | addToFunctionSummaryMap( |
| 3880 | "__defaultparam" , |
| 3881 | Signature(ArgTypes{Irrelevant, IntTy}, RetType{IntTy}), |
| 3882 | Summary(EvalCallAsPure).ArgConstraint(VC: NotNull(ArgNo(0)))); |
| 3883 | addToFunctionSummaryMap( |
| 3884 | "__variadic" , |
| 3885 | Signature(ArgTypes{VoidPtrTy, ConstCharPtrTy}, RetType{IntTy}), |
| 3886 | Summary(EvalCallAsPure) |
| 3887 | .ArgConstraint(VC: NotNull(ArgNo(0))) |
| 3888 | .ArgConstraint(VC: NotNull(ArgNo(1)))); |
| 3889 | addToFunctionSummaryMap( |
| 3890 | "__buf_size_arg_constraint" , |
| 3891 | Signature(ArgTypes{ConstVoidPtrTy, SizeTy}, RetType{IntTy}), |
| 3892 | Summary(EvalCallAsPure) |
| 3893 | .ArgConstraint( |
| 3894 | VC: BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1)))); |
| 3895 | addToFunctionSummaryMap( |
| 3896 | "__buf_size_arg_constraint_mul" , |
| 3897 | Signature(ArgTypes{ConstVoidPtrTy, SizeTy, SizeTy}, RetType{IntTy}), |
| 3898 | Summary(EvalCallAsPure) |
| 3899 | .ArgConstraint(VC: BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), |
| 3900 | /*BufSizeMultiplier=*/ArgNo(2)))); |
| 3901 | addToFunctionSummaryMap( |
| 3902 | "__buf_size_arg_constraint_concrete" , |
| 3903 | Signature(ArgTypes{ConstVoidPtrTy}, RetType{IntTy}), |
| 3904 | Summary(EvalCallAsPure) |
| 3905 | .ArgConstraint(VC: BufferSize(/*Buffer=*/ArgNo(0), |
| 3906 | /*BufSize=*/BVF.getValue(X: 10, T: IntTy)))); |
| 3907 | addToFunctionSummaryMap( |
| 3908 | {"__test_restrict_param_0" , "__test_restrict_param_1" , |
| 3909 | "__test_restrict_param_2" }, |
| 3910 | Signature(ArgTypes{VoidPtrRestrictTy}, RetType{VoidTy}), |
| 3911 | Summary(EvalCallAsPure)); |
| 3912 | |
| 3913 | // Test the application of cases. |
| 3914 | addToFunctionSummaryMap( |
| 3915 | "__test_case_note" , Signature(ArgTypes{}, RetType{IntTy}), |
| 3916 | Summary(EvalCallAsPure) |
| 3917 | .Case(CS: {ReturnValueCondition(WithinRange, SingleValue(0))}, |
| 3918 | ErrnoC: ErrnoIrrelevant, Note: "Function returns 0" ) |
| 3919 | .Case(CS: {ReturnValueCondition(WithinRange, SingleValue(1))}, |
| 3920 | ErrnoC: ErrnoIrrelevant, Note: "Function returns 1" )); |
| 3921 | addToFunctionSummaryMap( |
| 3922 | "__test_case_range_1_2__4_6" , |
| 3923 | Signature(ArgTypes{IntTy}, RetType{IntTy}), |
| 3924 | Summary(EvalCallAsPure) |
| 3925 | .Case(CS: {ArgumentCondition(0U, WithinRange, |
| 3926 | IntRangeVector{{IntMin, 0}, {3, 3}}), |
| 3927 | ReturnValueCondition(WithinRange, SingleValue(1))}, |
| 3928 | ErrnoC: ErrnoIrrelevant) |
| 3929 | .Case(CS: {ArgumentCondition(0U, WithinRange, |
| 3930 | IntRangeVector{{3, 3}, {7, IntMax}}), |
| 3931 | ReturnValueCondition(WithinRange, SingleValue(2))}, |
| 3932 | ErrnoC: ErrnoIrrelevant) |
| 3933 | .Case(CS: {ArgumentCondition(0U, WithinRange, |
| 3934 | IntRangeVector{{IntMin, 0}, {7, IntMax}}), |
| 3935 | ReturnValueCondition(WithinRange, SingleValue(3))}, |
| 3936 | ErrnoC: ErrnoIrrelevant) |
| 3937 | .Case(CS: {ArgumentCondition( |
| 3938 | 0U, WithinRange, |
| 3939 | IntRangeVector{{IntMin, 0}, {3, 3}, {7, IntMax}}), |
| 3940 | ReturnValueCondition(WithinRange, SingleValue(4))}, |
| 3941 | ErrnoC: ErrnoIrrelevant)); |
| 3942 | } |
| 3943 | } |
| 3944 | |
| 3945 | void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) { |
| 3946 | auto *Checker = mgr.registerChecker<StdLibraryFunctionsChecker>(); |
| 3947 | Checker->CheckName = mgr.getCurrentCheckerName(); |
| 3948 | const AnalyzerOptions &Opts = mgr.getAnalyzerOptions(); |
| 3949 | Checker->DisplayLoadedSummaries = |
| 3950 | Opts.getCheckerBooleanOption(C: Checker, OptionName: "DisplayLoadedSummaries" ); |
| 3951 | Checker->ModelPOSIX = Opts.getCheckerBooleanOption(C: Checker, OptionName: "ModelPOSIX" ); |
| 3952 | Checker->ShouldAssumeControlledEnvironment = |
| 3953 | Opts.ShouldAssumeControlledEnvironment; |
| 3954 | } |
| 3955 | |
| 3956 | bool ento::shouldRegisterStdCLibraryFunctionsChecker( |
| 3957 | const CheckerManager &mgr) { |
| 3958 | return true; |
| 3959 | } |
| 3960 | |
| 3961 | void ento::registerStdCLibraryFunctionsTesterChecker(CheckerManager &mgr) { |
| 3962 | auto *Checker = mgr.getChecker<StdLibraryFunctionsChecker>(); |
| 3963 | Checker->AddTestFunctions = true; |
| 3964 | } |
| 3965 | |
| 3966 | bool ento::shouldRegisterStdCLibraryFunctionsTesterChecker( |
| 3967 | const CheckerManager &mgr) { |
| 3968 | return true; |
| 3969 | } |
| 3970 | |