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