File: | clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp |
Warning: | line 747, column 17 Called C++ object pointer is null |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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 effets), 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 | // The following standard C functions are currently supported: | |||
42 | // | |||
43 | // fgetc getline isdigit isupper toascii | |||
44 | // fread isalnum isgraph isxdigit | |||
45 | // fwrite isalpha islower read | |||
46 | // getc isascii isprint write | |||
47 | // getchar isblank ispunct toupper | |||
48 | // getdelim iscntrl isspace tolower | |||
49 | // | |||
50 | //===----------------------------------------------------------------------===// | |||
51 | ||||
52 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" | |||
53 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" | |||
54 | #include "clang/StaticAnalyzer/Core/Checker.h" | |||
55 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" | |||
56 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" | |||
57 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" | |||
58 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" | |||
59 | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicSize.h" | |||
60 | ||||
61 | using namespace clang; | |||
62 | using namespace clang::ento; | |||
63 | ||||
64 | namespace { | |||
65 | class StdLibraryFunctionsChecker | |||
66 | : public Checker<check::PreCall, check::PostCall, eval::Call> { | |||
67 | ||||
68 | class Summary; | |||
69 | ||||
70 | /// Specify how much the analyzer engine should entrust modeling this function | |||
71 | /// to us. If he doesn't, he performs additional invalidations. | |||
72 | enum InvalidationKind { NoEvalCall, EvalCallAsPure }; | |||
73 | ||||
74 | // The universal integral type to use in value range descriptions. | |||
75 | // Unsigned to make sure overflows are well-defined. | |||
76 | typedef uint64_t RangeInt; | |||
77 | ||||
78 | /// Normally, describes a single range constraint, eg. {{0, 1}, {3, 4}} is | |||
79 | /// a non-negative integer, which less than 5 and not equal to 2. For | |||
80 | /// `ComparesToArgument', holds information about how exactly to compare to | |||
81 | /// the argument. | |||
82 | typedef std::vector<std::pair<RangeInt, RangeInt>> IntRangeVector; | |||
83 | ||||
84 | /// A reference to an argument or return value by its number. | |||
85 | /// ArgNo in CallExpr and CallEvent is defined as Unsigned, but | |||
86 | /// obviously uint32_t should be enough for all practical purposes. | |||
87 | typedef uint32_t ArgNo; | |||
88 | static const ArgNo Ret; | |||
89 | ||||
90 | class ValueConstraint; | |||
91 | ||||
92 | // Pointer to the ValueConstraint. We need a copyable, polymorphic and | |||
93 | // default initialize able type (vector needs that). A raw pointer was good, | |||
94 | // however, we cannot default initialize that. unique_ptr makes the Summary | |||
95 | // class non-copyable, therefore not an option. Releasing the copyability | |||
96 | // requirement would render the initialization of the Summary map infeasible. | |||
97 | using ValueConstraintPtr = std::shared_ptr<ValueConstraint>; | |||
98 | ||||
99 | /// Polymorphic base class that represents a constraint on a given argument | |||
100 | /// (or return value) of a function. Derived classes implement different kind | |||
101 | /// of constraints, e.g range constraints or correlation between two | |||
102 | /// arguments. | |||
103 | class ValueConstraint { | |||
104 | public: | |||
105 | ValueConstraint(ArgNo ArgN) : ArgN(ArgN) {} | |||
106 | virtual ~ValueConstraint() {} | |||
107 | /// Apply the effects of the constraint on the given program state. If null | |||
108 | /// is returned then the constraint is not feasible. | |||
109 | virtual ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, | |||
110 | const Summary &Summary, | |||
111 | CheckerContext &C) const = 0; | |||
112 | virtual ValueConstraintPtr negate() const { | |||
113 | llvm_unreachable("Not implemented")::llvm::llvm_unreachable_internal("Not implemented", "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 113); | |||
114 | }; | |||
115 | ||||
116 | // Check whether the constraint is malformed or not. It is malformed if the | |||
117 | // specified argument has a mismatch with the given FunctionDecl (e.g. the | |||
118 | // arg number is out-of-range of the function's argument list). | |||
119 | bool checkValidity(const FunctionDecl *FD) const { | |||
120 | const bool ValidArg = ArgN == Ret || ArgN < FD->getNumParams(); | |||
121 | assert(ValidArg && "Arg out of range!")((ValidArg && "Arg out of range!") ? static_cast<void > (0) : __assert_fail ("ValidArg && \"Arg out of range!\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 121, __PRETTY_FUNCTION__)); | |||
122 | if (!ValidArg) | |||
123 | return false; | |||
124 | // Subclasses may further refine the validation. | |||
125 | return checkSpecificValidity(FD); | |||
126 | } | |||
127 | ArgNo getArgNo() const { return ArgN; } | |||
128 | ||||
129 | virtual StringRef getName() const = 0; | |||
130 | ||||
131 | protected: | |||
132 | ArgNo ArgN; // Argument to which we apply the constraint. | |||
133 | ||||
134 | /// Do polymorphic sanity check on the constraint. | |||
135 | virtual bool checkSpecificValidity(const FunctionDecl *FD) const { | |||
136 | return true; | |||
137 | } | |||
138 | }; | |||
139 | ||||
140 | /// Given a range, should the argument stay inside or outside this range? | |||
141 | enum RangeKind { OutOfRange, WithinRange }; | |||
142 | ||||
143 | /// Encapsulates a range on a single symbol. | |||
144 | class RangeConstraint : public ValueConstraint { | |||
145 | RangeKind Kind; | |||
146 | // A range is formed as a set of intervals (sub-ranges). | |||
147 | // E.g. {['A', 'Z'], ['a', 'z']} | |||
148 | // | |||
149 | // The default constructed RangeConstraint has an empty range set, applying | |||
150 | // such constraint does not involve any assumptions, thus the State remains | |||
151 | // unchanged. This is meaningful, if the range is dependent on a looked up | |||
152 | // type (e.g. [0, Socklen_tMax]). If the type is not found, then the range | |||
153 | // is default initialized to be empty. | |||
154 | IntRangeVector Ranges; | |||
155 | ||||
156 | public: | |||
157 | StringRef getName() const override { return "Range"; } | |||
158 | RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges) | |||
159 | : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges) {} | |||
160 | ||||
161 | const IntRangeVector &getRanges() const { return Ranges; } | |||
162 | ||||
163 | private: | |||
164 | ProgramStateRef applyAsOutOfRange(ProgramStateRef State, | |||
165 | const CallEvent &Call, | |||
166 | const Summary &Summary) const; | |||
167 | ProgramStateRef applyAsWithinRange(ProgramStateRef State, | |||
168 | const CallEvent &Call, | |||
169 | const Summary &Summary) const; | |||
170 | ||||
171 | public: | |||
172 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, | |||
173 | const Summary &Summary, | |||
174 | CheckerContext &C) const override { | |||
175 | switch (Kind) { | |||
176 | case OutOfRange: | |||
177 | return applyAsOutOfRange(State, Call, Summary); | |||
178 | case WithinRange: | |||
179 | return applyAsWithinRange(State, Call, Summary); | |||
180 | } | |||
181 | llvm_unreachable("Unknown range kind!")::llvm::llvm_unreachable_internal("Unknown range kind!", "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 181); | |||
182 | } | |||
183 | ||||
184 | ValueConstraintPtr negate() const override { | |||
185 | RangeConstraint Tmp(*this); | |||
186 | switch (Kind) { | |||
187 | case OutOfRange: | |||
188 | Tmp.Kind = WithinRange; | |||
189 | break; | |||
190 | case WithinRange: | |||
191 | Tmp.Kind = OutOfRange; | |||
192 | break; | |||
193 | } | |||
194 | return std::make_shared<RangeConstraint>(Tmp); | |||
195 | } | |||
196 | ||||
197 | bool checkSpecificValidity(const FunctionDecl *FD) const override { | |||
198 | const bool ValidArg = | |||
199 | getArgType(FD, ArgN)->isIntegralType(FD->getASTContext()); | |||
200 | assert(ValidArg &&((ValidArg && "This constraint should be applied on an integral type" ) ? static_cast<void> (0) : __assert_fail ("ValidArg && \"This constraint should be applied on an integral type\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 201, __PRETTY_FUNCTION__)) | |||
201 | "This constraint should be applied on an integral type")((ValidArg && "This constraint should be applied on an integral type" ) ? static_cast<void> (0) : __assert_fail ("ValidArg && \"This constraint should be applied on an integral type\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 201, __PRETTY_FUNCTION__)); | |||
202 | return ValidArg; | |||
203 | } | |||
204 | }; | |||
205 | ||||
206 | class ComparisonConstraint : public ValueConstraint { | |||
207 | BinaryOperator::Opcode Opcode; | |||
208 | ArgNo OtherArgN; | |||
209 | ||||
210 | public: | |||
211 | virtual StringRef getName() const override { return "Comparison"; }; | |||
212 | ComparisonConstraint(ArgNo ArgN, BinaryOperator::Opcode Opcode, | |||
213 | ArgNo OtherArgN) | |||
214 | : ValueConstraint(ArgN), Opcode(Opcode), OtherArgN(OtherArgN) {} | |||
215 | ArgNo getOtherArgNo() const { return OtherArgN; } | |||
216 | BinaryOperator::Opcode getOpcode() const { return Opcode; } | |||
217 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, | |||
218 | const Summary &Summary, | |||
219 | CheckerContext &C) const override; | |||
220 | }; | |||
221 | ||||
222 | class NotNullConstraint : public ValueConstraint { | |||
223 | using ValueConstraint::ValueConstraint; | |||
224 | // This variable has a role when we negate the constraint. | |||
225 | bool CannotBeNull = true; | |||
226 | ||||
227 | public: | |||
228 | StringRef getName() const override { return "NonNull"; } | |||
229 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, | |||
230 | const Summary &Summary, | |||
231 | CheckerContext &C) const override { | |||
232 | SVal V = getArgSVal(Call, getArgNo()); | |||
233 | if (V.isUndef()) | |||
234 | return State; | |||
235 | ||||
236 | DefinedOrUnknownSVal L = V.castAs<DefinedOrUnknownSVal>(); | |||
237 | if (!L.getAs<Loc>()) | |||
238 | return State; | |||
239 | ||||
240 | return State->assume(L, CannotBeNull); | |||
241 | } | |||
242 | ||||
243 | ValueConstraintPtr negate() const override { | |||
244 | NotNullConstraint Tmp(*this); | |||
245 | Tmp.CannotBeNull = !this->CannotBeNull; | |||
246 | return std::make_shared<NotNullConstraint>(Tmp); | |||
247 | } | |||
248 | ||||
249 | bool checkSpecificValidity(const FunctionDecl *FD) const override { | |||
250 | const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); | |||
251 | assert(ValidArg &&((ValidArg && "This constraint should be applied only on a pointer type" ) ? static_cast<void> (0) : __assert_fail ("ValidArg && \"This constraint should be applied only on a pointer type\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 252, __PRETTY_FUNCTION__)) | |||
252 | "This constraint should be applied only on a pointer type")((ValidArg && "This constraint should be applied only on a pointer type" ) ? static_cast<void> (0) : __assert_fail ("ValidArg && \"This constraint should be applied only on a pointer type\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 252, __PRETTY_FUNCTION__)); | |||
253 | return ValidArg; | |||
254 | } | |||
255 | }; | |||
256 | ||||
257 | // Represents a buffer argument with an additional size constraint. The | |||
258 | // constraint may be a concrete value, or a symbolic value in an argument. | |||
259 | // Example 1. Concrete value as the minimum buffer size. | |||
260 | // char *asctime_r(const struct tm *restrict tm, char *restrict buf); | |||
261 | // // `buf` size must be at least 26 bytes according the POSIX standard. | |||
262 | // Example 2. Argument as a buffer size. | |||
263 | // ctime_s(char *buffer, rsize_t bufsz, const time_t *time); | |||
264 | // Example 3. The size is computed as a multiplication of other args. | |||
265 | // size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); | |||
266 | // // Here, ptr is the buffer, and its minimum size is `size * nmemb`. | |||
267 | class BufferSizeConstraint : public ValueConstraint { | |||
268 | // The concrete value which is the minimum size for the buffer. | |||
269 | llvm::Optional<llvm::APSInt> ConcreteSize; | |||
270 | // The argument which holds the size of the buffer. | |||
271 | llvm::Optional<ArgNo> SizeArgN; | |||
272 | // The argument which is a multiplier to size. This is set in case of | |||
273 | // `fread` like functions where the size is computed as a multiplication of | |||
274 | // two arguments. | |||
275 | llvm::Optional<ArgNo> SizeMultiplierArgN; | |||
276 | // The operator we use in apply. This is negated in negate(). | |||
277 | BinaryOperator::Opcode Op = BO_LE; | |||
278 | ||||
279 | public: | |||
280 | StringRef getName() const override { return "BufferSize"; } | |||
281 | BufferSizeConstraint(ArgNo Buffer, llvm::APSInt BufMinSize) | |||
282 | : ValueConstraint(Buffer), ConcreteSize(BufMinSize) {} | |||
283 | BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize) | |||
284 | : ValueConstraint(Buffer), SizeArgN(BufSize) {} | |||
285 | BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize, ArgNo BufSizeMultiplier) | |||
286 | : ValueConstraint(Buffer), SizeArgN(BufSize), | |||
287 | SizeMultiplierArgN(BufSizeMultiplier) {} | |||
288 | ||||
289 | ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, | |||
290 | const Summary &Summary, | |||
291 | CheckerContext &C) const override { | |||
292 | SValBuilder &SvalBuilder = C.getSValBuilder(); | |||
293 | // The buffer argument. | |||
294 | SVal BufV = getArgSVal(Call, getArgNo()); | |||
295 | ||||
296 | // Get the size constraint. | |||
297 | const SVal SizeV = [this, &State, &Call, &Summary, &SvalBuilder]() { | |||
298 | if (ConcreteSize) { | |||
299 | return SVal(SvalBuilder.makeIntVal(*ConcreteSize)); | |||
300 | } else if (SizeArgN) { | |||
301 | // The size argument. | |||
302 | SVal SizeV = getArgSVal(Call, *SizeArgN); | |||
303 | // Multiply with another argument if given. | |||
304 | if (SizeMultiplierArgN) { | |||
305 | SVal SizeMulV = getArgSVal(Call, *SizeMultiplierArgN); | |||
306 | SizeV = SvalBuilder.evalBinOp(State, BO_Mul, SizeV, SizeMulV, | |||
307 | Summary.getArgType(*SizeArgN)); | |||
308 | } | |||
309 | return SizeV; | |||
310 | } else { | |||
311 | llvm_unreachable("The constraint must be either a concrete value or "::llvm::llvm_unreachable_internal("The constraint must be either a concrete value or " "encoded in an arguement.", "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 312) | |||
312 | "encoded in an arguement.")::llvm::llvm_unreachable_internal("The constraint must be either a concrete value or " "encoded in an arguement.", "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 312); | |||
313 | } | |||
314 | }(); | |||
315 | ||||
316 | // The dynamic size of the buffer argument, got from the analyzer engine. | |||
317 | SVal BufDynSize = getDynamicSizeWithOffset(State, BufV); | |||
318 | ||||
319 | SVal Feasible = SvalBuilder.evalBinOp(State, Op, SizeV, BufDynSize, | |||
320 | SvalBuilder.getContext().BoolTy); | |||
321 | if (auto F = Feasible.getAs<DefinedOrUnknownSVal>()) | |||
322 | return State->assume(*F, true); | |||
323 | ||||
324 | // We can get here only if the size argument or the dynamic size is | |||
325 | // undefined. But the dynamic size should never be undefined, only | |||
326 | // unknown. So, here, the size of the argument is undefined, i.e. we | |||
327 | // cannot apply the constraint. Actually, other checkers like | |||
328 | // CallAndMessage should catch this situation earlier, because we call a | |||
329 | // function with an uninitialized argument. | |||
330 | llvm_unreachable("Size argument or the dynamic size is Undefined")::llvm::llvm_unreachable_internal("Size argument or the dynamic size is Undefined" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 330); | |||
331 | } | |||
332 | ||||
333 | ValueConstraintPtr negate() const override { | |||
334 | BufferSizeConstraint Tmp(*this); | |||
335 | Tmp.Op = BinaryOperator::negateComparisonOp(Op); | |||
336 | return std::make_shared<BufferSizeConstraint>(Tmp); | |||
337 | } | |||
338 | ||||
339 | bool checkSpecificValidity(const FunctionDecl *FD) const override { | |||
340 | const bool ValidArg = getArgType(FD, ArgN)->isPointerType(); | |||
341 | assert(ValidArg &&((ValidArg && "This constraint should be applied only on a pointer type" ) ? static_cast<void> (0) : __assert_fail ("ValidArg && \"This constraint should be applied only on a pointer type\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 342, __PRETTY_FUNCTION__)) | |||
342 | "This constraint should be applied only on a pointer type")((ValidArg && "This constraint should be applied only on a pointer type" ) ? static_cast<void> (0) : __assert_fail ("ValidArg && \"This constraint should be applied only on a pointer type\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 342, __PRETTY_FUNCTION__)); | |||
343 | return ValidArg; | |||
344 | } | |||
345 | }; | |||
346 | ||||
347 | /// The complete list of constraints that defines a single branch. | |||
348 | typedef std::vector<ValueConstraintPtr> ConstraintSet; | |||
349 | ||||
350 | using ArgTypes = std::vector<Optional<QualType>>; | |||
351 | using RetType = Optional<QualType>; | |||
352 | ||||
353 | // A placeholder type, we use it whenever we do not care about the concrete | |||
354 | // type in a Signature. | |||
355 | const QualType Irrelevant{}; | |||
356 | bool static isIrrelevant(QualType T) { return T.isNull(); } | |||
357 | ||||
358 | // The signature of a function we want to describe with a summary. This is a | |||
359 | // concessive signature, meaning there may be irrelevant types in the | |||
360 | // signature which we do not check against a function with concrete types. | |||
361 | // All types in the spec need to be canonical. | |||
362 | class Signature { | |||
363 | using ArgQualTypes = std::vector<QualType>; | |||
364 | ArgQualTypes ArgTys; | |||
365 | QualType RetTy; | |||
366 | // True if any component type is not found by lookup. | |||
367 | bool Invalid = false; | |||
368 | ||||
369 | public: | |||
370 | // Construct a signature from optional types. If any of the optional types | |||
371 | // are not set then the signature will be invalid. | |||
372 | Signature(ArgTypes ArgTys, RetType RetTy) { | |||
373 | for (Optional<QualType> Arg : ArgTys) { | |||
374 | if (!Arg) { | |||
375 | Invalid = true; | |||
376 | return; | |||
377 | } else { | |||
378 | assertArgTypeSuitableForSignature(*Arg); | |||
379 | this->ArgTys.push_back(*Arg); | |||
380 | } | |||
381 | } | |||
382 | if (!RetTy) { | |||
383 | Invalid = true; | |||
384 | return; | |||
385 | } else { | |||
386 | assertRetTypeSuitableForSignature(*RetTy); | |||
387 | this->RetTy = *RetTy; | |||
388 | } | |||
389 | } | |||
390 | ||||
391 | bool isInvalid() const { return Invalid; } | |||
392 | bool matches(const FunctionDecl *FD) const; | |||
393 | ||||
394 | private: | |||
395 | static void assertArgTypeSuitableForSignature(QualType T) { | |||
396 | assert((T.isNull() || !T->isVoidType()) &&(((T.isNull() || !T->isVoidType()) && "We should have no void types in the spec" ) ? static_cast<void> (0) : __assert_fail ("(T.isNull() || !T->isVoidType()) && \"We should have no void types in the spec\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 397, __PRETTY_FUNCTION__)) | |||
397 | "We should have no void types in the spec")(((T.isNull() || !T->isVoidType()) && "We should have no void types in the spec" ) ? static_cast<void> (0) : __assert_fail ("(T.isNull() || !T->isVoidType()) && \"We should have no void types in the spec\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 397, __PRETTY_FUNCTION__)); | |||
398 | assert((T.isNull() || T.isCanonical()) &&(((T.isNull() || T.isCanonical()) && "We should only have canonical types in the spec" ) ? static_cast<void> (0) : __assert_fail ("(T.isNull() || T.isCanonical()) && \"We should only have canonical types in the spec\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 399, __PRETTY_FUNCTION__)) | |||
399 | "We should only have canonical types in the spec")(((T.isNull() || T.isCanonical()) && "We should only have canonical types in the spec" ) ? static_cast<void> (0) : __assert_fail ("(T.isNull() || T.isCanonical()) && \"We should only have canonical types in the spec\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 399, __PRETTY_FUNCTION__)); | |||
400 | } | |||
401 | static void assertRetTypeSuitableForSignature(QualType T) { | |||
402 | assert((T.isNull() || T.isCanonical()) &&(((T.isNull() || T.isCanonical()) && "We should only have canonical types in the spec" ) ? static_cast<void> (0) : __assert_fail ("(T.isNull() || T.isCanonical()) && \"We should only have canonical types in the spec\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 403, __PRETTY_FUNCTION__)) | |||
403 | "We should only have canonical types in the spec")(((T.isNull() || T.isCanonical()) && "We should only have canonical types in the spec" ) ? static_cast<void> (0) : __assert_fail ("(T.isNull() || T.isCanonical()) && \"We should only have canonical types in the spec\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 403, __PRETTY_FUNCTION__)); | |||
404 | } | |||
405 | }; | |||
406 | ||||
407 | static QualType getArgType(const FunctionDecl *FD, ArgNo ArgN) { | |||
408 | assert(FD && "Function must be set")((FD && "Function must be set") ? static_cast<void > (0) : __assert_fail ("FD && \"Function must be set\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 408, __PRETTY_FUNCTION__)); | |||
409 | QualType T = (ArgN == Ret) | |||
410 | ? FD->getReturnType().getCanonicalType() | |||
411 | : FD->getParamDecl(ArgN)->getType().getCanonicalType(); | |||
412 | return T; | |||
413 | } | |||
414 | ||||
415 | using Cases = std::vector<ConstraintSet>; | |||
416 | ||||
417 | /// A summary includes information about | |||
418 | /// * function prototype (signature) | |||
419 | /// * approach to invalidation, | |||
420 | /// * a list of branches - a list of list of ranges - | |||
421 | /// A branch represents a path in the exploded graph of a function (which | |||
422 | /// is a tree). So, a branch is a series of assumptions. In other words, | |||
423 | /// branches represent split states and additional assumptions on top of | |||
424 | /// the splitting assumption. | |||
425 | /// For example, consider the branches in `isalpha(x)` | |||
426 | /// Branch 1) | |||
427 | /// x is in range ['A', 'Z'] or in ['a', 'z'] | |||
428 | /// then the return value is not 0. (I.e. out-of-range [0, 0]) | |||
429 | /// Branch 2) | |||
430 | /// x is out-of-range ['A', 'Z'] and out-of-range ['a', 'z'] | |||
431 | /// then the return value is 0. | |||
432 | /// * a list of argument constraints, that must be true on every branch. | |||
433 | /// If these constraints are not satisfied that means a fatal error | |||
434 | /// usually resulting in undefined behaviour. | |||
435 | /// | |||
436 | /// Application of a summary: | |||
437 | /// The signature and argument constraints together contain information | |||
438 | /// about which functions are handled by the summary. The signature can use | |||
439 | /// "wildcards", i.e. Irrelevant types. Irrelevant type of a parameter in | |||
440 | /// a signature means that type is not compared to the type of the parameter | |||
441 | /// in the found FunctionDecl. Argument constraints may specify additional | |||
442 | /// rules for the given parameter's type, those rules are checked once the | |||
443 | /// signature is matched. | |||
444 | class Summary { | |||
445 | const InvalidationKind InvalidationKd; | |||
446 | Cases CaseConstraints; | |||
447 | ConstraintSet ArgConstraints; | |||
448 | ||||
449 | // The function to which the summary applies. This is set after lookup and | |||
450 | // match to the signature. | |||
451 | const FunctionDecl *FD = nullptr; | |||
452 | ||||
453 | public: | |||
454 | Summary(InvalidationKind InvalidationKd) : InvalidationKd(InvalidationKd) {} | |||
455 | ||||
456 | Summary &Case(ConstraintSet &&CS) { | |||
457 | CaseConstraints.push_back(std::move(CS)); | |||
458 | return *this; | |||
459 | } | |||
460 | Summary &Case(const ConstraintSet &CS) { | |||
461 | CaseConstraints.push_back(CS); | |||
462 | return *this; | |||
463 | } | |||
464 | Summary &ArgConstraint(ValueConstraintPtr VC) { | |||
465 | assert(VC->getArgNo() != Ret &&((VC->getArgNo() != Ret && "Arg constraint should not refer to the return value" ) ? static_cast<void> (0) : __assert_fail ("VC->getArgNo() != Ret && \"Arg constraint should not refer to the return value\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 466, __PRETTY_FUNCTION__)) | |||
466 | "Arg constraint should not refer to the return value")((VC->getArgNo() != Ret && "Arg constraint should not refer to the return value" ) ? static_cast<void> (0) : __assert_fail ("VC->getArgNo() != Ret && \"Arg constraint should not refer to the return value\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 466, __PRETTY_FUNCTION__)); | |||
467 | ArgConstraints.push_back(VC); | |||
468 | return *this; | |||
469 | } | |||
470 | ||||
471 | InvalidationKind getInvalidationKd() const { return InvalidationKd; } | |||
472 | const Cases &getCaseConstraints() const { return CaseConstraints; } | |||
473 | const ConstraintSet &getArgConstraints() const { return ArgConstraints; } | |||
474 | ||||
475 | QualType getArgType(ArgNo ArgN) const { | |||
476 | return StdLibraryFunctionsChecker::getArgType(FD, ArgN); | |||
477 | } | |||
478 | ||||
479 | // Returns true if the summary should be applied to the given function. | |||
480 | // And if yes then store the function declaration. | |||
481 | bool matchesAndSet(const Signature &Sign, const FunctionDecl *FD) { | |||
482 | bool Result = Sign.matches(FD) && validateByConstraints(FD); | |||
483 | if (Result) { | |||
484 | assert(!this->FD && "FD must not be set more than once")((!this->FD && "FD must not be set more than once" ) ? static_cast<void> (0) : __assert_fail ("!this->FD && \"FD must not be set more than once\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 484, __PRETTY_FUNCTION__)); | |||
485 | this->FD = FD; | |||
486 | } | |||
487 | return Result; | |||
488 | } | |||
489 | ||||
490 | private: | |||
491 | // Once we know the exact type of the function then do sanity check on all | |||
492 | // the given constraints. | |||
493 | bool validateByConstraints(const FunctionDecl *FD) const { | |||
494 | for (const ConstraintSet &Case : CaseConstraints) | |||
495 | for (const ValueConstraintPtr &Constraint : Case) | |||
496 | if (!Constraint->checkValidity(FD)) | |||
497 | return false; | |||
498 | for (const ValueConstraintPtr &Constraint : ArgConstraints) | |||
499 | if (!Constraint->checkValidity(FD)) | |||
500 | return false; | |||
501 | return true; | |||
502 | } | |||
503 | }; | |||
504 | ||||
505 | // The map of all functions supported by the checker. It is initialized | |||
506 | // lazily, and it doesn't change after initialization. | |||
507 | using FunctionSummaryMapType = llvm::DenseMap<const FunctionDecl *, Summary>; | |||
508 | mutable FunctionSummaryMapType FunctionSummaryMap; | |||
509 | ||||
510 | mutable std::unique_ptr<BugType> BT_InvalidArg; | |||
511 | ||||
512 | static SVal getArgSVal(const CallEvent &Call, ArgNo ArgN) { | |||
513 | return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgN); | |||
514 | } | |||
515 | ||||
516 | public: | |||
517 | void checkPreCall(const CallEvent &Call, CheckerContext &C) const; | |||
518 | void checkPostCall(const CallEvent &Call, CheckerContext &C) const; | |||
519 | bool evalCall(const CallEvent &Call, CheckerContext &C) const; | |||
520 | ||||
521 | enum CheckKind { | |||
522 | CK_StdCLibraryFunctionArgsChecker, | |||
523 | CK_StdCLibraryFunctionsTesterChecker, | |||
524 | CK_NumCheckKinds | |||
525 | }; | |||
526 | DefaultBool ChecksEnabled[CK_NumCheckKinds]; | |||
527 | CheckerNameRef CheckNames[CK_NumCheckKinds]; | |||
528 | ||||
529 | bool DisplayLoadedSummaries = false; | |||
530 | bool ModelPOSIX = false; | |||
531 | ||||
532 | private: | |||
533 | Optional<Summary> findFunctionSummary(const FunctionDecl *FD, | |||
534 | CheckerContext &C) const; | |||
535 | Optional<Summary> findFunctionSummary(const CallEvent &Call, | |||
536 | CheckerContext &C) const; | |||
537 | ||||
538 | void initFunctionSummaries(CheckerContext &C) const; | |||
539 | ||||
540 | void reportBug(const CallEvent &Call, ExplodedNode *N, | |||
541 | const ValueConstraint *VC, CheckerContext &C) const { | |||
542 | if (!ChecksEnabled[CK_StdCLibraryFunctionArgsChecker]) | |||
543 | return; | |||
544 | // TODO Add more detailed diagnostic. | |||
545 | std::string Msg = | |||
546 | (Twine("Function argument constraint is not satisfied, constraint: ") + | |||
547 | VC->getName().data() + ", ArgN: " + Twine(VC->getArgNo())) | |||
548 | .str(); | |||
549 | if (!BT_InvalidArg) | |||
550 | BT_InvalidArg = std::make_unique<BugType>( | |||
551 | CheckNames[CK_StdCLibraryFunctionArgsChecker], | |||
552 | "Unsatisfied argument constraints", categories::LogicError); | |||
553 | auto R = std::make_unique<PathSensitiveBugReport>(*BT_InvalidArg, Msg, N); | |||
554 | bugreporter::trackExpressionValue(N, Call.getArgExpr(VC->getArgNo()), *R); | |||
555 | ||||
556 | // Highlight the range of the argument that was violated. | |||
557 | R->addRange(Call.getArgSourceRange(VC->getArgNo())); | |||
558 | ||||
559 | C.emitReport(std::move(R)); | |||
560 | } | |||
561 | }; | |||
562 | ||||
563 | const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret = | |||
564 | std::numeric_limits<ArgNo>::max(); | |||
565 | ||||
566 | } // end of anonymous namespace | |||
567 | ||||
568 | ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsOutOfRange( | |||
569 | ProgramStateRef State, const CallEvent &Call, | |||
570 | const Summary &Summary) const { | |||
571 | if (Ranges.empty()) | |||
572 | return State; | |||
573 | ||||
574 | ProgramStateManager &Mgr = State->getStateManager(); | |||
575 | SValBuilder &SVB = Mgr.getSValBuilder(); | |||
576 | BasicValueFactory &BVF = SVB.getBasicValueFactory(); | |||
577 | ConstraintManager &CM = Mgr.getConstraintManager(); | |||
578 | QualType T = Summary.getArgType(getArgNo()); | |||
579 | SVal V = getArgSVal(Call, getArgNo()); | |||
580 | ||||
581 | if (auto N = V.getAs<NonLoc>()) { | |||
582 | const IntRangeVector &R = getRanges(); | |||
583 | size_t E = R.size(); | |||
584 | for (size_t I = 0; I != E; ++I) { | |||
585 | const llvm::APSInt &Min = BVF.getValue(R[I].first, T); | |||
586 | const llvm::APSInt &Max = BVF.getValue(R[I].second, T); | |||
587 | assert(Min <= Max)((Min <= Max) ? static_cast<void> (0) : __assert_fail ("Min <= Max", "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 587, __PRETTY_FUNCTION__)); | |||
588 | State = CM.assumeInclusiveRange(State, *N, Min, Max, false); | |||
589 | if (!State) | |||
590 | break; | |||
591 | } | |||
592 | } | |||
593 | ||||
594 | return State; | |||
595 | } | |||
596 | ||||
597 | ProgramStateRef StdLibraryFunctionsChecker::RangeConstraint::applyAsWithinRange( | |||
598 | ProgramStateRef State, const CallEvent &Call, | |||
599 | const Summary &Summary) const { | |||
600 | if (Ranges.empty()) | |||
601 | return State; | |||
602 | ||||
603 | ProgramStateManager &Mgr = State->getStateManager(); | |||
604 | SValBuilder &SVB = Mgr.getSValBuilder(); | |||
605 | BasicValueFactory &BVF = SVB.getBasicValueFactory(); | |||
606 | ConstraintManager &CM = Mgr.getConstraintManager(); | |||
607 | QualType T = Summary.getArgType(getArgNo()); | |||
608 | SVal V = getArgSVal(Call, getArgNo()); | |||
609 | ||||
610 | // "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R". | |||
611 | // We cut off [T_MIN, min(R) - 1] and [max(R) + 1, T_MAX] if necessary, | |||
612 | // and then cut away all holes in R one by one. | |||
613 | // | |||
614 | // E.g. consider a range list R as [A, B] and [C, D] | |||
615 | // -------+--------+------------------+------------+-----------> | |||
616 | // A B C D | |||
617 | // Then we assume that the value is not in [-inf, A - 1], | |||
618 | // then not in [D + 1, +inf], then not in [B + 1, C - 1] | |||
619 | if (auto N = V.getAs<NonLoc>()) { | |||
620 | const IntRangeVector &R = getRanges(); | |||
621 | size_t E = R.size(); | |||
622 | ||||
623 | const llvm::APSInt &MinusInf = BVF.getMinValue(T); | |||
624 | const llvm::APSInt &PlusInf = BVF.getMaxValue(T); | |||
625 | ||||
626 | const llvm::APSInt &Left = BVF.getValue(R[0].first - 1ULL, T); | |||
627 | if (Left != PlusInf) { | |||
628 | assert(MinusInf <= Left)((MinusInf <= Left) ? static_cast<void> (0) : __assert_fail ("MinusInf <= Left", "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 628, __PRETTY_FUNCTION__)); | |||
629 | State = CM.assumeInclusiveRange(State, *N, MinusInf, Left, false); | |||
630 | if (!State) | |||
631 | return nullptr; | |||
632 | } | |||
633 | ||||
634 | const llvm::APSInt &Right = BVF.getValue(R[E - 1].second + 1ULL, T); | |||
635 | if (Right != MinusInf) { | |||
636 | assert(Right <= PlusInf)((Right <= PlusInf) ? static_cast<void> (0) : __assert_fail ("Right <= PlusInf", "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 636, __PRETTY_FUNCTION__)); | |||
637 | State = CM.assumeInclusiveRange(State, *N, Right, PlusInf, false); | |||
638 | if (!State) | |||
639 | return nullptr; | |||
640 | } | |||
641 | ||||
642 | for (size_t I = 1; I != E; ++I) { | |||
643 | const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, T); | |||
644 | const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, T); | |||
645 | if (Min <= Max) { | |||
646 | State = CM.assumeInclusiveRange(State, *N, Min, Max, false); | |||
647 | if (!State) | |||
648 | return nullptr; | |||
649 | } | |||
650 | } | |||
651 | } | |||
652 | ||||
653 | return State; | |||
654 | } | |||
655 | ||||
656 | ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( | |||
657 | ProgramStateRef State, const CallEvent &Call, const Summary &Summary, | |||
658 | CheckerContext &C) const { | |||
659 | ||||
660 | ProgramStateManager &Mgr = State->getStateManager(); | |||
661 | SValBuilder &SVB = Mgr.getSValBuilder(); | |||
662 | QualType CondT = SVB.getConditionType(); | |||
663 | QualType T = Summary.getArgType(getArgNo()); | |||
664 | SVal V = getArgSVal(Call, getArgNo()); | |||
665 | ||||
666 | BinaryOperator::Opcode Op = getOpcode(); | |||
667 | ArgNo OtherArg = getOtherArgNo(); | |||
668 | SVal OtherV = getArgSVal(Call, OtherArg); | |||
669 | QualType OtherT = Summary.getArgType(OtherArg); | |||
670 | // Note: we avoid integral promotion for comparison. | |||
671 | OtherV = SVB.evalCast(OtherV, T, OtherT); | |||
672 | if (auto CompV = SVB.evalBinOp(State, Op, V, OtherV, CondT) | |||
673 | .getAs<DefinedOrUnknownSVal>()) | |||
674 | State = State->assume(*CompV, true); | |||
675 | return State; | |||
676 | } | |||
677 | ||||
678 | void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, | |||
679 | CheckerContext &C) const { | |||
680 | Optional<Summary> FoundSummary = findFunctionSummary(Call, C); | |||
681 | if (!FoundSummary) | |||
682 | return; | |||
683 | ||||
684 | const Summary &Summary = *FoundSummary; | |||
685 | ProgramStateRef State = C.getState(); | |||
686 | ||||
687 | ProgramStateRef NewState = State; | |||
688 | for (const ValueConstraintPtr &Constraint : Summary.getArgConstraints()) { | |||
689 | ProgramStateRef SuccessSt = Constraint->apply(NewState, Call, Summary, C); | |||
690 | ProgramStateRef FailureSt = | |||
691 | Constraint->negate()->apply(NewState, Call, Summary, C); | |||
692 | // The argument constraint is not satisfied. | |||
693 | if (FailureSt && !SuccessSt) { | |||
694 | if (ExplodedNode *N = C.generateErrorNode(NewState)) | |||
695 | reportBug(Call, N, Constraint.get(), C); | |||
696 | break; | |||
697 | } else { | |||
698 | // We will apply the constraint even if we cannot reason about the | |||
699 | // argument. This means both SuccessSt and FailureSt can be true. If we | |||
700 | // weren't applying the constraint that would mean that symbolic | |||
701 | // execution continues on a code whose behaviour is undefined. | |||
702 | assert(SuccessSt)((SuccessSt) ? static_cast<void> (0) : __assert_fail ("SuccessSt" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 702, __PRETTY_FUNCTION__)); | |||
703 | NewState = SuccessSt; | |||
704 | } | |||
705 | } | |||
706 | if (NewState && NewState != State) | |||
707 | C.addTransition(NewState); | |||
708 | } | |||
709 | ||||
710 | void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, | |||
711 | CheckerContext &C) const { | |||
712 | Optional<Summary> FoundSummary = findFunctionSummary(Call, C); | |||
713 | if (!FoundSummary) | |||
714 | return; | |||
715 | ||||
716 | // Now apply the constraints. | |||
717 | const Summary &Summary = *FoundSummary; | |||
718 | ProgramStateRef State = C.getState(); | |||
719 | ||||
720 | // Apply case/branch specifications. | |||
721 | for (const ConstraintSet &Case : Summary.getCaseConstraints()) { | |||
722 | ProgramStateRef NewState = State; | |||
723 | for (const ValueConstraintPtr &Constraint : Case) { | |||
724 | NewState = Constraint->apply(NewState, Call, Summary, C); | |||
725 | if (!NewState) | |||
726 | break; | |||
727 | } | |||
728 | ||||
729 | if (NewState && NewState != State) | |||
730 | C.addTransition(NewState); | |||
731 | } | |||
732 | } | |||
733 | ||||
734 | bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call, | |||
735 | CheckerContext &C) const { | |||
736 | Optional<Summary> FoundSummary = findFunctionSummary(Call, C); | |||
737 | if (!FoundSummary) | |||
| ||||
738 | return false; | |||
739 | ||||
740 | const Summary &Summary = *FoundSummary; | |||
741 | switch (Summary.getInvalidationKd()) { | |||
742 | case EvalCallAsPure: { | |||
743 | ProgramStateRef State = C.getState(); | |||
744 | const LocationContext *LC = C.getLocationContext(); | |||
745 | const auto *CE = cast_or_null<CallExpr>(Call.getOriginExpr()); | |||
746 | SVal V = C.getSValBuilder().conjureSymbolVal( | |||
747 | CE, LC, CE->getType().getCanonicalType(), C.blockCount()); | |||
| ||||
748 | State = State->BindExpr(CE, LC, V); | |||
749 | C.addTransition(State); | |||
750 | return true; | |||
751 | } | |||
752 | case NoEvalCall: | |||
753 | // Summary tells us to avoid performing eval::Call. The function is possibly | |||
754 | // evaluated by another checker, or evaluated conservatively. | |||
755 | return false; | |||
756 | } | |||
757 | llvm_unreachable("Unknown invalidation kind!")::llvm::llvm_unreachable_internal("Unknown invalidation kind!" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 757); | |||
758 | } | |||
759 | ||||
760 | bool StdLibraryFunctionsChecker::Signature::matches( | |||
761 | const FunctionDecl *FD) const { | |||
762 | assert(!isInvalid())((!isInvalid()) ? static_cast<void> (0) : __assert_fail ("!isInvalid()", "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 762, __PRETTY_FUNCTION__)); | |||
763 | // Check the number of arguments. | |||
764 | if (FD->param_size() != ArgTys.size()) | |||
765 | return false; | |||
766 | ||||
767 | // The "restrict" keyword is illegal in C++, however, many libc | |||
768 | // implementations use the "__restrict" compiler intrinsic in functions | |||
769 | // prototypes. The "__restrict" keyword qualifies a type as a restricted type | |||
770 | // even in C++. | |||
771 | // In case of any non-C99 languages, we don't want to match based on the | |||
772 | // restrict qualifier because we cannot know if the given libc implementation | |||
773 | // qualifies the paramter type or not. | |||
774 | auto RemoveRestrict = [&FD](QualType T) { | |||
775 | if (!FD->getASTContext().getLangOpts().C99) | |||
776 | T.removeLocalRestrict(); | |||
777 | return T; | |||
778 | }; | |||
779 | ||||
780 | // Check the return type. | |||
781 | if (!isIrrelevant(RetTy)) { | |||
782 | QualType FDRetTy = RemoveRestrict(FD->getReturnType().getCanonicalType()); | |||
783 | if (RetTy != FDRetTy) | |||
784 | return false; | |||
785 | } | |||
786 | ||||
787 | // Check the argument types. | |||
788 | for (size_t I = 0, E = ArgTys.size(); I != E; ++I) { | |||
789 | QualType ArgTy = ArgTys[I]; | |||
790 | if (isIrrelevant(ArgTy)) | |||
791 | continue; | |||
792 | QualType FDArgTy = | |||
793 | RemoveRestrict(FD->getParamDecl(I)->getType().getCanonicalType()); | |||
794 | if (ArgTy != FDArgTy) | |||
795 | return false; | |||
796 | } | |||
797 | ||||
798 | return true; | |||
799 | } | |||
800 | ||||
801 | Optional<StdLibraryFunctionsChecker::Summary> | |||
802 | StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD, | |||
803 | CheckerContext &C) const { | |||
804 | if (!FD) | |||
805 | return None; | |||
806 | ||||
807 | initFunctionSummaries(C); | |||
808 | ||||
809 | auto FSMI = FunctionSummaryMap.find(FD->getCanonicalDecl()); | |||
810 | if (FSMI == FunctionSummaryMap.end()) | |||
811 | return None; | |||
812 | return FSMI->second; | |||
813 | } | |||
814 | ||||
815 | Optional<StdLibraryFunctionsChecker::Summary> | |||
816 | StdLibraryFunctionsChecker::findFunctionSummary(const CallEvent &Call, | |||
817 | CheckerContext &C) const { | |||
818 | const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); | |||
819 | if (!FD) | |||
820 | return None; | |||
821 | return findFunctionSummary(FD, C); | |||
822 | } | |||
823 | ||||
824 | void StdLibraryFunctionsChecker::initFunctionSummaries( | |||
825 | CheckerContext &C) const { | |||
826 | if (!FunctionSummaryMap.empty()) | |||
827 | return; | |||
828 | ||||
829 | SValBuilder &SVB = C.getSValBuilder(); | |||
830 | BasicValueFactory &BVF = SVB.getBasicValueFactory(); | |||
831 | const ASTContext &ACtx = BVF.getContext(); | |||
832 | ||||
833 | // Helper class to lookup a type by its name. | |||
834 | class LookupType { | |||
835 | const ASTContext &ACtx; | |||
836 | ||||
837 | public: | |||
838 | LookupType(const ASTContext &ACtx) : ACtx(ACtx) {} | |||
839 | ||||
840 | // Find the type. If not found then the optional is not set. | |||
841 | llvm::Optional<QualType> operator()(StringRef Name) { | |||
842 | IdentifierInfo &II = ACtx.Idents.get(Name); | |||
843 | auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); | |||
844 | if (LookupRes.size() == 0) | |||
845 | return None; | |||
846 | ||||
847 | // Prioritze typedef declarations. | |||
848 | // This is needed in case of C struct typedefs. E.g.: | |||
849 | // typedef struct FILE FILE; | |||
850 | // In this case, we have a RecordDecl 'struct FILE' with the name 'FILE' | |||
851 | // and we have a TypedefDecl with the name 'FILE'. | |||
852 | for (Decl *D : LookupRes) | |||
853 | if (auto *TD = dyn_cast<TypedefNameDecl>(D)) | |||
854 | return ACtx.getTypeDeclType(TD).getCanonicalType(); | |||
855 | ||||
856 | // Find the first TypeDecl. | |||
857 | // There maybe cases when a function has the same name as a struct. | |||
858 | // E.g. in POSIX: `struct stat` and the function `stat()`: | |||
859 | // int stat(const char *restrict path, struct stat *restrict buf); | |||
860 | for (Decl *D : LookupRes) | |||
861 | if (auto *TD = dyn_cast<TypeDecl>(D)) | |||
862 | return ACtx.getTypeDeclType(TD).getCanonicalType(); | |||
863 | return None; | |||
864 | } | |||
865 | } lookupTy(ACtx); | |||
866 | ||||
867 | // Below are auxiliary classes to handle optional types that we get as a | |||
868 | // result of the lookup. | |||
869 | class GetRestrictTy { | |||
870 | const ASTContext &ACtx; | |||
871 | ||||
872 | public: | |||
873 | GetRestrictTy(const ASTContext &ACtx) : ACtx(ACtx) {} | |||
874 | QualType operator()(QualType Ty) { | |||
875 | return ACtx.getLangOpts().C99 ? ACtx.getRestrictType(Ty) : Ty; | |||
876 | } | |||
877 | Optional<QualType> operator()(Optional<QualType> Ty) { | |||
878 | if (Ty) | |||
879 | return operator()(*Ty); | |||
880 | return None; | |||
881 | } | |||
882 | } getRestrictTy(ACtx); | |||
883 | class GetPointerTy { | |||
884 | const ASTContext &ACtx; | |||
885 | ||||
886 | public: | |||
887 | GetPointerTy(const ASTContext &ACtx) : ACtx(ACtx) {} | |||
888 | QualType operator()(QualType Ty) { return ACtx.getPointerType(Ty); } | |||
889 | Optional<QualType> operator()(Optional<QualType> Ty) { | |||
890 | if (Ty) | |||
891 | return operator()(*Ty); | |||
892 | return None; | |||
893 | } | |||
894 | } getPointerTy(ACtx); | |||
895 | class { | |||
896 | public: | |||
897 | Optional<QualType> operator()(Optional<QualType> Ty) { | |||
898 | return Ty ? Optional<QualType>(Ty->withConst()) : None; | |||
899 | } | |||
900 | QualType operator()(QualType Ty) { return Ty.withConst(); } | |||
901 | } getConstTy; | |||
902 | class GetMaxValue { | |||
903 | BasicValueFactory &BVF; | |||
904 | ||||
905 | public: | |||
906 | GetMaxValue(BasicValueFactory &BVF) : BVF(BVF) {} | |||
907 | Optional<RangeInt> operator()(QualType Ty) { | |||
908 | return BVF.getMaxValue(Ty).getLimitedValue(); | |||
909 | } | |||
910 | Optional<RangeInt> operator()(Optional<QualType> Ty) { | |||
911 | if (Ty) { | |||
912 | return operator()(*Ty); | |||
913 | } | |||
914 | return None; | |||
915 | } | |||
916 | } getMaxValue(BVF); | |||
917 | ||||
918 | // These types are useful for writing specifications quickly, | |||
919 | // New specifications should probably introduce more types. | |||
920 | // Some types are hard to obtain from the AST, eg. "ssize_t". | |||
921 | // In such cases it should be possible to provide multiple variants | |||
922 | // of function summary for common cases (eg. ssize_t could be int or long | |||
923 | // or long long, so three summary variants would be enough). | |||
924 | // Of course, function variants are also useful for C++ overloads. | |||
925 | const QualType VoidTy = ACtx.VoidTy; | |||
926 | const QualType CharTy = ACtx.CharTy; | |||
927 | const QualType WCharTy = ACtx.WCharTy; | |||
928 | const QualType IntTy = ACtx.IntTy; | |||
929 | const QualType UnsignedIntTy = ACtx.UnsignedIntTy; | |||
930 | const QualType LongTy = ACtx.LongTy; | |||
931 | const QualType SizeTy = ACtx.getSizeType(); | |||
932 | ||||
933 | const QualType VoidPtrTy = getPointerTy(VoidTy); // void * | |||
934 | const QualType IntPtrTy = getPointerTy(IntTy); // int * | |||
935 | const QualType UnsignedIntPtrTy = | |||
936 | getPointerTy(UnsignedIntTy); // unsigned int * | |||
937 | const QualType VoidPtrRestrictTy = getRestrictTy(VoidPtrTy); | |||
938 | const QualType ConstVoidPtrTy = | |||
939 | getPointerTy(getConstTy(VoidTy)); // const void * | |||
940 | const QualType CharPtrTy = getPointerTy(CharTy); // char * | |||
941 | const QualType CharPtrRestrictTy = getRestrictTy(CharPtrTy); | |||
942 | const QualType ConstCharPtrTy = | |||
943 | getPointerTy(getConstTy(CharTy)); // const char * | |||
944 | const QualType ConstCharPtrRestrictTy = getRestrictTy(ConstCharPtrTy); | |||
945 | const QualType Wchar_tPtrTy = getPointerTy(WCharTy); // wchar_t * | |||
946 | const QualType ConstWchar_tPtrTy = | |||
947 | getPointerTy(getConstTy(WCharTy)); // const wchar_t * | |||
948 | const QualType ConstVoidPtrRestrictTy = getRestrictTy(ConstVoidPtrTy); | |||
949 | const QualType SizePtrTy = getPointerTy(SizeTy); | |||
950 | const QualType SizePtrRestrictTy = getRestrictTy(SizePtrTy); | |||
951 | ||||
952 | const RangeInt IntMax = BVF.getMaxValue(IntTy).getLimitedValue(); | |||
953 | const RangeInt UnsignedIntMax = | |||
954 | BVF.getMaxValue(UnsignedIntTy).getLimitedValue(); | |||
955 | const RangeInt LongMax = BVF.getMaxValue(LongTy).getLimitedValue(); | |||
956 | const RangeInt SizeMax = BVF.getMaxValue(SizeTy).getLimitedValue(); | |||
957 | ||||
958 | // Set UCharRangeMax to min of int or uchar maximum value. | |||
959 | // The C standard states that the arguments of functions like isalpha must | |||
960 | // be representable as an unsigned char. Their type is 'int', so the max | |||
961 | // value of the argument should be min(UCharMax, IntMax). This just happen | |||
962 | // to be true for commonly used and well tested instruction set | |||
963 | // architectures, but not for others. | |||
964 | const RangeInt UCharRangeMax = | |||
965 | std::min(BVF.getMaxValue(ACtx.UnsignedCharTy).getLimitedValue(), IntMax); | |||
966 | ||||
967 | // The platform dependent value of EOF. | |||
968 | // Try our best to parse this from the Preprocessor, otherwise fallback to -1. | |||
969 | const auto EOFv = [&C]() -> RangeInt { | |||
970 | if (const llvm::Optional<int> OptInt = | |||
971 | tryExpandAsInteger("EOF", C.getPreprocessor())) | |||
972 | return *OptInt; | |||
973 | return -1; | |||
974 | }(); | |||
975 | ||||
976 | // Auxiliary class to aid adding summaries to the summary map. | |||
977 | struct AddToFunctionSummaryMap { | |||
978 | const ASTContext &ACtx; | |||
979 | FunctionSummaryMapType ⤅ | |||
980 | bool DisplayLoadedSummaries; | |||
981 | AddToFunctionSummaryMap(const ASTContext &ACtx, FunctionSummaryMapType &FSM, | |||
982 | bool DisplayLoadedSummaries) | |||
983 | : ACtx(ACtx), Map(FSM), DisplayLoadedSummaries(DisplayLoadedSummaries) { | |||
984 | } | |||
985 | ||||
986 | // Add a summary to a FunctionDecl found by lookup. The lookup is performed | |||
987 | // by the given Name, and in the global scope. The summary will be attached | |||
988 | // to the found FunctionDecl only if the signatures match. | |||
989 | // | |||
990 | // Returns true if the summary has been added, false otherwise. | |||
991 | bool operator()(StringRef Name, Signature Sign, Summary Sum) { | |||
992 | if (Sign.isInvalid()) | |||
993 | return false; | |||
994 | IdentifierInfo &II = ACtx.Idents.get(Name); | |||
995 | auto LookupRes = ACtx.getTranslationUnitDecl()->lookup(&II); | |||
996 | if (LookupRes.size() == 0) | |||
997 | return false; | |||
998 | for (Decl *D : LookupRes) { | |||
999 | if (auto *FD = dyn_cast<FunctionDecl>(D)) { | |||
1000 | if (Sum.matchesAndSet(Sign, FD)) { | |||
1001 | auto Res = Map.insert({FD->getCanonicalDecl(), Sum}); | |||
1002 | assert(Res.second && "Function already has a summary set!")((Res.second && "Function already has a summary set!" ) ? static_cast<void> (0) : __assert_fail ("Res.second && \"Function already has a summary set!\"" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp" , 1002, __PRETTY_FUNCTION__)); | |||
1003 | (void)Res; | |||
1004 | if (DisplayLoadedSummaries) { | |||
1005 | llvm::errs() << "Loaded summary for: "; | |||
1006 | FD->print(llvm::errs()); | |||
1007 | llvm::errs() << "\n"; | |||
1008 | } | |||
1009 | return true; | |||
1010 | } | |||
1011 | } | |||
1012 | } | |||
1013 | return false; | |||
1014 | } | |||
1015 | // Add the same summary for different names with the Signature explicitly | |||
1016 | // given. | |||
1017 | void operator()(std::vector<StringRef> Names, Signature Sign, Summary Sum) { | |||
1018 | for (StringRef Name : Names) | |||
1019 | operator()(Name, Sign, Sum); | |||
1020 | } | |||
1021 | } addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries); | |||
1022 | ||||
1023 | // Below are helpers functions to create the summaries. | |||
1024 | auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, | |||
1025 | IntRangeVector Ranges) { | |||
1026 | return std::make_shared<RangeConstraint>(ArgN, Kind, Ranges); | |||
1027 | }; | |||
1028 | auto BufferSize = [](auto... Args) { | |||
1029 | return std::make_shared<BufferSizeConstraint>(Args...); | |||
1030 | }; | |||
1031 | struct { | |||
1032 | auto operator()(RangeKind Kind, IntRangeVector Ranges) { | |||
1033 | return std::make_shared<RangeConstraint>(Ret, Kind, Ranges); | |||
1034 | } | |||
1035 | auto operator()(BinaryOperator::Opcode Op, ArgNo OtherArgN) { | |||
1036 | return std::make_shared<ComparisonConstraint>(Ret, Op, OtherArgN); | |||
1037 | } | |||
1038 | } ReturnValueCondition; | |||
1039 | struct { | |||
1040 | auto operator()(RangeInt b, RangeInt e) { | |||
1041 | return IntRangeVector{std::pair<RangeInt, RangeInt>{b, e}}; | |||
1042 | } | |||
1043 | auto operator()(RangeInt b, Optional<RangeInt> e) { | |||
1044 | if (e) | |||
1045 | return IntRangeVector{std::pair<RangeInt, RangeInt>{b, *e}}; | |||
1046 | return IntRangeVector{}; | |||
1047 | } | |||
1048 | auto operator()(std::pair<RangeInt, RangeInt> i0, | |||
1049 | std::pair<RangeInt, Optional<RangeInt>> i1) { | |||
1050 | if (i1.second) | |||
1051 | return IntRangeVector{i0, {i1.first, *(i1.second)}}; | |||
1052 | return IntRangeVector{i0}; | |||
1053 | } | |||
1054 | } Range; | |||
1055 | auto SingleValue = [](RangeInt v) { | |||
1056 | return IntRangeVector{std::pair<RangeInt, RangeInt>{v, v}}; | |||
1057 | }; | |||
1058 | auto LessThanOrEq = BO_LE; | |||
1059 | auto NotNull = [&](ArgNo ArgN) { | |||
1060 | return std::make_shared<NotNullConstraint>(ArgN); | |||
1061 | }; | |||
1062 | ||||
1063 | Optional<QualType> FileTy = lookupTy("FILE"); | |||
1064 | Optional<QualType> FilePtrTy = getPointerTy(FileTy); | |||
1065 | Optional<QualType> FilePtrRestrictTy = getRestrictTy(FilePtrTy); | |||
1066 | ||||
1067 | // We are finally ready to define specifications for all supported functions. | |||
1068 | // | |||
1069 | // Argument ranges should always cover all variants. If return value | |||
1070 | // is completely unknown, omit it from the respective range set. | |||
1071 | // | |||
1072 | // Every item in the list of range sets represents a particular | |||
1073 | // execution path the analyzer would need to explore once | |||
1074 | // the call is modeled - a new program state is constructed | |||
1075 | // for every range set, and each range line in the range set | |||
1076 | // corresponds to a specific constraint within this state. | |||
1077 | ||||
1078 | // The isascii() family of functions. | |||
1079 | // The behavior is undefined if the value of the argument is not | |||
1080 | // representable as unsigned char or is not equal to EOF. See e.g. C99 | |||
1081 | // 7.4.1.2 The isalpha function (p: 181-182). | |||
1082 | addToFunctionSummaryMap( | |||
1083 | "isalnum", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1084 | Summary(EvalCallAsPure) | |||
1085 | // Boils down to isupper() or islower() or isdigit(). | |||
1086 | .Case({ArgumentCondition(0U, WithinRange, | |||
1087 | {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}}), | |||
1088 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1089 | // The locale-specific range. | |||
1090 | // No post-condition. We are completely unaware of | |||
1091 | // locale-specific return values. | |||
1092 | .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) | |||
1093 | .Case( | |||
1094 | {ArgumentCondition( | |||
1095 | 0U, OutOfRange, | |||
1096 | {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), | |||
1097 | ReturnValueCondition(WithinRange, SingleValue(0))}) | |||
1098 | .ArgConstraint(ArgumentCondition( | |||
1099 | 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); | |||
1100 | addToFunctionSummaryMap( | |||
1101 | "isalpha", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1102 | Summary(EvalCallAsPure) | |||
1103 | .Case({ArgumentCondition(0U, WithinRange, {{'A', 'Z'}, {'a', 'z'}}), | |||
1104 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1105 | // The locale-specific range. | |||
1106 | .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) | |||
1107 | .Case({ArgumentCondition( | |||
1108 | 0U, OutOfRange, | |||
1109 | {{'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), | |||
1110 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1111 | addToFunctionSummaryMap( | |||
1112 | "isascii", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1113 | Summary(EvalCallAsPure) | |||
1114 | .Case({ArgumentCondition(0U, WithinRange, Range(0, 127)), | |||
1115 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1116 | .Case({ArgumentCondition(0U, OutOfRange, Range(0, 127)), | |||
1117 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1118 | addToFunctionSummaryMap( | |||
1119 | "isblank", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1120 | Summary(EvalCallAsPure) | |||
1121 | .Case({ArgumentCondition(0U, WithinRange, {{'\t', '\t'}, {' ', ' '}}), | |||
1122 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1123 | .Case({ArgumentCondition(0U, OutOfRange, {{'\t', '\t'}, {' ', ' '}}), | |||
1124 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1125 | addToFunctionSummaryMap( | |||
1126 | "iscntrl", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1127 | Summary(EvalCallAsPure) | |||
1128 | .Case({ArgumentCondition(0U, WithinRange, {{0, 32}, {127, 127}}), | |||
1129 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1130 | .Case({ArgumentCondition(0U, OutOfRange, {{0, 32}, {127, 127}}), | |||
1131 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1132 | addToFunctionSummaryMap( | |||
1133 | "isdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1134 | Summary(EvalCallAsPure) | |||
1135 | .Case({ArgumentCondition(0U, WithinRange, Range('0', '9')), | |||
1136 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1137 | .Case({ArgumentCondition(0U, OutOfRange, Range('0', '9')), | |||
1138 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1139 | addToFunctionSummaryMap( | |||
1140 | "isgraph", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1141 | Summary(EvalCallAsPure) | |||
1142 | .Case({ArgumentCondition(0U, WithinRange, Range(33, 126)), | |||
1143 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1144 | .Case({ArgumentCondition(0U, OutOfRange, Range(33, 126)), | |||
1145 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1146 | addToFunctionSummaryMap( | |||
1147 | "islower", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1148 | Summary(EvalCallAsPure) | |||
1149 | // Is certainly lowercase. | |||
1150 | .Case({ArgumentCondition(0U, WithinRange, Range('a', 'z')), | |||
1151 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1152 | // Is ascii but not lowercase. | |||
1153 | .Case({ArgumentCondition(0U, WithinRange, Range(0, 127)), | |||
1154 | ArgumentCondition(0U, OutOfRange, Range('a', 'z')), | |||
1155 | ReturnValueCondition(WithinRange, SingleValue(0))}) | |||
1156 | // The locale-specific range. | |||
1157 | .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) | |||
1158 | // Is not an unsigned char. | |||
1159 | .Case({ArgumentCondition(0U, OutOfRange, Range(0, UCharRangeMax)), | |||
1160 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1161 | addToFunctionSummaryMap( | |||
1162 | "isprint", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1163 | Summary(EvalCallAsPure) | |||
1164 | .Case({ArgumentCondition(0U, WithinRange, Range(32, 126)), | |||
1165 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1166 | .Case({ArgumentCondition(0U, OutOfRange, Range(32, 126)), | |||
1167 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1168 | addToFunctionSummaryMap( | |||
1169 | "ispunct", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1170 | Summary(EvalCallAsPure) | |||
1171 | .Case({ArgumentCondition( | |||
1172 | 0U, WithinRange, | |||
1173 | {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), | |||
1174 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1175 | .Case({ArgumentCondition( | |||
1176 | 0U, OutOfRange, | |||
1177 | {{'!', '/'}, {':', '@'}, {'[', '`'}, {'{', '~'}}), | |||
1178 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1179 | addToFunctionSummaryMap( | |||
1180 | "isspace", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1181 | Summary(EvalCallAsPure) | |||
1182 | // Space, '\f', '\n', '\r', '\t', '\v'. | |||
1183 | .Case({ArgumentCondition(0U, WithinRange, {{9, 13}, {' ', ' '}}), | |||
1184 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1185 | // The locale-specific range. | |||
1186 | .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) | |||
1187 | .Case({ArgumentCondition(0U, OutOfRange, | |||
1188 | {{9, 13}, {' ', ' '}, {128, UCharRangeMax}}), | |||
1189 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1190 | addToFunctionSummaryMap( | |||
1191 | "isupper", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1192 | Summary(EvalCallAsPure) | |||
1193 | // Is certainly uppercase. | |||
1194 | .Case({ArgumentCondition(0U, WithinRange, Range('A', 'Z')), | |||
1195 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1196 | // The locale-specific range. | |||
1197 | .Case({ArgumentCondition(0U, WithinRange, {{128, UCharRangeMax}})}) | |||
1198 | // Other. | |||
1199 | .Case({ArgumentCondition(0U, OutOfRange, | |||
1200 | {{'A', 'Z'}, {128, UCharRangeMax}}), | |||
1201 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1202 | addToFunctionSummaryMap( | |||
1203 | "isxdigit", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1204 | Summary(EvalCallAsPure) | |||
1205 | .Case({ArgumentCondition(0U, WithinRange, | |||
1206 | {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), | |||
1207 | ReturnValueCondition(OutOfRange, SingleValue(0))}) | |||
1208 | .Case({ArgumentCondition(0U, OutOfRange, | |||
1209 | {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), | |||
1210 | ReturnValueCondition(WithinRange, SingleValue(0))})); | |||
1211 | addToFunctionSummaryMap( | |||
1212 | "toupper", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1213 | Summary(EvalCallAsPure) | |||
1214 | .ArgConstraint(ArgumentCondition( | |||
1215 | 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); | |||
1216 | addToFunctionSummaryMap( | |||
1217 | "tolower", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1218 | Summary(EvalCallAsPure) | |||
1219 | .ArgConstraint(ArgumentCondition( | |||
1220 | 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); | |||
1221 | addToFunctionSummaryMap( | |||
1222 | "toascii", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1223 | Summary(EvalCallAsPure) | |||
1224 | .ArgConstraint(ArgumentCondition( | |||
1225 | 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); | |||
1226 | ||||
1227 | // The getc() family of functions that returns either a char or an EOF. | |||
1228 | addToFunctionSummaryMap( | |||
1229 | {"getc", "fgetc"}, Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), | |||
1230 | Summary(NoEvalCall) | |||
1231 | .Case({ReturnValueCondition(WithinRange, | |||
1232 | {{EOFv, EOFv}, {0, UCharRangeMax}})})); | |||
1233 | addToFunctionSummaryMap( | |||
1234 | "getchar", Signature(ArgTypes{}, RetType{IntTy}), | |||
1235 | Summary(NoEvalCall) | |||
1236 | .Case({ReturnValueCondition(WithinRange, | |||
1237 | {{EOFv, EOFv}, {0, UCharRangeMax}})})); | |||
1238 | ||||
1239 | // read()-like functions that never return more than buffer size. | |||
1240 | auto FreadSummary = | |||
1241 | Summary(NoEvalCall) | |||
1242 | .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), | |||
1243 | ReturnValueCondition(WithinRange, Range(0, SizeMax))}) | |||
1244 | .ArgConstraint(NotNull(ArgNo(0))) | |||
1245 | .ArgConstraint(NotNull(ArgNo(3))) | |||
1246 | .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), | |||
1247 | /*BufSizeMultiplier=*/ArgNo(2))); | |||
1248 | ||||
1249 | // size_t fread(void *restrict ptr, size_t size, size_t nitems, | |||
1250 | // FILE *restrict stream); | |||
1251 | addToFunctionSummaryMap( | |||
1252 | "fread", | |||
1253 | Signature(ArgTypes{VoidPtrRestrictTy, SizeTy, SizeTy, FilePtrRestrictTy}, | |||
1254 | RetType{SizeTy}), | |||
1255 | FreadSummary); | |||
1256 | // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, | |||
1257 | // FILE *restrict stream); | |||
1258 | addToFunctionSummaryMap("fwrite", | |||
1259 | Signature(ArgTypes{ConstVoidPtrRestrictTy, SizeTy, | |||
1260 | SizeTy, FilePtrRestrictTy}, | |||
1261 | RetType{SizeTy}), | |||
1262 | FreadSummary); | |||
1263 | ||||
1264 | Optional<QualType> Ssize_tTy = lookupTy("ssize_t"); | |||
1265 | Optional<RangeInt> Ssize_tMax = getMaxValue(Ssize_tTy); | |||
1266 | ||||
1267 | auto ReadSummary = | |||
1268 | Summary(NoEvalCall) | |||
1269 | .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), | |||
1270 | ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}); | |||
1271 | ||||
1272 | // FIXME these are actually defined by POSIX and not by the C standard, we | |||
1273 | // should handle them together with the rest of the POSIX functions. | |||
1274 | // ssize_t read(int fildes, void *buf, size_t nbyte); | |||
1275 | addToFunctionSummaryMap( | |||
1276 | "read", Signature(ArgTypes{IntTy, VoidPtrTy, SizeTy}, RetType{Ssize_tTy}), | |||
1277 | ReadSummary); | |||
1278 | // ssize_t write(int fildes, const void *buf, size_t nbyte); | |||
1279 | addToFunctionSummaryMap( | |||
1280 | "write", | |||
1281 | Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy}, RetType{Ssize_tTy}), | |||
1282 | ReadSummary); | |||
1283 | ||||
1284 | auto GetLineSummary = | |||
1285 | Summary(NoEvalCall) | |||
1286 | .Case({ReturnValueCondition(WithinRange, | |||
1287 | Range({-1, -1}, {1, Ssize_tMax}))}); | |||
1288 | ||||
1289 | QualType CharPtrPtrRestrictTy = getRestrictTy(getPointerTy(CharPtrTy)); | |||
1290 | ||||
1291 | // getline()-like functions either fail or read at least the delimiter. | |||
1292 | // FIXME these are actually defined by POSIX and not by the C standard, we | |||
1293 | // should handle them together with the rest of the POSIX functions. | |||
1294 | // ssize_t getline(char **restrict lineptr, size_t *restrict n, | |||
1295 | // FILE *restrict stream); | |||
1296 | addToFunctionSummaryMap( | |||
1297 | "getline", | |||
1298 | Signature( | |||
1299 | ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, FilePtrRestrictTy}, | |||
1300 | RetType{Ssize_tTy}), | |||
1301 | GetLineSummary); | |||
1302 | // ssize_t getdelim(char **restrict lineptr, size_t *restrict n, | |||
1303 | // int delimiter, FILE *restrict stream); | |||
1304 | addToFunctionSummaryMap( | |||
1305 | "getdelim", | |||
1306 | Signature(ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, IntTy, | |||
1307 | FilePtrRestrictTy}, | |||
1308 | RetType{Ssize_tTy}), | |||
1309 | GetLineSummary); | |||
1310 | ||||
1311 | if (ModelPOSIX) { | |||
1312 | ||||
1313 | // long a64l(const char *str64); | |||
1314 | addToFunctionSummaryMap( | |||
1315 | "a64l", Signature(ArgTypes{ConstCharPtrTy}, RetType{LongTy}), | |||
1316 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1317 | ||||
1318 | // char *l64a(long value); | |||
1319 | addToFunctionSummaryMap("l64a", | |||
1320 | Signature(ArgTypes{LongTy}, RetType{CharPtrTy}), | |||
1321 | Summary(NoEvalCall) | |||
1322 | .ArgConstraint(ArgumentCondition( | |||
1323 | 0, WithinRange, Range(0, LongMax)))); | |||
1324 | ||||
1325 | const auto ReturnsZeroOrMinusOne = | |||
1326 | ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, 0))}; | |||
1327 | const auto ReturnsFileDescriptor = | |||
1328 | ConstraintSet{ReturnValueCondition(WithinRange, Range(-1, IntMax))}; | |||
1329 | ||||
1330 | // int access(const char *pathname, int amode); | |||
1331 | addToFunctionSummaryMap( | |||
1332 | "access", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{IntTy}), | |||
1333 | Summary(NoEvalCall) | |||
1334 | .Case(ReturnsZeroOrMinusOne) | |||
1335 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1336 | ||||
1337 | // int faccessat(int dirfd, const char *pathname, int mode, int flags); | |||
1338 | addToFunctionSummaryMap( | |||
1339 | "faccessat", | |||
1340 | Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, IntTy}, | |||
1341 | RetType{IntTy}), | |||
1342 | Summary(NoEvalCall) | |||
1343 | .Case(ReturnsZeroOrMinusOne) | |||
1344 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1345 | ||||
1346 | // int dup(int fildes); | |||
1347 | addToFunctionSummaryMap("dup", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1348 | Summary(NoEvalCall) | |||
1349 | .Case(ReturnsFileDescriptor) | |||
1350 | .ArgConstraint(ArgumentCondition( | |||
1351 | 0, WithinRange, Range(0, IntMax)))); | |||
1352 | ||||
1353 | // int dup2(int fildes1, int filedes2); | |||
1354 | addToFunctionSummaryMap( | |||
1355 | "dup2", Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), | |||
1356 | Summary(NoEvalCall) | |||
1357 | .Case(ReturnsFileDescriptor) | |||
1358 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1359 | .ArgConstraint( | |||
1360 | ArgumentCondition(1, WithinRange, Range(0, IntMax)))); | |||
1361 | ||||
1362 | // int fdatasync(int fildes); | |||
1363 | addToFunctionSummaryMap("fdatasync", | |||
1364 | Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1365 | Summary(NoEvalCall) | |||
1366 | .Case(ReturnsZeroOrMinusOne) | |||
1367 | .ArgConstraint(ArgumentCondition( | |||
1368 | 0, WithinRange, Range(0, IntMax)))); | |||
1369 | ||||
1370 | // int fnmatch(const char *pattern, const char *string, int flags); | |||
1371 | addToFunctionSummaryMap( | |||
1372 | "fnmatch", | |||
1373 | Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy, IntTy}, | |||
1374 | RetType{IntTy}), | |||
1375 | Summary(EvalCallAsPure) | |||
1376 | .ArgConstraint(NotNull(ArgNo(0))) | |||
1377 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1378 | ||||
1379 | // int fsync(int fildes); | |||
1380 | addToFunctionSummaryMap("fsync", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1381 | Summary(NoEvalCall) | |||
1382 | .Case(ReturnsZeroOrMinusOne) | |||
1383 | .ArgConstraint(ArgumentCondition( | |||
1384 | 0, WithinRange, Range(0, IntMax)))); | |||
1385 | ||||
1386 | Optional<QualType> Off_tTy = lookupTy("off_t"); | |||
1387 | ||||
1388 | // int truncate(const char *path, off_t length); | |||
1389 | addToFunctionSummaryMap( | |||
1390 | "truncate", | |||
1391 | Signature(ArgTypes{ConstCharPtrTy, Off_tTy}, RetType{IntTy}), | |||
1392 | Summary(NoEvalCall) | |||
1393 | .Case(ReturnsZeroOrMinusOne) | |||
1394 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1395 | ||||
1396 | // int symlink(const char *oldpath, const char *newpath); | |||
1397 | addToFunctionSummaryMap( | |||
1398 | "symlink", | |||
1399 | Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}), | |||
1400 | Summary(NoEvalCall) | |||
1401 | .Case(ReturnsZeroOrMinusOne) | |||
1402 | .ArgConstraint(NotNull(ArgNo(0))) | |||
1403 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1404 | ||||
1405 | // int symlinkat(const char *oldpath, int newdirfd, const char *newpath); | |||
1406 | addToFunctionSummaryMap( | |||
1407 | "symlinkat", | |||
1408 | Signature(ArgTypes{ConstCharPtrTy, IntTy, ConstCharPtrTy}, | |||
1409 | RetType{IntTy}), | |||
1410 | Summary(NoEvalCall) | |||
1411 | .Case(ReturnsZeroOrMinusOne) | |||
1412 | .ArgConstraint(NotNull(ArgNo(0))) | |||
1413 | .ArgConstraint(ArgumentCondition(1, WithinRange, Range(0, IntMax))) | |||
1414 | .ArgConstraint(NotNull(ArgNo(2)))); | |||
1415 | ||||
1416 | // int lockf(int fd, int cmd, off_t len); | |||
1417 | addToFunctionSummaryMap( | |||
1418 | "lockf", Signature(ArgTypes{IntTy, IntTy, Off_tTy}, RetType{IntTy}), | |||
1419 | Summary(NoEvalCall) | |||
1420 | .Case(ReturnsZeroOrMinusOne) | |||
1421 | .ArgConstraint( | |||
1422 | ArgumentCondition(0, WithinRange, Range(0, IntMax)))); | |||
1423 | ||||
1424 | Optional<QualType> Mode_tTy = lookupTy("mode_t"); | |||
1425 | ||||
1426 | // int creat(const char *pathname, mode_t mode); | |||
1427 | addToFunctionSummaryMap( | |||
1428 | "creat", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), | |||
1429 | Summary(NoEvalCall) | |||
1430 | .Case(ReturnsFileDescriptor) | |||
1431 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1432 | ||||
1433 | // unsigned int sleep(unsigned int seconds); | |||
1434 | addToFunctionSummaryMap( | |||
1435 | "sleep", Signature(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}), | |||
1436 | Summary(NoEvalCall) | |||
1437 | .ArgConstraint( | |||
1438 | ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); | |||
1439 | ||||
1440 | Optional<QualType> DirTy = lookupTy("DIR"); | |||
1441 | Optional<QualType> DirPtrTy = getPointerTy(DirTy); | |||
1442 | ||||
1443 | // int dirfd(DIR *dirp); | |||
1444 | addToFunctionSummaryMap("dirfd", | |||
1445 | Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), | |||
1446 | Summary(NoEvalCall) | |||
1447 | .Case(ReturnsFileDescriptor) | |||
1448 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1449 | ||||
1450 | // unsigned int alarm(unsigned int seconds); | |||
1451 | addToFunctionSummaryMap( | |||
1452 | "alarm", Signature(ArgTypes{UnsignedIntTy}, RetType{UnsignedIntTy}), | |||
1453 | Summary(NoEvalCall) | |||
1454 | .ArgConstraint( | |||
1455 | ArgumentCondition(0, WithinRange, Range(0, UnsignedIntMax)))); | |||
1456 | ||||
1457 | // int closedir(DIR *dir); | |||
1458 | addToFunctionSummaryMap("closedir", | |||
1459 | Signature(ArgTypes{DirPtrTy}, RetType{IntTy}), | |||
1460 | Summary(NoEvalCall) | |||
1461 | .Case(ReturnsZeroOrMinusOne) | |||
1462 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1463 | ||||
1464 | // char *strdup(const char *s); | |||
1465 | addToFunctionSummaryMap( | |||
1466 | "strdup", Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}), | |||
1467 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1468 | ||||
1469 | // char *strndup(const char *s, size_t n); | |||
1470 | addToFunctionSummaryMap( | |||
1471 | "strndup", | |||
1472 | Signature(ArgTypes{ConstCharPtrTy, SizeTy}, RetType{CharPtrTy}), | |||
1473 | Summary(NoEvalCall) | |||
1474 | .ArgConstraint(NotNull(ArgNo(0))) | |||
1475 | .ArgConstraint( | |||
1476 | ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); | |||
1477 | ||||
1478 | // wchar_t *wcsdup(const wchar_t *s); | |||
1479 | addToFunctionSummaryMap( | |||
1480 | "wcsdup", Signature(ArgTypes{ConstWchar_tPtrTy}, RetType{Wchar_tPtrTy}), | |||
1481 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1482 | ||||
1483 | // int mkstemp(char *template); | |||
1484 | addToFunctionSummaryMap("mkstemp", | |||
1485 | Signature(ArgTypes{CharPtrTy}, RetType{IntTy}), | |||
1486 | Summary(NoEvalCall) | |||
1487 | .Case(ReturnsFileDescriptor) | |||
1488 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1489 | ||||
1490 | // char *mkdtemp(char *template); | |||
1491 | addToFunctionSummaryMap( | |||
1492 | "mkdtemp", Signature(ArgTypes{CharPtrTy}, RetType{CharPtrTy}), | |||
1493 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1494 | ||||
1495 | // char *getcwd(char *buf, size_t size); | |||
1496 | addToFunctionSummaryMap( | |||
1497 | "getcwd", Signature(ArgTypes{CharPtrTy, SizeTy}, RetType{CharPtrTy}), | |||
1498 | Summary(NoEvalCall) | |||
1499 | .ArgConstraint( | |||
1500 | ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); | |||
1501 | ||||
1502 | // int mkdir(const char *pathname, mode_t mode); | |||
1503 | addToFunctionSummaryMap( | |||
1504 | "mkdir", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), | |||
1505 | Summary(NoEvalCall) | |||
1506 | .Case(ReturnsZeroOrMinusOne) | |||
1507 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1508 | ||||
1509 | // int mkdirat(int dirfd, const char *pathname, mode_t mode); | |||
1510 | addToFunctionSummaryMap( | |||
1511 | "mkdirat", | |||
1512 | Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), | |||
1513 | Summary(NoEvalCall) | |||
1514 | .Case(ReturnsZeroOrMinusOne) | |||
1515 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1516 | ||||
1517 | Optional<QualType> Dev_tTy = lookupTy("dev_t"); | |||
1518 | ||||
1519 | // int mknod(const char *pathname, mode_t mode, dev_t dev); | |||
1520 | addToFunctionSummaryMap( | |||
1521 | "mknod", | |||
1522 | Signature(ArgTypes{ConstCharPtrTy, Mode_tTy, Dev_tTy}, RetType{IntTy}), | |||
1523 | Summary(NoEvalCall) | |||
1524 | .Case(ReturnsZeroOrMinusOne) | |||
1525 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1526 | ||||
1527 | // int mknodat(int dirfd, const char *pathname, mode_t mode, dev_t dev); | |||
1528 | addToFunctionSummaryMap( | |||
1529 | "mknodat", | |||
1530 | Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, Dev_tTy}, | |||
1531 | RetType{IntTy}), | |||
1532 | Summary(NoEvalCall) | |||
1533 | .Case(ReturnsZeroOrMinusOne) | |||
1534 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1535 | ||||
1536 | // int chmod(const char *path, mode_t mode); | |||
1537 | addToFunctionSummaryMap( | |||
1538 | "chmod", Signature(ArgTypes{ConstCharPtrTy, Mode_tTy}, RetType{IntTy}), | |||
1539 | Summary(NoEvalCall) | |||
1540 | .Case(ReturnsZeroOrMinusOne) | |||
1541 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1542 | ||||
1543 | // int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags); | |||
1544 | addToFunctionSummaryMap( | |||
1545 | "fchmodat", | |||
1546 | Signature(ArgTypes{IntTy, ConstCharPtrTy, Mode_tTy, IntTy}, | |||
1547 | RetType{IntTy}), | |||
1548 | Summary(NoEvalCall) | |||
1549 | .Case(ReturnsZeroOrMinusOne) | |||
1550 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1551 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1552 | ||||
1553 | // int fchmod(int fildes, mode_t mode); | |||
1554 | addToFunctionSummaryMap( | |||
1555 | "fchmod", Signature(ArgTypes{IntTy, Mode_tTy}, RetType{IntTy}), | |||
1556 | Summary(NoEvalCall) | |||
1557 | .Case(ReturnsZeroOrMinusOne) | |||
1558 | .ArgConstraint( | |||
1559 | ArgumentCondition(0, WithinRange, Range(0, IntMax)))); | |||
1560 | ||||
1561 | Optional<QualType> Uid_tTy = lookupTy("uid_t"); | |||
1562 | Optional<QualType> Gid_tTy = lookupTy("gid_t"); | |||
1563 | ||||
1564 | // int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, | |||
1565 | // int flags); | |||
1566 | addToFunctionSummaryMap( | |||
1567 | "fchownat", | |||
1568 | Signature(ArgTypes{IntTy, ConstCharPtrTy, Uid_tTy, Gid_tTy, IntTy}, | |||
1569 | RetType{IntTy}), | |||
1570 | Summary(NoEvalCall) | |||
1571 | .Case(ReturnsZeroOrMinusOne) | |||
1572 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1573 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1574 | ||||
1575 | // int chown(const char *path, uid_t owner, gid_t group); | |||
1576 | addToFunctionSummaryMap( | |||
1577 | "chown", | |||
1578 | Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), | |||
1579 | Summary(NoEvalCall) | |||
1580 | .Case(ReturnsZeroOrMinusOne) | |||
1581 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1582 | ||||
1583 | // int lchown(const char *path, uid_t owner, gid_t group); | |||
1584 | addToFunctionSummaryMap( | |||
1585 | "lchown", | |||
1586 | Signature(ArgTypes{ConstCharPtrTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), | |||
1587 | Summary(NoEvalCall) | |||
1588 | .Case(ReturnsZeroOrMinusOne) | |||
1589 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1590 | ||||
1591 | // int fchown(int fildes, uid_t owner, gid_t group); | |||
1592 | addToFunctionSummaryMap( | |||
1593 | "fchown", Signature(ArgTypes{IntTy, Uid_tTy, Gid_tTy}, RetType{IntTy}), | |||
1594 | Summary(NoEvalCall) | |||
1595 | .Case(ReturnsZeroOrMinusOne) | |||
1596 | .ArgConstraint( | |||
1597 | ArgumentCondition(0, WithinRange, Range(0, IntMax)))); | |||
1598 | ||||
1599 | // int rmdir(const char *pathname); | |||
1600 | addToFunctionSummaryMap("rmdir", | |||
1601 | Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), | |||
1602 | Summary(NoEvalCall) | |||
1603 | .Case(ReturnsZeroOrMinusOne) | |||
1604 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1605 | ||||
1606 | // int chdir(const char *path); | |||
1607 | addToFunctionSummaryMap("chdir", | |||
1608 | Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), | |||
1609 | Summary(NoEvalCall) | |||
1610 | .Case(ReturnsZeroOrMinusOne) | |||
1611 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1612 | ||||
1613 | // int link(const char *oldpath, const char *newpath); | |||
1614 | addToFunctionSummaryMap( | |||
1615 | "link", | |||
1616 | Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{IntTy}), | |||
1617 | Summary(NoEvalCall) | |||
1618 | .Case(ReturnsZeroOrMinusOne) | |||
1619 | .ArgConstraint(NotNull(ArgNo(0))) | |||
1620 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1621 | ||||
1622 | // int linkat(int fd1, const char *path1, int fd2, const char *path2, | |||
1623 | // int flag); | |||
1624 | addToFunctionSummaryMap( | |||
1625 | "linkat", | |||
1626 | Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy, IntTy}, | |||
1627 | RetType{IntTy}), | |||
1628 | Summary(NoEvalCall) | |||
1629 | .Case(ReturnsZeroOrMinusOne) | |||
1630 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1631 | .ArgConstraint(NotNull(ArgNo(1))) | |||
1632 | .ArgConstraint(ArgumentCondition(2, WithinRange, Range(0, IntMax))) | |||
1633 | .ArgConstraint(NotNull(ArgNo(3)))); | |||
1634 | ||||
1635 | // int unlink(const char *pathname); | |||
1636 | addToFunctionSummaryMap("unlink", | |||
1637 | Signature(ArgTypes{ConstCharPtrTy}, RetType{IntTy}), | |||
1638 | Summary(NoEvalCall) | |||
1639 | .Case(ReturnsZeroOrMinusOne) | |||
1640 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1641 | ||||
1642 | // int unlinkat(int fd, const char *path, int flag); | |||
1643 | addToFunctionSummaryMap( | |||
1644 | "unlinkat", | |||
1645 | Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy}, RetType{IntTy}), | |||
1646 | Summary(NoEvalCall) | |||
1647 | .Case(ReturnsZeroOrMinusOne) | |||
1648 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1649 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1650 | ||||
1651 | Optional<QualType> StructStatTy = lookupTy("stat"); | |||
1652 | Optional<QualType> StructStatPtrTy = getPointerTy(StructStatTy); | |||
1653 | Optional<QualType> StructStatPtrRestrictTy = getRestrictTy(StructStatPtrTy); | |||
1654 | ||||
1655 | // int fstat(int fd, struct stat *statbuf); | |||
1656 | addToFunctionSummaryMap( | |||
1657 | "fstat", Signature(ArgTypes{IntTy, StructStatPtrTy}, RetType{IntTy}), | |||
1658 | Summary(NoEvalCall) | |||
1659 | .Case(ReturnsZeroOrMinusOne) | |||
1660 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1661 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1662 | ||||
1663 | // int stat(const char *restrict path, struct stat *restrict buf); | |||
1664 | addToFunctionSummaryMap( | |||
1665 | "stat", | |||
1666 | Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy}, | |||
1667 | RetType{IntTy}), | |||
1668 | Summary(NoEvalCall) | |||
1669 | .Case(ReturnsZeroOrMinusOne) | |||
1670 | .ArgConstraint(NotNull(ArgNo(0))) | |||
1671 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1672 | ||||
1673 | // int lstat(const char *restrict path, struct stat *restrict buf); | |||
1674 | addToFunctionSummaryMap( | |||
1675 | "lstat", | |||
1676 | Signature(ArgTypes{ConstCharPtrRestrictTy, StructStatPtrRestrictTy}, | |||
1677 | RetType{IntTy}), | |||
1678 | Summary(NoEvalCall) | |||
1679 | .Case(ReturnsZeroOrMinusOne) | |||
1680 | .ArgConstraint(NotNull(ArgNo(0))) | |||
1681 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1682 | ||||
1683 | // int fstatat(int fd, const char *restrict path, | |||
1684 | // struct stat *restrict buf, int flag); | |||
1685 | addToFunctionSummaryMap( | |||
1686 | "fstatat", | |||
1687 | Signature(ArgTypes{IntTy, ConstCharPtrRestrictTy, | |||
1688 | StructStatPtrRestrictTy, IntTy}, | |||
1689 | RetType{IntTy}), | |||
1690 | Summary(NoEvalCall) | |||
1691 | .Case(ReturnsZeroOrMinusOne) | |||
1692 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1693 | .ArgConstraint(NotNull(ArgNo(1))) | |||
1694 | .ArgConstraint(NotNull(ArgNo(2)))); | |||
1695 | ||||
1696 | // DIR *opendir(const char *name); | |||
1697 | addToFunctionSummaryMap( | |||
1698 | "opendir", Signature(ArgTypes{ConstCharPtrTy}, RetType{DirPtrTy}), | |||
1699 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1700 | ||||
1701 | // DIR *fdopendir(int fd); | |||
1702 | addToFunctionSummaryMap("fdopendir", | |||
1703 | Signature(ArgTypes{IntTy}, RetType{DirPtrTy}), | |||
1704 | Summary(NoEvalCall) | |||
1705 | .ArgConstraint(ArgumentCondition( | |||
1706 | 0, WithinRange, Range(0, IntMax)))); | |||
1707 | ||||
1708 | // int isatty(int fildes); | |||
1709 | addToFunctionSummaryMap( | |||
1710 | "isatty", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1711 | Summary(NoEvalCall) | |||
1712 | .Case({ReturnValueCondition(WithinRange, Range(0, 1))}) | |||
1713 | .ArgConstraint( | |||
1714 | ArgumentCondition(0, WithinRange, Range(0, IntMax)))); | |||
1715 | ||||
1716 | // FILE *popen(const char *command, const char *type); | |||
1717 | addToFunctionSummaryMap( | |||
1718 | "popen", | |||
1719 | Signature(ArgTypes{ConstCharPtrTy, ConstCharPtrTy}, RetType{FilePtrTy}), | |||
1720 | Summary(NoEvalCall) | |||
1721 | .ArgConstraint(NotNull(ArgNo(0))) | |||
1722 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1723 | ||||
1724 | // int pclose(FILE *stream); | |||
1725 | addToFunctionSummaryMap( | |||
1726 | "pclose", Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), | |||
1727 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1728 | ||||
1729 | // int close(int fildes); | |||
1730 | addToFunctionSummaryMap("close", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
1731 | Summary(NoEvalCall) | |||
1732 | .Case(ReturnsZeroOrMinusOne) | |||
1733 | .ArgConstraint(ArgumentCondition( | |||
1734 | 0, WithinRange, Range(-1, IntMax)))); | |||
1735 | ||||
1736 | // long fpathconf(int fildes, int name); | |||
1737 | addToFunctionSummaryMap("fpathconf", | |||
1738 | Signature(ArgTypes{IntTy, IntTy}, RetType{LongTy}), | |||
1739 | Summary(NoEvalCall) | |||
1740 | .ArgConstraint(ArgumentCondition( | |||
1741 | 0, WithinRange, Range(0, IntMax)))); | |||
1742 | ||||
1743 | // long pathconf(const char *path, int name); | |||
1744 | addToFunctionSummaryMap( | |||
1745 | "pathconf", Signature(ArgTypes{ConstCharPtrTy, IntTy}, RetType{LongTy}), | |||
1746 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1747 | ||||
1748 | // FILE *fdopen(int fd, const char *mode); | |||
1749 | addToFunctionSummaryMap( | |||
1750 | "fdopen", | |||
1751 | Signature(ArgTypes{IntTy, ConstCharPtrTy}, RetType{FilePtrTy}), | |||
1752 | Summary(NoEvalCall) | |||
1753 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1754 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
1755 | ||||
1756 | // void rewinddir(DIR *dir); | |||
1757 | addToFunctionSummaryMap( | |||
1758 | "rewinddir", Signature(ArgTypes{DirPtrTy}, RetType{VoidTy}), | |||
1759 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1760 | ||||
1761 | // void seekdir(DIR *dirp, long loc); | |||
1762 | addToFunctionSummaryMap( | |||
1763 | "seekdir", Signature(ArgTypes{DirPtrTy, LongTy}, RetType{VoidTy}), | |||
1764 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1765 | ||||
1766 | // int rand_r(unsigned int *seedp); | |||
1767 | addToFunctionSummaryMap( | |||
1768 | "rand_r", Signature(ArgTypes{UnsignedIntPtrTy}, RetType{IntTy}), | |||
1769 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1770 | ||||
1771 | // int fileno(FILE *stream); | |||
1772 | addToFunctionSummaryMap("fileno", | |||
1773 | Signature(ArgTypes{FilePtrTy}, RetType{IntTy}), | |||
1774 | Summary(NoEvalCall) | |||
1775 | .Case(ReturnsFileDescriptor) | |||
1776 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1777 | ||||
1778 | // int fseeko(FILE *stream, off_t offset, int whence); | |||
1779 | addToFunctionSummaryMap( | |||
1780 | "fseeko", | |||
1781 | Signature(ArgTypes{FilePtrTy, Off_tTy, IntTy}, RetType{IntTy}), | |||
1782 | Summary(NoEvalCall) | |||
1783 | .Case(ReturnsZeroOrMinusOne) | |||
1784 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1785 | ||||
1786 | // off_t ftello(FILE *stream); | |||
1787 | addToFunctionSummaryMap( | |||
1788 | "ftello", Signature(ArgTypes{FilePtrTy}, RetType{Off_tTy}), | |||
1789 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1790 | ||||
1791 | // void *mmap(void *addr, size_t length, int prot, int flags, int fd, | |||
1792 | // off_t offset); | |||
1793 | addToFunctionSummaryMap( | |||
1794 | "mmap", | |||
1795 | Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off_tTy}, | |||
1796 | RetType{VoidPtrTy}), | |||
1797 | Summary(NoEvalCall) | |||
1798 | .ArgConstraint(ArgumentCondition(1, WithinRange, Range(1, SizeMax))) | |||
1799 | .ArgConstraint( | |||
1800 | ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); | |||
1801 | ||||
1802 | Optional<QualType> Off64_tTy = lookupTy("off64_t"); | |||
1803 | // void *mmap64(void *addr, size_t length, int prot, int flags, int fd, | |||
1804 | // off64_t offset); | |||
1805 | addToFunctionSummaryMap( | |||
1806 | "mmap64", | |||
1807 | Signature(ArgTypes{VoidPtrTy, SizeTy, IntTy, IntTy, IntTy, Off64_tTy}, | |||
1808 | RetType{VoidPtrTy}), | |||
1809 | Summary(NoEvalCall) | |||
1810 | .ArgConstraint(ArgumentCondition(1, WithinRange, Range(1, SizeMax))) | |||
1811 | .ArgConstraint( | |||
1812 | ArgumentCondition(4, WithinRange, Range(-1, IntMax)))); | |||
1813 | ||||
1814 | // int pipe(int fildes[2]); | |||
1815 | addToFunctionSummaryMap("pipe", | |||
1816 | Signature(ArgTypes{IntPtrTy}, RetType{IntTy}), | |||
1817 | Summary(NoEvalCall) | |||
1818 | .Case(ReturnsZeroOrMinusOne) | |||
1819 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1820 | ||||
1821 | // off_t lseek(int fildes, off_t offset, int whence); | |||
1822 | addToFunctionSummaryMap( | |||
1823 | "lseek", Signature(ArgTypes{IntTy, Off_tTy, IntTy}, RetType{Off_tTy}), | |||
1824 | Summary(NoEvalCall) | |||
1825 | .ArgConstraint( | |||
1826 | ArgumentCondition(0, WithinRange, Range(0, IntMax)))); | |||
1827 | ||||
1828 | // ssize_t readlink(const char *restrict path, char *restrict buf, | |||
1829 | // size_t bufsize); | |||
1830 | addToFunctionSummaryMap( | |||
1831 | "readlink", | |||
1832 | Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, | |||
1833 | RetType{Ssize_tTy}), | |||
1834 | Summary(NoEvalCall) | |||
1835 | .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), | |||
1836 | ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) | |||
1837 | .ArgConstraint(NotNull(ArgNo(0))) | |||
1838 | .ArgConstraint(NotNull(ArgNo(1))) | |||
1839 | .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), | |||
1840 | /*BufSize=*/ArgNo(2))) | |||
1841 | .ArgConstraint( | |||
1842 | ArgumentCondition(2, WithinRange, Range(0, SizeMax)))); | |||
1843 | ||||
1844 | // ssize_t readlinkat(int fd, const char *restrict path, | |||
1845 | // char *restrict buf, size_t bufsize); | |||
1846 | addToFunctionSummaryMap( | |||
1847 | "readlinkat", | |||
1848 | Signature( | |||
1849 | ArgTypes{IntTy, ConstCharPtrRestrictTy, CharPtrRestrictTy, SizeTy}, | |||
1850 | RetType{Ssize_tTy}), | |||
1851 | Summary(NoEvalCall) | |||
1852 | .Case({ReturnValueCondition(LessThanOrEq, ArgNo(3)), | |||
1853 | ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) | |||
1854 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1855 | .ArgConstraint(NotNull(ArgNo(1))) | |||
1856 | .ArgConstraint(NotNull(ArgNo(2))) | |||
1857 | .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(2), | |||
1858 | /*BufSize=*/ArgNo(3))) | |||
1859 | .ArgConstraint( | |||
1860 | ArgumentCondition(3, WithinRange, Range(0, SizeMax)))); | |||
1861 | ||||
1862 | // int renameat(int olddirfd, const char *oldpath, int newdirfd, const char | |||
1863 | // *newpath); | |||
1864 | addToFunctionSummaryMap( | |||
1865 | "renameat", | |||
1866 | Signature(ArgTypes{IntTy, ConstCharPtrTy, IntTy, ConstCharPtrTy}, | |||
1867 | RetType{IntTy}), | |||
1868 | Summary(NoEvalCall) | |||
1869 | .Case(ReturnsZeroOrMinusOne) | |||
1870 | .ArgConstraint(NotNull(ArgNo(1))) | |||
1871 | .ArgConstraint(NotNull(ArgNo(3)))); | |||
1872 | ||||
1873 | // char *realpath(const char *restrict file_name, | |||
1874 | // char *restrict resolved_name); | |||
1875 | addToFunctionSummaryMap( | |||
1876 | "realpath", | |||
1877 | Signature(ArgTypes{ConstCharPtrRestrictTy, CharPtrRestrictTy}, | |||
1878 | RetType{CharPtrTy}), | |||
1879 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
1880 | ||||
1881 | QualType CharPtrConstPtr = getPointerTy(getConstTy(CharPtrTy)); | |||
1882 | ||||
1883 | // int execv(const char *path, char *const argv[]); | |||
1884 | addToFunctionSummaryMap( | |||
1885 | "execv", | |||
1886 | Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}), | |||
1887 | Summary(NoEvalCall) | |||
1888 | .Case({ReturnValueCondition(WithinRange, SingleValue(-1))}) | |||
1889 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1890 | ||||
1891 | // int execvp(const char *file, char *const argv[]); | |||
1892 | addToFunctionSummaryMap( | |||
1893 | "execvp", | |||
1894 | Signature(ArgTypes{ConstCharPtrTy, CharPtrConstPtr}, RetType{IntTy}), | |||
1895 | Summary(NoEvalCall) | |||
1896 | .Case({ReturnValueCondition(WithinRange, SingleValue(-1))}) | |||
1897 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
1898 | ||||
1899 | // int getopt(int argc, char * const argv[], const char *optstring); | |||
1900 | addToFunctionSummaryMap( | |||
1901 | "getopt", | |||
1902 | Signature(ArgTypes{IntTy, CharPtrConstPtr, ConstCharPtrTy}, | |||
1903 | RetType{IntTy}), | |||
1904 | Summary(NoEvalCall) | |||
1905 | .Case({ReturnValueCondition(WithinRange, Range(-1, UCharRangeMax))}) | |||
1906 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1907 | .ArgConstraint(NotNull(ArgNo(1))) | |||
1908 | .ArgConstraint(NotNull(ArgNo(2)))); | |||
1909 | ||||
1910 | Optional<QualType> StructSockaddrTy = lookupTy("sockaddr"); | |||
1911 | Optional<QualType> StructSockaddrPtrTy = getPointerTy(StructSockaddrTy); | |||
1912 | Optional<QualType> ConstStructSockaddrPtrTy = | |||
1913 | getPointerTy(getConstTy(StructSockaddrTy)); | |||
1914 | Optional<QualType> StructSockaddrPtrRestrictTy = | |||
1915 | getRestrictTy(StructSockaddrPtrTy); | |||
1916 | Optional<QualType> ConstStructSockaddrPtrRestrictTy = | |||
1917 | getRestrictTy(ConstStructSockaddrPtrTy); | |||
1918 | Optional<QualType> Socklen_tTy = lookupTy("socklen_t"); | |||
1919 | Optional<QualType> Socklen_tPtrTy = getPointerTy(Socklen_tTy); | |||
1920 | Optional<QualType> Socklen_tPtrRestrictTy = getRestrictTy(Socklen_tPtrTy); | |||
1921 | Optional<RangeInt> Socklen_tMax = getMaxValue(Socklen_tTy); | |||
1922 | ||||
1923 | // In 'socket.h' of some libc implementations with C99, sockaddr parameter | |||
1924 | // is a transparent union of the underlying sockaddr_ family of pointers | |||
1925 | // instead of being a pointer to struct sockaddr. In these cases, the | |||
1926 | // standardized signature will not match, thus we try to match with another | |||
1927 | // signature that has the joker Irrelevant type. We also remove those | |||
1928 | // constraints which require pointer types for the sockaddr param. | |||
1929 | auto Accept = | |||
1930 | Summary(NoEvalCall) | |||
1931 | .Case(ReturnsFileDescriptor) | |||
1932 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))); | |||
1933 | if (!addToFunctionSummaryMap( | |||
1934 | "accept", | |||
1935 | // int accept(int socket, struct sockaddr *restrict address, | |||
1936 | // socklen_t *restrict address_len); | |||
1937 | Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, | |||
1938 | Socklen_tPtrRestrictTy}, | |||
1939 | RetType{IntTy}), | |||
1940 | Accept)) | |||
1941 | addToFunctionSummaryMap( | |||
1942 | "accept", | |||
1943 | Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, | |||
1944 | RetType{IntTy}), | |||
1945 | Accept); | |||
1946 | ||||
1947 | // int bind(int socket, const struct sockaddr *address, socklen_t | |||
1948 | // address_len); | |||
1949 | if (!addToFunctionSummaryMap( | |||
1950 | "bind", | |||
1951 | Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy}, | |||
1952 | RetType{IntTy}), | |||
1953 | Summary(NoEvalCall) | |||
1954 | .Case(ReturnsZeroOrMinusOne) | |||
1955 | .ArgConstraint( | |||
1956 | ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1957 | .ArgConstraint(NotNull(ArgNo(1))) | |||
1958 | .ArgConstraint( | |||
1959 | BufferSize(/*Buffer=*/ArgNo(1), /*BufSize=*/ArgNo(2))) | |||
1960 | .ArgConstraint( | |||
1961 | ArgumentCondition(2, WithinRange, Range(0, Socklen_tMax))))) | |||
1962 | // Do not add constraints on sockaddr. | |||
1963 | addToFunctionSummaryMap( | |||
1964 | "bind", | |||
1965 | Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}), | |||
1966 | Summary(NoEvalCall) | |||
1967 | .Case(ReturnsZeroOrMinusOne) | |||
1968 | .ArgConstraint( | |||
1969 | ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1970 | .ArgConstraint( | |||
1971 | ArgumentCondition(2, WithinRange, Range(0, Socklen_tMax)))); | |||
1972 | ||||
1973 | // int getpeername(int socket, struct sockaddr *restrict address, | |||
1974 | // socklen_t *restrict address_len); | |||
1975 | if (!addToFunctionSummaryMap( | |||
1976 | "getpeername", | |||
1977 | Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, | |||
1978 | Socklen_tPtrRestrictTy}, | |||
1979 | RetType{IntTy}), | |||
1980 | Summary(NoEvalCall) | |||
1981 | .Case(ReturnsZeroOrMinusOne) | |||
1982 | .ArgConstraint( | |||
1983 | ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
1984 | .ArgConstraint(NotNull(ArgNo(1))) | |||
1985 | .ArgConstraint(NotNull(ArgNo(2))))) | |||
1986 | addToFunctionSummaryMap( | |||
1987 | "getpeername", | |||
1988 | Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, | |||
1989 | RetType{IntTy}), | |||
1990 | Summary(NoEvalCall) | |||
1991 | .Case(ReturnsZeroOrMinusOne) | |||
1992 | .ArgConstraint( | |||
1993 | ArgumentCondition(0, WithinRange, Range(0, IntMax)))); | |||
1994 | ||||
1995 | // int getsockname(int socket, struct sockaddr *restrict address, | |||
1996 | // socklen_t *restrict address_len); | |||
1997 | if (!addToFunctionSummaryMap( | |||
1998 | "getsockname", | |||
1999 | Signature(ArgTypes{IntTy, StructSockaddrPtrRestrictTy, | |||
2000 | Socklen_tPtrRestrictTy}, | |||
2001 | RetType{IntTy}), | |||
2002 | Summary(NoEvalCall) | |||
2003 | .Case(ReturnsZeroOrMinusOne) | |||
2004 | .ArgConstraint( | |||
2005 | ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
2006 | .ArgConstraint(NotNull(ArgNo(1))) | |||
2007 | .ArgConstraint(NotNull(ArgNo(2))))) | |||
2008 | addToFunctionSummaryMap( | |||
2009 | "getsockname", | |||
2010 | Signature(ArgTypes{IntTy, Irrelevant, Socklen_tPtrRestrictTy}, | |||
2011 | RetType{IntTy}), | |||
2012 | Summary(NoEvalCall) | |||
2013 | .Case(ReturnsZeroOrMinusOne) | |||
2014 | .ArgConstraint( | |||
2015 | ArgumentCondition(0, WithinRange, Range(0, IntMax)))); | |||
2016 | ||||
2017 | // int connect(int socket, const struct sockaddr *address, socklen_t | |||
2018 | // address_len); | |||
2019 | if (!addToFunctionSummaryMap( | |||
2020 | "connect", | |||
2021 | Signature(ArgTypes{IntTy, ConstStructSockaddrPtrTy, Socklen_tTy}, | |||
2022 | RetType{IntTy}), | |||
2023 | Summary(NoEvalCall) | |||
2024 | .Case(ReturnsZeroOrMinusOne) | |||
2025 | .ArgConstraint( | |||
2026 | ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
2027 | .ArgConstraint(NotNull(ArgNo(1))))) | |||
2028 | addToFunctionSummaryMap( | |||
2029 | "connect", | |||
2030 | Signature(ArgTypes{IntTy, Irrelevant, Socklen_tTy}, RetType{IntTy}), | |||
2031 | Summary(NoEvalCall) | |||
2032 | .Case(ReturnsZeroOrMinusOne) | |||
2033 | .ArgConstraint( | |||
2034 | ArgumentCondition(0, WithinRange, Range(0, IntMax)))); | |||
2035 | ||||
2036 | auto Recvfrom = | |||
2037 | Summary(NoEvalCall) | |||
2038 | .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), | |||
2039 | ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) | |||
2040 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
2041 | .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), | |||
2042 | /*BufSize=*/ArgNo(2))); | |||
2043 | if (!addToFunctionSummaryMap( | |||
2044 | "recvfrom", | |||
2045 | // ssize_t recvfrom(int socket, void *restrict buffer, | |||
2046 | // size_t length, | |||
2047 | // int flags, struct sockaddr *restrict address, | |||
2048 | // socklen_t *restrict address_len); | |||
2049 | Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy, | |||
2050 | StructSockaddrPtrRestrictTy, | |||
2051 | Socklen_tPtrRestrictTy}, | |||
2052 | RetType{Ssize_tTy}), | |||
2053 | Recvfrom)) | |||
2054 | addToFunctionSummaryMap( | |||
2055 | "recvfrom", | |||
2056 | Signature(ArgTypes{IntTy, VoidPtrRestrictTy, SizeTy, IntTy, | |||
2057 | Irrelevant, Socklen_tPtrRestrictTy}, | |||
2058 | RetType{Ssize_tTy}), | |||
2059 | Recvfrom); | |||
2060 | ||||
2061 | auto Sendto = | |||
2062 | Summary(NoEvalCall) | |||
2063 | .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), | |||
2064 | ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) | |||
2065 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
2066 | .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), | |||
2067 | /*BufSize=*/ArgNo(2))); | |||
2068 | if (!addToFunctionSummaryMap( | |||
2069 | "sendto", | |||
2070 | // ssize_t sendto(int socket, const void *message, size_t length, | |||
2071 | // int flags, const struct sockaddr *dest_addr, | |||
2072 | // socklen_t dest_len); | |||
2073 | Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, | |||
2074 | ConstStructSockaddrPtrTy, Socklen_tTy}, | |||
2075 | RetType{Ssize_tTy}), | |||
2076 | Sendto)) | |||
2077 | addToFunctionSummaryMap( | |||
2078 | "sendto", | |||
2079 | Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy, Irrelevant, | |||
2080 | Socklen_tTy}, | |||
2081 | RetType{Ssize_tTy}), | |||
2082 | Sendto); | |||
2083 | ||||
2084 | // int listen(int sockfd, int backlog); | |||
2085 | addToFunctionSummaryMap("listen", | |||
2086 | Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), | |||
2087 | Summary(NoEvalCall) | |||
2088 | .Case(ReturnsZeroOrMinusOne) | |||
2089 | .ArgConstraint(ArgumentCondition( | |||
2090 | 0, WithinRange, Range(0, IntMax)))); | |||
2091 | ||||
2092 | // ssize_t recv(int sockfd, void *buf, size_t len, int flags); | |||
2093 | addToFunctionSummaryMap( | |||
2094 | "recv", | |||
2095 | Signature(ArgTypes{IntTy, VoidPtrTy, SizeTy, IntTy}, | |||
2096 | RetType{Ssize_tTy}), | |||
2097 | Summary(NoEvalCall) | |||
2098 | .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), | |||
2099 | ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) | |||
2100 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
2101 | .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), | |||
2102 | /*BufSize=*/ArgNo(2)))); | |||
2103 | ||||
2104 | Optional<QualType> StructMsghdrTy = lookupTy("msghdr"); | |||
2105 | Optional<QualType> StructMsghdrPtrTy = getPointerTy(StructMsghdrTy); | |||
2106 | Optional<QualType> ConstStructMsghdrPtrTy = | |||
2107 | getPointerTy(getConstTy(StructMsghdrTy)); | |||
2108 | ||||
2109 | // ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); | |||
2110 | addToFunctionSummaryMap( | |||
2111 | "recvmsg", | |||
2112 | Signature(ArgTypes{IntTy, StructMsghdrPtrTy, IntTy}, | |||
2113 | RetType{Ssize_tTy}), | |||
2114 | Summary(NoEvalCall) | |||
2115 | .Case({ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) | |||
2116 | .ArgConstraint( | |||
2117 | ArgumentCondition(0, WithinRange, Range(0, IntMax)))); | |||
2118 | ||||
2119 | // ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); | |||
2120 | addToFunctionSummaryMap( | |||
2121 | "sendmsg", | |||
2122 | Signature(ArgTypes{IntTy, ConstStructMsghdrPtrTy, IntTy}, | |||
2123 | RetType{Ssize_tTy}), | |||
2124 | Summary(NoEvalCall) | |||
2125 | .Case({ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) | |||
2126 | .ArgConstraint( | |||
2127 | ArgumentCondition(0, WithinRange, Range(0, IntMax)))); | |||
2128 | ||||
2129 | // int setsockopt(int socket, int level, int option_name, | |||
2130 | // const void *option_value, socklen_t option_len); | |||
2131 | addToFunctionSummaryMap( | |||
2132 | "setsockopt", | |||
2133 | Signature(ArgTypes{IntTy, IntTy, IntTy, ConstVoidPtrTy, Socklen_tTy}, | |||
2134 | RetType{IntTy}), | |||
2135 | Summary(NoEvalCall) | |||
2136 | .Case(ReturnsZeroOrMinusOne) | |||
2137 | .ArgConstraint(NotNull(ArgNo(3))) | |||
2138 | .ArgConstraint( | |||
2139 | BufferSize(/*Buffer=*/ArgNo(3), /*BufSize=*/ArgNo(4))) | |||
2140 | .ArgConstraint( | |||
2141 | ArgumentCondition(4, WithinRange, Range(0, Socklen_tMax)))); | |||
2142 | ||||
2143 | // int getsockopt(int socket, int level, int option_name, | |||
2144 | // void *restrict option_value, | |||
2145 | // socklen_t *restrict option_len); | |||
2146 | addToFunctionSummaryMap( | |||
2147 | "getsockopt", | |||
2148 | Signature(ArgTypes{IntTy, IntTy, IntTy, VoidPtrRestrictTy, | |||
2149 | Socklen_tPtrRestrictTy}, | |||
2150 | RetType{IntTy}), | |||
2151 | Summary(NoEvalCall) | |||
2152 | .Case(ReturnsZeroOrMinusOne) | |||
2153 | .ArgConstraint(NotNull(ArgNo(3))) | |||
2154 | .ArgConstraint(NotNull(ArgNo(4)))); | |||
2155 | ||||
2156 | // ssize_t send(int sockfd, const void *buf, size_t len, int flags); | |||
2157 | addToFunctionSummaryMap( | |||
2158 | "send", | |||
2159 | Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy, IntTy}, | |||
2160 | RetType{Ssize_tTy}), | |||
2161 | Summary(NoEvalCall) | |||
2162 | .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), | |||
2163 | ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}) | |||
2164 | .ArgConstraint(ArgumentCondition(0, WithinRange, Range(0, IntMax))) | |||
2165 | .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), | |||
2166 | /*BufSize=*/ArgNo(2)))); | |||
2167 | ||||
2168 | // int socketpair(int domain, int type, int protocol, int sv[2]); | |||
2169 | addToFunctionSummaryMap( | |||
2170 | "socketpair", | |||
2171 | Signature(ArgTypes{IntTy, IntTy, IntTy, IntPtrTy}, RetType{IntTy}), | |||
2172 | Summary(NoEvalCall) | |||
2173 | .Case(ReturnsZeroOrMinusOne) | |||
2174 | .ArgConstraint(NotNull(ArgNo(3)))); | |||
2175 | ||||
2176 | // int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, | |||
2177 | // char *restrict node, socklen_t nodelen, | |||
2178 | // char *restrict service, | |||
2179 | // socklen_t servicelen, int flags); | |||
2180 | // | |||
2181 | // This is defined in netdb.h. And contrary to 'socket.h', the sockaddr | |||
2182 | // parameter is never handled as a transparent union in netdb.h | |||
2183 | addToFunctionSummaryMap( | |||
2184 | "getnameinfo", | |||
2185 | Signature(ArgTypes{ConstStructSockaddrPtrRestrictTy, Socklen_tTy, | |||
2186 | CharPtrRestrictTy, Socklen_tTy, CharPtrRestrictTy, | |||
2187 | Socklen_tTy, IntTy}, | |||
2188 | RetType{IntTy}), | |||
2189 | Summary(NoEvalCall) | |||
2190 | .ArgConstraint( | |||
2191 | BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1))) | |||
2192 | .ArgConstraint( | |||
2193 | ArgumentCondition(1, WithinRange, Range(0, Socklen_tMax))) | |||
2194 | .ArgConstraint( | |||
2195 | BufferSize(/*Buffer=*/ArgNo(2), /*BufSize=*/ArgNo(3))) | |||
2196 | .ArgConstraint( | |||
2197 | ArgumentCondition(3, WithinRange, Range(0, Socklen_tMax))) | |||
2198 | .ArgConstraint( | |||
2199 | BufferSize(/*Buffer=*/ArgNo(4), /*BufSize=*/ArgNo(5))) | |||
2200 | .ArgConstraint( | |||
2201 | ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax)))); | |||
2202 | ||||
2203 | Optional<QualType> StructUtimbufTy = lookupTy("utimbuf"); | |||
2204 | Optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy); | |||
2205 | ||||
2206 | // int utime(const char *filename, struct utimbuf *buf); | |||
2207 | addToFunctionSummaryMap( | |||
2208 | "utime", | |||
2209 | Signature(ArgTypes{ConstCharPtrTy, StructUtimbufPtrTy}, RetType{IntTy}), | |||
2210 | Summary(NoEvalCall) | |||
2211 | .Case(ReturnsZeroOrMinusOne) | |||
2212 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
2213 | ||||
2214 | Optional<QualType> StructTimespecTy = lookupTy("timespec"); | |||
2215 | Optional<QualType> StructTimespecPtrTy = getPointerTy(StructTimespecTy); | |||
2216 | Optional<QualType> ConstStructTimespecPtrTy = | |||
2217 | getPointerTy(getConstTy(StructTimespecTy)); | |||
2218 | ||||
2219 | // int futimens(int fd, const struct timespec times[2]); | |||
2220 | addToFunctionSummaryMap( | |||
2221 | "futimens", | |||
2222 | Signature(ArgTypes{IntTy, ConstStructTimespecPtrTy}, RetType{IntTy}), | |||
2223 | Summary(NoEvalCall) | |||
2224 | .Case(ReturnsZeroOrMinusOne) | |||
2225 | .ArgConstraint( | |||
2226 | ArgumentCondition(0, WithinRange, Range(0, IntMax)))); | |||
2227 | ||||
2228 | // int utimensat(int dirfd, const char *pathname, | |||
2229 | // const struct timespec times[2], int flags); | |||
2230 | addToFunctionSummaryMap("utimensat", | |||
2231 | Signature(ArgTypes{IntTy, ConstCharPtrTy, | |||
2232 | ConstStructTimespecPtrTy, IntTy}, | |||
2233 | RetType{IntTy}), | |||
2234 | Summary(NoEvalCall) | |||
2235 | .Case(ReturnsZeroOrMinusOne) | |||
2236 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
2237 | ||||
2238 | Optional<QualType> StructTimevalTy = lookupTy("timeval"); | |||
2239 | Optional<QualType> ConstStructTimevalPtrTy = | |||
2240 | getPointerTy(getConstTy(StructTimevalTy)); | |||
2241 | ||||
2242 | // int utimes(const char *filename, const struct timeval times[2]); | |||
2243 | addToFunctionSummaryMap( | |||
2244 | "utimes", | |||
2245 | Signature(ArgTypes{ConstCharPtrTy, ConstStructTimevalPtrTy}, | |||
2246 | RetType{IntTy}), | |||
2247 | Summary(NoEvalCall) | |||
2248 | .Case(ReturnsZeroOrMinusOne) | |||
2249 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
2250 | ||||
2251 | // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); | |||
2252 | addToFunctionSummaryMap( | |||
2253 | "nanosleep", | |||
2254 | Signature(ArgTypes{ConstStructTimespecPtrTy, StructTimespecPtrTy}, | |||
2255 | RetType{IntTy}), | |||
2256 | Summary(NoEvalCall) | |||
2257 | .Case(ReturnsZeroOrMinusOne) | |||
2258 | .ArgConstraint(NotNull(ArgNo(0)))); | |||
2259 | ||||
2260 | Optional<QualType> Time_tTy = lookupTy("time_t"); | |||
2261 | Optional<QualType> ConstTime_tPtrTy = getPointerTy(getConstTy(Time_tTy)); | |||
2262 | Optional<QualType> ConstTime_tPtrRestrictTy = | |||
2263 | getRestrictTy(ConstTime_tPtrTy); | |||
2264 | ||||
2265 | Optional<QualType> StructTmTy = lookupTy("tm"); | |||
2266 | Optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy); | |||
2267 | Optional<QualType> StructTmPtrRestrictTy = getRestrictTy(StructTmPtrTy); | |||
2268 | Optional<QualType> ConstStructTmPtrTy = | |||
2269 | getPointerTy(getConstTy(StructTmTy)); | |||
2270 | Optional<QualType> ConstStructTmPtrRestrictTy = | |||
2271 | getRestrictTy(ConstStructTmPtrTy); | |||
2272 | ||||
2273 | // struct tm * localtime(const time_t *tp); | |||
2274 | addToFunctionSummaryMap( | |||
2275 | "localtime", | |||
2276 | Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), | |||
2277 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
2278 | ||||
2279 | // struct tm *localtime_r(const time_t *restrict timer, | |||
2280 | // struct tm *restrict result); | |||
2281 | addToFunctionSummaryMap( | |||
2282 | "localtime_r", | |||
2283 | Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy}, | |||
2284 | RetType{StructTmPtrTy}), | |||
2285 | Summary(NoEvalCall) | |||
2286 | .ArgConstraint(NotNull(ArgNo(0))) | |||
2287 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
2288 | ||||
2289 | // char *asctime_r(const struct tm *restrict tm, char *restrict buf); | |||
2290 | addToFunctionSummaryMap( | |||
2291 | "asctime_r", | |||
2292 | Signature(ArgTypes{ConstStructTmPtrRestrictTy, CharPtrRestrictTy}, | |||
2293 | RetType{CharPtrTy}), | |||
2294 | Summary(NoEvalCall) | |||
2295 | .ArgConstraint(NotNull(ArgNo(0))) | |||
2296 | .ArgConstraint(NotNull(ArgNo(1))) | |||
2297 | .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1), | |||
2298 | /*MinBufSize=*/BVF.getValue(26, IntTy)))); | |||
2299 | ||||
2300 | // char *ctime_r(const time_t *timep, char *buf); | |||
2301 | addToFunctionSummaryMap( | |||
2302 | "ctime_r", | |||
2303 | Signature(ArgTypes{ConstTime_tPtrTy, CharPtrTy}, RetType{CharPtrTy}), | |||
2304 | Summary(NoEvalCall) | |||
2305 | .ArgConstraint(NotNull(ArgNo(0))) | |||
2306 | .ArgConstraint(NotNull(ArgNo(1))) | |||
2307 | .ArgConstraint(BufferSize( | |||
2308 | /*Buffer=*/ArgNo(1), | |||
2309 | /*MinBufSize=*/BVF.getValue(26, IntTy)))); | |||
2310 | ||||
2311 | // struct tm *gmtime_r(const time_t *restrict timer, | |||
2312 | // struct tm *restrict result); | |||
2313 | addToFunctionSummaryMap( | |||
2314 | "gmtime_r", | |||
2315 | Signature(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy}, | |||
2316 | RetType{StructTmPtrTy}), | |||
2317 | Summary(NoEvalCall) | |||
2318 | .ArgConstraint(NotNull(ArgNo(0))) | |||
2319 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
2320 | ||||
2321 | // struct tm * gmtime(const time_t *tp); | |||
2322 | addToFunctionSummaryMap( | |||
2323 | "gmtime", Signature(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}), | |||
2324 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
2325 | ||||
2326 | Optional<QualType> Clockid_tTy = lookupTy("clockid_t"); | |||
2327 | ||||
2328 | // int clock_gettime(clockid_t clock_id, struct timespec *tp); | |||
2329 | addToFunctionSummaryMap( | |||
2330 | "clock_gettime", | |||
2331 | Signature(ArgTypes{Clockid_tTy, StructTimespecPtrTy}, RetType{IntTy}), | |||
2332 | Summary(NoEvalCall) | |||
2333 | .Case(ReturnsZeroOrMinusOne) | |||
2334 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
2335 | ||||
2336 | Optional<QualType> StructItimervalTy = lookupTy("itimerval"); | |||
2337 | Optional<QualType> StructItimervalPtrTy = getPointerTy(StructItimervalTy); | |||
2338 | ||||
2339 | // int getitimer(int which, struct itimerval *curr_value); | |||
2340 | addToFunctionSummaryMap( | |||
2341 | "getitimer", | |||
2342 | Signature(ArgTypes{IntTy, StructItimervalPtrTy}, RetType{IntTy}), | |||
2343 | Summary(NoEvalCall) | |||
2344 | .Case(ReturnsZeroOrMinusOne) | |||
2345 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
2346 | ||||
2347 | Optional<QualType> Pthread_cond_tTy = lookupTy("pthread_cond_t"); | |||
2348 | Optional<QualType> Pthread_cond_tPtrTy = getPointerTy(Pthread_cond_tTy); | |||
2349 | Optional<QualType> Pthread_tTy = lookupTy("pthread_t"); | |||
2350 | Optional<QualType> Pthread_tPtrTy = getPointerTy(Pthread_tTy); | |||
2351 | Optional<QualType> Pthread_tPtrRestrictTy = getRestrictTy(Pthread_tPtrTy); | |||
2352 | Optional<QualType> Pthread_mutex_tTy = lookupTy("pthread_mutex_t"); | |||
2353 | Optional<QualType> Pthread_mutex_tPtrTy = getPointerTy(Pthread_mutex_tTy); | |||
2354 | Optional<QualType> Pthread_mutex_tPtrRestrictTy = | |||
2355 | getRestrictTy(Pthread_mutex_tPtrTy); | |||
2356 | Optional<QualType> Pthread_attr_tTy = lookupTy("pthread_attr_t"); | |||
2357 | Optional<QualType> Pthread_attr_tPtrTy = getPointerTy(Pthread_attr_tTy); | |||
2358 | Optional<QualType> ConstPthread_attr_tPtrTy = | |||
2359 | getPointerTy(getConstTy(Pthread_attr_tTy)); | |||
2360 | Optional<QualType> ConstPthread_attr_tPtrRestrictTy = | |||
2361 | getRestrictTy(ConstPthread_attr_tPtrTy); | |||
2362 | Optional<QualType> Pthread_mutexattr_tTy = lookupTy("pthread_mutexattr_t"); | |||
2363 | Optional<QualType> ConstPthread_mutexattr_tPtrTy = | |||
2364 | getPointerTy(getConstTy(Pthread_mutexattr_tTy)); | |||
2365 | Optional<QualType> ConstPthread_mutexattr_tPtrRestrictTy = | |||
2366 | getRestrictTy(ConstPthread_mutexattr_tPtrTy); | |||
2367 | ||||
2368 | QualType PthreadStartRoutineTy = getPointerTy( | |||
2369 | ACtx.getFunctionType(/*ResultTy=*/VoidPtrTy, /*Args=*/VoidPtrTy, | |||
2370 | FunctionProtoType::ExtProtoInfo())); | |||
2371 | ||||
2372 | // int pthread_cond_signal(pthread_cond_t *cond); | |||
2373 | // int pthread_cond_broadcast(pthread_cond_t *cond); | |||
2374 | addToFunctionSummaryMap( | |||
2375 | {"pthread_cond_signal", "pthread_cond_broadcast"}, | |||
2376 | Signature(ArgTypes{Pthread_cond_tPtrTy}, RetType{IntTy}), | |||
2377 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
2378 | ||||
2379 | // int pthread_create(pthread_t *restrict thread, | |||
2380 | // const pthread_attr_t *restrict attr, | |||
2381 | // void *(*start_routine)(void*), void *restrict arg); | |||
2382 | addToFunctionSummaryMap( | |||
2383 | "pthread_create", | |||
2384 | Signature(ArgTypes{Pthread_tPtrRestrictTy, | |||
2385 | ConstPthread_attr_tPtrRestrictTy, | |||
2386 | PthreadStartRoutineTy, VoidPtrRestrictTy}, | |||
2387 | RetType{IntTy}), | |||
2388 | Summary(NoEvalCall) | |||
2389 | .ArgConstraint(NotNull(ArgNo(0))) | |||
2390 | .ArgConstraint(NotNull(ArgNo(2)))); | |||
2391 | ||||
2392 | // int pthread_attr_destroy(pthread_attr_t *attr); | |||
2393 | // int pthread_attr_init(pthread_attr_t *attr); | |||
2394 | addToFunctionSummaryMap( | |||
2395 | {"pthread_attr_destroy", "pthread_attr_init"}, | |||
2396 | Signature(ArgTypes{Pthread_attr_tPtrTy}, RetType{IntTy}), | |||
2397 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
2398 | ||||
2399 | // int pthread_attr_getstacksize(const pthread_attr_t *restrict attr, | |||
2400 | // size_t *restrict stacksize); | |||
2401 | // int pthread_attr_getguardsize(const pthread_attr_t *restrict attr, | |||
2402 | // size_t *restrict guardsize); | |||
2403 | addToFunctionSummaryMap( | |||
2404 | {"pthread_attr_getstacksize", "pthread_attr_getguardsize"}, | |||
2405 | Signature(ArgTypes{ConstPthread_attr_tPtrRestrictTy, SizePtrRestrictTy}, | |||
2406 | RetType{IntTy}), | |||
2407 | Summary(NoEvalCall) | |||
2408 | .ArgConstraint(NotNull(ArgNo(0))) | |||
2409 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
2410 | ||||
2411 | // int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize); | |||
2412 | // int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize); | |||
2413 | addToFunctionSummaryMap( | |||
2414 | {"pthread_attr_setstacksize", "pthread_attr_setguardsize"}, | |||
2415 | Signature(ArgTypes{Pthread_attr_tPtrTy, SizeTy}, RetType{IntTy}), | |||
2416 | Summary(NoEvalCall) | |||
2417 | .ArgConstraint(NotNull(ArgNo(0))) | |||
2418 | .ArgConstraint( | |||
2419 | ArgumentCondition(1, WithinRange, Range(0, SizeMax)))); | |||
2420 | ||||
2421 | // int pthread_mutex_init(pthread_mutex_t *restrict mutex, const | |||
2422 | // pthread_mutexattr_t *restrict attr); | |||
2423 | addToFunctionSummaryMap( | |||
2424 | "pthread_mutex_init", | |||
2425 | Signature(ArgTypes{Pthread_mutex_tPtrRestrictTy, | |||
2426 | ConstPthread_mutexattr_tPtrRestrictTy}, | |||
2427 | RetType{IntTy}), | |||
2428 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
2429 | ||||
2430 | // int pthread_mutex_destroy(pthread_mutex_t *mutex); | |||
2431 | // int pthread_mutex_lock(pthread_mutex_t *mutex); | |||
2432 | // int pthread_mutex_trylock(pthread_mutex_t *mutex); | |||
2433 | // int pthread_mutex_unlock(pthread_mutex_t *mutex); | |||
2434 | addToFunctionSummaryMap( | |||
2435 | {"pthread_mutex_destroy", "pthread_mutex_lock", "pthread_mutex_trylock", | |||
2436 | "pthread_mutex_unlock"}, | |||
2437 | Signature(ArgTypes{Pthread_mutex_tPtrTy}, RetType{IntTy}), | |||
2438 | Summary(NoEvalCall).ArgConstraint(NotNull(ArgNo(0)))); | |||
2439 | } | |||
2440 | ||||
2441 | // Functions for testing. | |||
2442 | if (ChecksEnabled[CK_StdCLibraryFunctionsTesterChecker]) { | |||
2443 | addToFunctionSummaryMap( | |||
2444 | "__two_constrained_args", | |||
2445 | Signature(ArgTypes{IntTy, IntTy}, RetType{IntTy}), | |||
2446 | Summary(EvalCallAsPure) | |||
2447 | .ArgConstraint(ArgumentCondition(0U, WithinRange, SingleValue(1))) | |||
2448 | .ArgConstraint(ArgumentCondition(1U, WithinRange, SingleValue(1)))); | |||
2449 | addToFunctionSummaryMap( | |||
2450 | "__arg_constrained_twice", Signature(ArgTypes{IntTy}, RetType{IntTy}), | |||
2451 | Summary(EvalCallAsPure) | |||
2452 | .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1))) | |||
2453 | .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(2)))); | |||
2454 | addToFunctionSummaryMap( | |||
2455 | "__defaultparam", | |||
2456 | Signature(ArgTypes{Irrelevant, IntTy}, RetType{IntTy}), | |||
2457 | Summary(EvalCallAsPure).ArgConstraint(NotNull(ArgNo(0)))); | |||
2458 | addToFunctionSummaryMap( | |||
2459 | "__variadic", | |||
2460 | Signature(ArgTypes{VoidPtrTy, ConstCharPtrTy}, RetType{IntTy}), | |||
2461 | Summary(EvalCallAsPure) | |||
2462 | .ArgConstraint(NotNull(ArgNo(0))) | |||
2463 | .ArgConstraint(NotNull(ArgNo(1)))); | |||
2464 | addToFunctionSummaryMap( | |||
2465 | "__buf_size_arg_constraint", | |||
2466 | Signature(ArgTypes{ConstVoidPtrTy, SizeTy}, RetType{IntTy}), | |||
2467 | Summary(EvalCallAsPure) | |||
2468 | .ArgConstraint( | |||
2469 | BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1)))); | |||
2470 | addToFunctionSummaryMap( | |||
2471 | "__buf_size_arg_constraint_mul", | |||
2472 | Signature(ArgTypes{ConstVoidPtrTy, SizeTy, SizeTy}, RetType{IntTy}), | |||
2473 | Summary(EvalCallAsPure) | |||
2474 | .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1), | |||
2475 | /*BufSizeMultiplier=*/ArgNo(2)))); | |||
2476 | addToFunctionSummaryMap( | |||
2477 | "__buf_size_arg_constraint_concrete", | |||
2478 | Signature(ArgTypes{ConstVoidPtrTy}, RetType{IntTy}), | |||
2479 | Summary(EvalCallAsPure) | |||
2480 | .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), | |||
2481 | /*BufSize=*/BVF.getValue(10, IntTy)))); | |||
2482 | addToFunctionSummaryMap( | |||
2483 | {"__test_restrict_param_0", "__test_restrict_param_1", | |||
2484 | "__test_restrict_param_2"}, | |||
2485 | Signature(ArgTypes{VoidPtrRestrictTy}, RetType{VoidTy}), | |||
2486 | Summary(EvalCallAsPure)); | |||
2487 | } | |||
2488 | } | |||
2489 | ||||
2490 | void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) { | |||
2491 | auto *Checker = mgr.registerChecker<StdLibraryFunctionsChecker>(); | |||
2492 | Checker->DisplayLoadedSummaries = | |||
2493 | mgr.getAnalyzerOptions().getCheckerBooleanOption( | |||
2494 | Checker, "DisplayLoadedSummaries"); | |||
2495 | Checker->ModelPOSIX = | |||
2496 | mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "ModelPOSIX"); | |||
2497 | } | |||
2498 | ||||
2499 | bool ento::shouldRegisterStdCLibraryFunctionsChecker( | |||
2500 | const CheckerManager &mgr) { | |||
2501 | return true; | |||
2502 | } | |||
2503 | ||||
2504 | #define REGISTER_CHECKER(name)void ento::registername(CheckerManager &mgr) { StdLibraryFunctionsChecker *checker = mgr.getChecker<StdLibraryFunctionsChecker>( ); checker->ChecksEnabled[StdLibraryFunctionsChecker::CK_name ] = true; checker->CheckNames[StdLibraryFunctionsChecker:: CK_name] = mgr.getCurrentCheckerName(); } bool ento::shouldRegistername (const CheckerManager &mgr) { return true; } \ | |||
2505 | void ento::register##name(CheckerManager &mgr) { \ | |||
2506 | StdLibraryFunctionsChecker *checker = \ | |||
2507 | mgr.getChecker<StdLibraryFunctionsChecker>(); \ | |||
2508 | checker->ChecksEnabled[StdLibraryFunctionsChecker::CK_##name] = true; \ | |||
2509 | checker->CheckNames[StdLibraryFunctionsChecker::CK_##name] = \ | |||
2510 | mgr.getCurrentCheckerName(); \ | |||
2511 | } \ | |||
2512 | \ | |||
2513 | bool ento::shouldRegister##name(const CheckerManager &mgr) { return true; } | |||
2514 | ||||
2515 | REGISTER_CHECKER(StdCLibraryFunctionArgsChecker)void ento::registerStdCLibraryFunctionArgsChecker(CheckerManager &mgr) { StdLibraryFunctionsChecker *checker = mgr.getChecker <StdLibraryFunctionsChecker>(); checker->ChecksEnabled [StdLibraryFunctionsChecker::CK_StdCLibraryFunctionArgsChecker ] = true; checker->CheckNames[StdLibraryFunctionsChecker:: CK_StdCLibraryFunctionArgsChecker] = mgr.getCurrentCheckerName (); } bool ento::shouldRegisterStdCLibraryFunctionArgsChecker (const CheckerManager &mgr) { return true; } | |||
2516 | REGISTER_CHECKER(StdCLibraryFunctionsTesterChecker)void ento::registerStdCLibraryFunctionsTesterChecker(CheckerManager &mgr) { StdLibraryFunctionsChecker *checker = mgr.getChecker <StdLibraryFunctionsChecker>(); checker->ChecksEnabled [StdLibraryFunctionsChecker::CK_StdCLibraryFunctionsTesterChecker ] = true; checker->CheckNames[StdLibraryFunctionsChecker:: CK_StdCLibraryFunctionsTesterChecker] = mgr.getCurrentCheckerName (); } bool ento::shouldRegisterStdCLibraryFunctionsTesterChecker (const CheckerManager &mgr) { return true; } |
1 | //===- Optional.h - Simple variant for passing optional values --*- 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 file provides Optional, a template class modeled in the spirit of |
10 | // OCaml's 'opt' variant. The idea is to strongly type whether or not |
11 | // a value can be optional. |
12 | // |
13 | //===----------------------------------------------------------------------===// |
14 | |
15 | #ifndef LLVM_ADT_OPTIONAL_H |
16 | #define LLVM_ADT_OPTIONAL_H |
17 | |
18 | #include "llvm/ADT/Hashing.h" |
19 | #include "llvm/ADT/None.h" |
20 | #include "llvm/Support/Compiler.h" |
21 | #include "llvm/Support/type_traits.h" |
22 | #include <cassert> |
23 | #include <memory> |
24 | #include <new> |
25 | #include <utility> |
26 | |
27 | namespace llvm { |
28 | |
29 | class raw_ostream; |
30 | |
31 | namespace optional_detail { |
32 | |
33 | struct in_place_t {}; |
34 | |
35 | /// Storage for any type. |
36 | // |
37 | // The specialization condition intentionally uses |
38 | // llvm::is_trivially_copy_constructible instead of |
39 | // std::is_trivially_copy_constructible. GCC versions prior to 7.4 may |
40 | // instantiate the copy constructor of `T` when |
41 | // std::is_trivially_copy_constructible is instantiated. This causes |
42 | // compilation to fail if we query the trivially copy constructible property of |
43 | // a class which is not copy constructible. |
44 | // |
45 | // The current implementation of OptionalStorage insists that in order to use |
46 | // the trivial specialization, the value_type must be trivially copy |
47 | // constructible and trivially copy assignable due to =default implementations |
48 | // of the copy/move constructor/assignment. It does not follow that this is |
49 | // necessarily the case std::is_trivially_copyable is true (hence the expanded |
50 | // specialization condition). |
51 | // |
52 | // The move constructible / assignable conditions emulate the remaining behavior |
53 | // of std::is_trivially_copyable. |
54 | template <typename T, bool = (llvm::is_trivially_copy_constructible<T>::value && |
55 | std::is_trivially_copy_assignable<T>::value && |
56 | (std::is_trivially_move_constructible<T>::value || |
57 | !std::is_move_constructible<T>::value) && |
58 | (std::is_trivially_move_assignable<T>::value || |
59 | !std::is_move_assignable<T>::value))> |
60 | class OptionalStorage { |
61 | union { |
62 | char empty; |
63 | T value; |
64 | }; |
65 | bool hasVal; |
66 | |
67 | public: |
68 | ~OptionalStorage() { reset(); } |
69 | |
70 | constexpr OptionalStorage() noexcept : empty(), hasVal(false) {} |
71 | |
72 | constexpr OptionalStorage(OptionalStorage const &other) : OptionalStorage() { |
73 | if (other.hasValue()) { |
74 | emplace(other.value); |
75 | } |
76 | } |
77 | constexpr OptionalStorage(OptionalStorage &&other) : OptionalStorage() { |
78 | if (other.hasValue()) { |
79 | emplace(std::move(other.value)); |
80 | } |
81 | } |
82 | |
83 | template <class... Args> |
84 | constexpr explicit OptionalStorage(in_place_t, Args &&... args) |
85 | : value(std::forward<Args>(args)...), hasVal(true) {} |
86 | |
87 | void reset() noexcept { |
88 | if (hasVal) { |
89 | value.~T(); |
90 | hasVal = false; |
91 | } |
92 | } |
93 | |
94 | constexpr bool hasValue() const noexcept { return hasVal; } |
95 | |
96 | T &getValue() LLVM_LVALUE_FUNCTION& noexcept { |
97 | assert(hasVal)((hasVal) ? static_cast<void> (0) : __assert_fail ("hasVal" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/llvm/include/llvm/ADT/Optional.h" , 97, __PRETTY_FUNCTION__)); |
98 | return value; |
99 | } |
100 | constexpr T const &getValue() const LLVM_LVALUE_FUNCTION& noexcept { |
101 | assert(hasVal)((hasVal) ? static_cast<void> (0) : __assert_fail ("hasVal" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/llvm/include/llvm/ADT/Optional.h" , 101, __PRETTY_FUNCTION__)); |
102 | return value; |
103 | } |
104 | #if LLVM_HAS_RVALUE_REFERENCE_THIS1 |
105 | T &&getValue() && noexcept { |
106 | assert(hasVal)((hasVal) ? static_cast<void> (0) : __assert_fail ("hasVal" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/llvm/include/llvm/ADT/Optional.h" , 106, __PRETTY_FUNCTION__)); |
107 | return std::move(value); |
108 | } |
109 | #endif |
110 | |
111 | template <class... Args> void emplace(Args &&... args) { |
112 | reset(); |
113 | ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...); |
114 | hasVal = true; |
115 | } |
116 | |
117 | OptionalStorage &operator=(T const &y) { |
118 | if (hasValue()) { |
119 | value = y; |
120 | } else { |
121 | ::new ((void *)std::addressof(value)) T(y); |
122 | hasVal = true; |
123 | } |
124 | return *this; |
125 | } |
126 | OptionalStorage &operator=(T &&y) { |
127 | if (hasValue()) { |
128 | value = std::move(y); |
129 | } else { |
130 | ::new ((void *)std::addressof(value)) T(std::move(y)); |
131 | hasVal = true; |
132 | } |
133 | return *this; |
134 | } |
135 | |
136 | OptionalStorage &operator=(OptionalStorage const &other) { |
137 | if (other.hasValue()) { |
138 | if (hasValue()) { |
139 | value = other.value; |
140 | } else { |
141 | ::new ((void *)std::addressof(value)) T(other.value); |
142 | hasVal = true; |
143 | } |
144 | } else { |
145 | reset(); |
146 | } |
147 | return *this; |
148 | } |
149 | |
150 | OptionalStorage &operator=(OptionalStorage &&other) { |
151 | if (other.hasValue()) { |
152 | if (hasValue()) { |
153 | value = std::move(other.value); |
154 | } else { |
155 | ::new ((void *)std::addressof(value)) T(std::move(other.value)); |
156 | hasVal = true; |
157 | } |
158 | } else { |
159 | reset(); |
160 | } |
161 | return *this; |
162 | } |
163 | }; |
164 | |
165 | template <typename T> class OptionalStorage<T, true> { |
166 | union { |
167 | char empty; |
168 | T value; |
169 | }; |
170 | bool hasVal = false; |
171 | |
172 | public: |
173 | ~OptionalStorage() = default; |
174 | |
175 | constexpr OptionalStorage() noexcept : empty{} {} |
176 | |
177 | constexpr OptionalStorage(OptionalStorage const &other) = default; |
178 | constexpr OptionalStorage(OptionalStorage &&other) = default; |
179 | |
180 | OptionalStorage &operator=(OptionalStorage const &other) = default; |
181 | OptionalStorage &operator=(OptionalStorage &&other) = default; |
182 | |
183 | template <class... Args> |
184 | constexpr explicit OptionalStorage(in_place_t, Args &&... args) |
185 | : value(std::forward<Args>(args)...), hasVal(true) {} |
186 | |
187 | void reset() noexcept { |
188 | if (hasVal) { |
189 | value.~T(); |
190 | hasVal = false; |
191 | } |
192 | } |
193 | |
194 | constexpr bool hasValue() const noexcept { return hasVal; } |
195 | |
196 | T &getValue() LLVM_LVALUE_FUNCTION& noexcept { |
197 | assert(hasVal)((hasVal) ? static_cast<void> (0) : __assert_fail ("hasVal" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/llvm/include/llvm/ADT/Optional.h" , 197, __PRETTY_FUNCTION__)); |
198 | return value; |
199 | } |
200 | constexpr T const &getValue() const LLVM_LVALUE_FUNCTION& noexcept { |
201 | assert(hasVal)((hasVal) ? static_cast<void> (0) : __assert_fail ("hasVal" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/llvm/include/llvm/ADT/Optional.h" , 201, __PRETTY_FUNCTION__)); |
202 | return value; |
203 | } |
204 | #if LLVM_HAS_RVALUE_REFERENCE_THIS1 |
205 | T &&getValue() && noexcept { |
206 | assert(hasVal)((hasVal) ? static_cast<void> (0) : __assert_fail ("hasVal" , "/build/llvm-toolchain-snapshot-12~++20210125100614+2cdb34efdac5/llvm/include/llvm/ADT/Optional.h" , 206, __PRETTY_FUNCTION__)); |
207 | return std::move(value); |
208 | } |
209 | #endif |
210 | |
211 | template <class... Args> void emplace(Args &&... args) { |
212 | reset(); |
213 | ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...); |
214 | hasVal = true; |
215 | } |
216 | |
217 | OptionalStorage &operator=(T const &y) { |
218 | if (hasValue()) { |
219 | value = y; |
220 | } else { |
221 | ::new ((void *)std::addressof(value)) T(y); |
222 | hasVal = true; |
223 | } |
224 | return *this; |
225 | } |
226 | OptionalStorage &operator=(T &&y) { |
227 | if (hasValue()) { |
228 | value = std::move(y); |
229 | } else { |
230 | ::new ((void *)std::addressof(value)) T(std::move(y)); |
231 | hasVal = true; |
232 | } |
233 | return *this; |
234 | } |
235 | }; |
236 | |
237 | } // namespace optional_detail |
238 | |
239 | template <typename T> class Optional { |
240 | optional_detail::OptionalStorage<T> Storage; |
241 | |
242 | public: |
243 | using value_type = T; |
244 | |
245 | constexpr Optional() {} |
246 | constexpr Optional(NoneType) {} |
247 | |
248 | constexpr Optional(const T &y) : Storage(optional_detail::in_place_t{}, y) {} |
249 | constexpr Optional(const Optional &O) = default; |
250 | |
251 | constexpr Optional(T &&y) |
252 | : Storage(optional_detail::in_place_t{}, std::move(y)) {} |
253 | constexpr Optional(Optional &&O) = default; |
254 | |
255 | Optional &operator=(T &&y) { |
256 | Storage = std::move(y); |
257 | return *this; |
258 | } |
259 | Optional &operator=(Optional &&O) = default; |
260 | |
261 | /// Create a new object by constructing it in place with the given arguments. |
262 | template <typename... ArgTypes> void emplace(ArgTypes &&... Args) { |
263 | Storage.emplace(std::forward<ArgTypes>(Args)...); |
264 | } |
265 | |
266 | static constexpr Optional create(const T *y) { |
267 | return y ? Optional(*y) : Optional(); |
268 | } |
269 | |
270 | Optional &operator=(const T &y) { |
271 | Storage = y; |
272 | return *this; |
273 | } |
274 | Optional &operator=(const Optional &O) = default; |
275 | |
276 | void reset() { Storage.reset(); } |
277 | |
278 | constexpr const T *getPointer() const { return &Storage.getValue(); } |
279 | T *getPointer() { return &Storage.getValue(); } |
280 | constexpr const T &getValue() const LLVM_LVALUE_FUNCTION& { |
281 | return Storage.getValue(); |
282 | } |
283 | T &getValue() LLVM_LVALUE_FUNCTION& { return Storage.getValue(); } |
284 | |
285 | constexpr explicit operator bool() const { return hasValue(); } |
286 | constexpr bool hasValue() const { return Storage.hasValue(); } |
287 | constexpr const T *operator->() const { return getPointer(); } |
288 | T *operator->() { return getPointer(); } |
289 | constexpr const T &operator*() const LLVM_LVALUE_FUNCTION& { |
290 | return getValue(); |
291 | } |
292 | T &operator*() LLVM_LVALUE_FUNCTION& { return getValue(); } |
293 | |
294 | template <typename U> |
295 | constexpr T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION& { |
296 | return hasValue() ? getValue() : std::forward<U>(value); |
297 | } |
298 | |
299 | /// Apply a function to the value if present; otherwise return None. |
300 | template <class Function> |
301 | auto map(const Function &F) const LLVM_LVALUE_FUNCTION& |
302 | -> Optional<decltype(F(getValue()))> { |
303 | if (*this) return F(getValue()); |
304 | return None; |
305 | } |
306 | |
307 | #if LLVM_HAS_RVALUE_REFERENCE_THIS1 |
308 | T &&getValue() && { return std::move(Storage.getValue()); } |
309 | T &&operator*() && { return std::move(Storage.getValue()); } |
310 | |
311 | template <typename U> |
312 | T getValueOr(U &&value) && { |
313 | return hasValue() ? std::move(getValue()) : std::forward<U>(value); |
314 | } |
315 | |
316 | /// Apply a function to the value if present; otherwise return None. |
317 | template <class Function> |
318 | auto map(const Function &F) && |
319 | -> Optional<decltype(F(std::move(*this).getValue()))> { |
320 | if (*this) return F(std::move(*this).getValue()); |
321 | return None; |
322 | } |
323 | #endif |
324 | }; |
325 | |
326 | template <class T> llvm::hash_code hash_value(const Optional<T> &O) { |
327 | return O ? hash_combine(true, *O) : hash_value(false); |
328 | } |
329 | |
330 | template <typename T, typename U> |
331 | constexpr bool operator==(const Optional<T> &X, const Optional<U> &Y) { |
332 | if (X && Y) |
333 | return *X == *Y; |
334 | return X.hasValue() == Y.hasValue(); |
335 | } |
336 | |
337 | template <typename T, typename U> |
338 | constexpr bool operator!=(const Optional<T> &X, const Optional<U> &Y) { |
339 | return !(X == Y); |
340 | } |
341 | |
342 | template <typename T, typename U> |
343 | constexpr bool operator<(const Optional<T> &X, const Optional<U> &Y) { |
344 | if (X && Y) |
345 | return *X < *Y; |
346 | return X.hasValue() < Y.hasValue(); |
347 | } |
348 | |
349 | template <typename T, typename U> |
350 | constexpr bool operator<=(const Optional<T> &X, const Optional<U> &Y) { |
351 | return !(Y < X); |
352 | } |
353 | |
354 | template <typename T, typename U> |
355 | constexpr bool operator>(const Optional<T> &X, const Optional<U> &Y) { |
356 | return Y < X; |
357 | } |
358 | |
359 | template <typename T, typename U> |
360 | constexpr bool operator>=(const Optional<T> &X, const Optional<U> &Y) { |
361 | return !(X < Y); |
362 | } |
363 | |
364 | template <typename T> |
365 | constexpr bool operator==(const Optional<T> &X, NoneType) { |
366 | return !X; |
367 | } |
368 | |
369 | template <typename T> |
370 | constexpr bool operator==(NoneType, const Optional<T> &X) { |
371 | return X == None; |
372 | } |
373 | |
374 | template <typename T> |
375 | constexpr bool operator!=(const Optional<T> &X, NoneType) { |
376 | return !(X == None); |
377 | } |
378 | |
379 | template <typename T> |
380 | constexpr bool operator!=(NoneType, const Optional<T> &X) { |
381 | return X != None; |
382 | } |
383 | |
384 | template <typename T> constexpr bool operator<(const Optional<T> &X, NoneType) { |
385 | return false; |
386 | } |
387 | |
388 | template <typename T> constexpr bool operator<(NoneType, const Optional<T> &X) { |
389 | return X.hasValue(); |
390 | } |
391 | |
392 | template <typename T> |
393 | constexpr bool operator<=(const Optional<T> &X, NoneType) { |
394 | return !(None < X); |
395 | } |
396 | |
397 | template <typename T> |
398 | constexpr bool operator<=(NoneType, const Optional<T> &X) { |
399 | return !(X < None); |
400 | } |
401 | |
402 | template <typename T> constexpr bool operator>(const Optional<T> &X, NoneType) { |
403 | return None < X; |
404 | } |
405 | |
406 | template <typename T> constexpr bool operator>(NoneType, const Optional<T> &X) { |
407 | return X < None; |
408 | } |
409 | |
410 | template <typename T> |
411 | constexpr bool operator>=(const Optional<T> &X, NoneType) { |
412 | return None <= X; |
413 | } |
414 | |
415 | template <typename T> |
416 | constexpr bool operator>=(NoneType, const Optional<T> &X) { |
417 | return X <= None; |
418 | } |
419 | |
420 | template <typename T> |
421 | constexpr bool operator==(const Optional<T> &X, const T &Y) { |
422 | return X && *X == Y; |
423 | } |
424 | |
425 | template <typename T> |
426 | constexpr bool operator==(const T &X, const Optional<T> &Y) { |
427 | return Y && X == *Y; |
428 | } |
429 | |
430 | template <typename T> |
431 | constexpr bool operator!=(const Optional<T> &X, const T &Y) { |
432 | return !(X == Y); |
433 | } |
434 | |
435 | template <typename T> |
436 | constexpr bool operator!=(const T &X, const Optional<T> &Y) { |
437 | return !(X == Y); |
438 | } |
439 | |
440 | template <typename T> |
441 | constexpr bool operator<(const Optional<T> &X, const T &Y) { |
442 | return !X || *X < Y; |
443 | } |
444 | |
445 | template <typename T> |
446 | constexpr bool operator<(const T &X, const Optional<T> &Y) { |
447 | return Y && X < *Y; |
448 | } |
449 | |
450 | template <typename T> |
451 | constexpr bool operator<=(const Optional<T> &X, const T &Y) { |
452 | return !(Y < X); |
453 | } |
454 | |
455 | template <typename T> |
456 | constexpr bool operator<=(const T &X, const Optional<T> &Y) { |
457 | return !(Y < X); |
458 | } |
459 | |
460 | template <typename T> |
461 | constexpr bool operator>(const Optional<T> &X, const T &Y) { |
462 | return Y < X; |
463 | } |
464 | |
465 | template <typename T> |
466 | constexpr bool operator>(const T &X, const Optional<T> &Y) { |
467 | return Y < X; |
468 | } |
469 | |
470 | template <typename T> |
471 | constexpr bool operator>=(const Optional<T> &X, const T &Y) { |
472 | return !(X < Y); |
473 | } |
474 | |
475 | template <typename T> |
476 | constexpr bool operator>=(const T &X, const Optional<T> &Y) { |
477 | return !(X < Y); |
478 | } |
479 | |
480 | raw_ostream &operator<<(raw_ostream &OS, NoneType); |
481 | |
482 | template <typename T, typename = decltype(std::declval<raw_ostream &>() |
483 | << std::declval<const T &>())> |
484 | raw_ostream &operator<<(raw_ostream &OS, const Optional<T> &O) { |
485 | if (O) |
486 | OS << *O; |
487 | else |
488 | OS << None; |
489 | return OS; |
490 | } |
491 | |
492 | } // end namespace llvm |
493 | |
494 | #endif // LLVM_ADT_OPTIONAL_H |