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 | |