xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp (revision 26b0a9d8aca9f0c6b0a5940877396558be543e03)
1 //=== StdLibraryFunctionsChecker.cpp - Model standard functions -*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This checker improves modeling of a few simple library functions.
11 // It does not generate warnings.
12 //
13 // This checker provides a specification format - `FunctionSummaryTy' - and
14 // contains descriptions of some library functions in this format. Each
15 // specification contains a list of branches for splitting the program state
16 // upon call, and range constraints on argument and return-value symbols that
17 // are satisfied on each branch. This spec can be expanded to include more
18 // items, like external effects of the function.
19 //
20 // The main difference between this approach and the body farms technique is
21 // in more explicit control over how many branches are produced. For example,
22 // consider standard C function `ispunct(int x)', which returns a non-zero value
23 // iff `x' is a punctuation character, that is, when `x' is in range
24 //   ['!', '/']   [':', '@']  U  ['[', '\`']  U  ['{', '~'].
25 // `FunctionSummaryTy' provides only two branches for this function. However,
26 // any attempt to describe this range with if-statements in the body farm
27 // would result in many more branches. Because each branch needs to be analyzed
28 // independently, this significantly reduces performance. Additionally,
29 // once we consider a branch on which `x' is in range, say, ['!', '/'],
30 // we assume that such branch is an important separate path through the program,
31 // which may lead to false positives because considering this particular path
32 // was not consciously intended, and therefore it might have been unreachable.
33 //
34 // This checker uses eval::Call for modeling "pure" functions, for which
35 // their `FunctionSummaryTy' is a precise model. This avoids unnecessary
36 // invalidation passes. Conflicts with other checkers are unlikely because
37 // if the function has no other effects, other checkers would probably never
38 // want to improve upon the modeling done by this checker.
39 //
40 // Non-"pure" functions, for which only partial improvement over the default
41 // behavior is expected, are modeled via check::PostCall, non-intrusively.
42 //
43 // The following standard C functions are currently supported:
44 //
45 //   fgetc      getline   isdigit   isupper
46 //   fread      isalnum   isgraph   isxdigit
47 //   fwrite     isalpha   islower   read
48 //   getc       isascii   isprint   write
49 //   getchar    isblank   ispunct
50 //   getdelim   iscntrl   isspace
51 //
52 //===----------------------------------------------------------------------===//
53 
54 #include "ClangSACheckers.h"
55 #include "clang/StaticAnalyzer/Core/Checker.h"
56 #include "clang/StaticAnalyzer/Core/CheckerManager.h"
57 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
58 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
59 
60 using namespace clang;
61 using namespace clang::ento;
62 
63 namespace {
64 class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> {
65   /// Below is a series of typedefs necessary to define function specs.
66   /// We avoid nesting types here because each additional qualifier
67   /// would need to be repeated in every function spec.
68   struct FunctionSummaryTy;
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 InvalidationKindTy { NoEvalCall, EvalCallAsPure };
73 
74   /// A pair of ValueRangeKindTy and IntRangeVectorTy would describe a range
75   /// imposed on a particular argument or return value symbol.
76   ///
77   /// Given a range, should the argument stay inside or outside this range?
78   /// The special `ComparesToArgument' value indicates that we should
79   /// impose a constraint that involves other argument or return value symbols.
80   enum ValueRangeKindTy { OutOfRange, WithinRange, ComparesToArgument };
81 
82   /// Normally, describes a single range constraint, eg. {{0, 1}, {3, 4}} is
83   /// a non-negative integer, which less than 5 and not equal to 2. For
84   /// `ComparesToArgument', holds information about how exactly to compare to
85   /// the argument.
86   typedef std::vector<std::pair<uint64_t, uint64_t>> IntRangeVectorTy;
87 
88   /// A reference to an argument or return value by its number.
89   /// ArgNo in CallExpr and CallEvent is defined as Unsigned, but
90   /// obviously uint32_t should be enough for all practical purposes.
91   typedef uint32_t ArgNoTy;
92   static const ArgNoTy Ret = std::numeric_limits<ArgNoTy>::max();
93 
94   /// Incapsulates a single range on a single symbol within a branch.
95   class ValueRange {
96     ArgNoTy ArgNo; // Argument to which we apply the range.
97     ValueRangeKindTy Kind; // Kind of range definition.
98     IntRangeVectorTy Args; // Polymorphic arguments.
99 
100   public:
101     ValueRange(ArgNoTy ArgNo, ValueRangeKindTy Kind,
102                const IntRangeVectorTy &Args)
103         : ArgNo(ArgNo), Kind(Kind), Args(Args) {}
104 
105     ArgNoTy getArgNo() const { return ArgNo; }
106     ValueRangeKindTy getKind() const { return Kind; }
107 
108     BinaryOperator::Opcode getOpcode() const {
109       assert(Kind == ComparesToArgument);
110       assert(Args.size() == 1);
111       BinaryOperator::Opcode Op =
112           static_cast<BinaryOperator::Opcode>(Args[0].first);
113       assert(BinaryOperator::isComparisonOp(Op) &&
114              "Only comparison ops are supported for ComparesToArgument");
115       return Op;
116     }
117 
118     ArgNoTy getOtherArgNo() const {
119       assert(Kind == ComparesToArgument);
120       assert(Args.size() == 1);
121       return static_cast<ArgNoTy>(Args[0].second);
122     }
123 
124     const IntRangeVectorTy &getRanges() const {
125       assert(Kind != ComparesToArgument);
126       return Args;
127     }
128 
129     // We avoid creating a virtual apply() method because
130     // it makes initializer lists harder to write.
131   private:
132     ProgramStateRef
133     applyAsOutOfRange(ProgramStateRef State, const CallEvent &Call,
134                       const FunctionSummaryTy &Summary) const;
135     ProgramStateRef
136     applyAsWithinRange(ProgramStateRef State, const CallEvent &Call,
137                        const FunctionSummaryTy &Summary) const;
138     ProgramStateRef
139     applyAsComparesToArgument(ProgramStateRef State, const CallEvent &Call,
140                               const FunctionSummaryTy &Summary) const;
141 
142   public:
143     ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
144                           const FunctionSummaryTy &Summary) const {
145       switch (Kind) {
146       case OutOfRange:
147         return applyAsOutOfRange(State, Call, Summary);
148       case WithinRange:
149         return applyAsWithinRange(State, Call, Summary);
150       case ComparesToArgument:
151         return applyAsComparesToArgument(State, Call, Summary);
152       }
153       llvm_unreachable("Unknown ValueRange kind!");
154     }
155   };
156 
157   /// The complete list of ranges that defines a single branch.
158   typedef std::vector<ValueRange> ValueRangeSet;
159 
160   /// Includes information about function prototype (which is necessary to
161   /// ensure we're modeling the right function and casting values properly),
162   /// approach to invalidation, and a list of branches - essentially, a list
163   /// of list of ranges - essentially, a list of lists of lists of segments.
164   struct FunctionSummaryTy {
165     const std::vector<QualType> ArgTypes;
166     const QualType RetType;
167     const InvalidationKindTy InvalidationKind;
168     const std::vector<ValueRangeSet> Ranges;
169 
170   private:
171     static void assertTypeSuitableForSummary(QualType T) {
172       assert(!T->isVoidType() &&
173              "We should have had no significant void types in the spec");
174       assert(T.isCanonical() &&
175              "We should only have canonical types in the spec");
176       // FIXME: lift this assert (but not the ones above!)
177       assert(T->isIntegralOrEnumerationType() &&
178              "We only support integral ranges in the spec");
179     }
180 
181   public:
182     QualType getArgType(ArgNoTy ArgNo) const {
183       QualType T = (ArgNo == Ret) ? RetType : ArgTypes[ArgNo];
184       assertTypeSuitableForSummary(T);
185       return T;
186     }
187 
188     /// Try our best to figure out if the call expression is the call of
189     /// *the* library function to which this specification applies.
190     bool matchesCall(const CallExpr *CE) const;
191   };
192 
193   // The map of all functions supported by the checker. It is initialized
194   // lazily, and it doesn't change after initialization.
195   typedef llvm::StringMap<FunctionSummaryTy> FunctionSummaryMapTy;
196   mutable FunctionSummaryMapTy FunctionSummaryMap;
197 
198   // Auxiliary functions to support ArgNoTy within all structures
199   // in a unified manner.
200   static QualType getArgType(const FunctionSummaryTy &Summary, ArgNoTy ArgNo) {
201     return Summary.getArgType(ArgNo);
202   }
203   static QualType getArgType(const CallEvent &Call, ArgNoTy ArgNo) {
204     return ArgNo == Ret ? Call.getResultType().getCanonicalType()
205                         : Call.getArgExpr(ArgNo)->getType().getCanonicalType();
206   }
207   static QualType getArgType(const CallExpr *CE, ArgNoTy ArgNo) {
208     return ArgNo == Ret ? CE->getType().getCanonicalType()
209                         : CE->getArg(ArgNo)->getType().getCanonicalType();
210   }
211   static SVal getArgSVal(const CallEvent &Call, ArgNoTy ArgNo) {
212     return ArgNo == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgNo);
213   }
214 
215 public:
216   void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
217   bool evalCall(const CallExpr *CE, CheckerContext &C) const;
218 
219 private:
220   Optional<FunctionSummaryTy> findFunctionSummary(const FunctionDecl *FD,
221                                           const CallExpr *CE,
222                                           CheckerContext &C) const;
223 
224   void initFunctionSummaries(BasicValueFactory &BVF) const;
225 };
226 } // end of anonymous namespace
227 
228 ProgramStateRef StdLibraryFunctionsChecker::ValueRange::applyAsOutOfRange(
229     ProgramStateRef State, const CallEvent &Call,
230     const FunctionSummaryTy &Summary) const {
231 
232   ProgramStateManager &Mgr = State->getStateManager();
233   SValBuilder &SVB = Mgr.getSValBuilder();
234   BasicValueFactory &BVF = SVB.getBasicValueFactory();
235   ConstraintManager &CM = Mgr.getConstraintManager();
236   QualType T = getArgType(Summary, getArgNo());
237   SVal V = getArgSVal(Call, getArgNo());
238 
239   if (auto N = V.getAs<NonLoc>()) {
240     const IntRangeVectorTy &R = getRanges();
241     size_t E = R.size();
242     for (size_t I = 0; I != E; ++I) {
243       const llvm::APSInt &Min = BVF.getValue(R[I].first, T);
244       const llvm::APSInt &Max = BVF.getValue(R[I].second, T);
245       assert(Min <= Max);
246       State = CM.assumeWithinInclusiveRange(State, *N, Min, Max, false);
247       if (!State)
248         break;
249     }
250   }
251 
252   return State;
253 }
254 
255 ProgramStateRef
256 StdLibraryFunctionsChecker::ValueRange::applyAsWithinRange(
257     ProgramStateRef State, const CallEvent &Call,
258     const FunctionSummaryTy &Summary) const {
259 
260   ProgramStateManager &Mgr = State->getStateManager();
261   SValBuilder &SVB = Mgr.getSValBuilder();
262   BasicValueFactory &BVF = SVB.getBasicValueFactory();
263   ConstraintManager &CM = Mgr.getConstraintManager();
264   QualType T = getArgType(Summary, getArgNo());
265   SVal V = getArgSVal(Call, getArgNo());
266 
267   // "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R".
268   // We cut off [T_MIN, min(R) - 1] and [max(R) + 1, T_MAX] if necessary,
269   // and then cut away all holes in R one by one.
270   if (auto N = V.getAs<NonLoc>()) {
271     const IntRangeVectorTy &R = getRanges();
272     size_t E = R.size();
273 
274     const llvm::APSInt &MinusInf = BVF.getMinValue(T);
275     const llvm::APSInt &PlusInf = BVF.getMaxValue(T);
276 
277     const llvm::APSInt &Left = BVF.getValue(R[0].first - 1ULL, T);
278     if (Left != PlusInf) {
279       assert(MinusInf <= Left);
280       State = CM.assumeWithinInclusiveRange(State, *N, MinusInf, Left, false);
281       if (!State)
282         return nullptr;
283     }
284 
285     const llvm::APSInt &Right = BVF.getValue(R[E - 1].second + 1ULL, T);
286     if (Right != MinusInf) {
287       assert(Right <= PlusInf);
288       State = CM.assumeWithinInclusiveRange(State, *N, Right, PlusInf, false);
289       if (!State)
290         return nullptr;
291     }
292 
293     for (size_t I = 1; I != E; ++I) {
294       const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, T);
295       const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, T);
296       assert(Min <= Max);
297       State = CM.assumeWithinInclusiveRange(State, *N, Min, Max, false);
298       if (!State)
299         return nullptr;
300     }
301   }
302 
303   return State;
304 }
305 
306 ProgramStateRef
307 StdLibraryFunctionsChecker::ValueRange::applyAsComparesToArgument(
308     ProgramStateRef State, const CallEvent &Call,
309     const FunctionSummaryTy &Summary) const {
310 
311   ProgramStateManager &Mgr = State->getStateManager();
312   SValBuilder &SVB = Mgr.getSValBuilder();
313   QualType CondT = SVB.getConditionType();
314   QualType T = getArgType(Summary, getArgNo());
315   SVal V = getArgSVal(Call, getArgNo());
316 
317   BinaryOperator::Opcode Op = getOpcode();
318   ArgNoTy OtherArg = getOtherArgNo();
319   SVal OtherV = getArgSVal(Call, OtherArg);
320   QualType OtherT = getArgType(Call, OtherArg);
321   // Note: we avoid integral promotion for comparison.
322   OtherV = SVB.evalCast(OtherV, T, OtherT);
323   if (auto CompV = SVB.evalBinOp(State, Op, V, OtherV, CondT)
324                        .getAs<DefinedOrUnknownSVal>())
325     State = State->assume(*CompV, true);
326   return State;
327 }
328 
329 void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
330                                                CheckerContext &C) const {
331   const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
332   if (!FD)
333     return;
334 
335   const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
336   if (!CE)
337     return;
338 
339   Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C);
340   if (!FoundSummary)
341     return;
342 
343   // Now apply ranges.
344   const FunctionSummaryTy &Summary = *FoundSummary;
345   ProgramStateRef State = C.getState();
346 
347   for (const auto &VRS: Summary.Ranges) {
348     ProgramStateRef NewState = State;
349     for (const auto &VR: VRS) {
350       NewState = VR.apply(NewState, Call, Summary);
351       if (!NewState)
352         break;
353     }
354 
355     if (NewState && NewState != State)
356       C.addTransition(NewState);
357   }
358 }
359 
360 bool StdLibraryFunctionsChecker::evalCall(const CallExpr *CE,
361                                           CheckerContext &C) const {
362   const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl());
363   if (!FD)
364     return false;
365 
366   Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C);
367   if (!FoundSummary)
368     return false;
369 
370   const FunctionSummaryTy &Summary = *FoundSummary;
371   switch (Summary.InvalidationKind) {
372   case EvalCallAsPure: {
373     ProgramStateRef State = C.getState();
374     const LocationContext *LC = C.getLocationContext();
375     SVal V = C.getSValBuilder().conjureSymbolVal(
376         CE, LC, CE->getType().getCanonicalType(), C.blockCount());
377     State = State->BindExpr(CE, LC, V);
378     C.addTransition(State);
379     return true;
380   }
381   case NoEvalCall:
382     // Summary tells us to avoid performing eval::Call. The function is possibly
383     // evaluated by another checker, or evaluated conservatively.
384     return false;
385   }
386   llvm_unreachable("Unknown invalidation kind!");
387 }
388 
389 bool StdLibraryFunctionsChecker::FunctionSummaryTy::matchesCall(
390     const CallExpr *CE) const {
391   // Check number of arguments:
392   if (CE->getNumArgs() != ArgTypes.size())
393     return false;
394 
395   // Check return type if relevant:
396   if (!RetType.isNull() && RetType != CE->getType().getCanonicalType())
397     return false;
398 
399   // Check argument types when relevant:
400   for (size_t I = 0, E = ArgTypes.size(); I != E; ++I) {
401     QualType FormalT = ArgTypes[I];
402     // Null type marks irrelevant arguments.
403     if (FormalT.isNull())
404       continue;
405 
406     assertTypeSuitableForSummary(FormalT);
407 
408     QualType ActualT = StdLibraryFunctionsChecker::getArgType(CE, I);
409     assert(ActualT.isCanonical());
410     if (ActualT != FormalT)
411       return false;
412   }
413 
414   return true;
415 }
416 
417 Optional<StdLibraryFunctionsChecker::FunctionSummaryTy>
418 StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD,
419                                                 const CallExpr *CE,
420                                                 CheckerContext &C) const {
421   // Note: we cannot always obtain FD from CE
422   // (eg. virtual call, or call by pointer).
423   assert(CE);
424 
425   if (!FD)
426     return None;
427 
428   SValBuilder &SVB = C.getSValBuilder();
429   BasicValueFactory &BVF = SVB.getBasicValueFactory();
430   initFunctionSummaries(BVF);
431 
432   std::string Name = FD->getQualifiedNameAsString();
433   if (Name.empty() || !C.isCLibraryFunction(FD, Name))
434     return None;
435 
436   auto FSMI = FunctionSummaryMap.find(Name);
437   if (FSMI == FunctionSummaryMap.end())
438     return None;
439 
440   // Verify that function signature matches the spec in advance.
441   // Otherwise we might be modeling the wrong function.
442   // Strict checking is important because we will be conducting
443   // very integral-type-sensitive operations on arguments and
444   // return values.
445   const FunctionSummaryTy &Spec = FSMI->second;
446   if (!Spec.matchesCall(CE))
447     return None;
448 
449   return Spec;
450 }
451 
452 void StdLibraryFunctionsChecker::initFunctionSummaries(
453     BasicValueFactory &BVF) const {
454   if (!FunctionSummaryMap.empty())
455     return;
456 
457   ASTContext &ACtx = BVF.getContext();
458 
459   // These types are useful for writing specifications quickly,
460   // New specifications should probably introduce more types.
461   QualType Irrelevant; // A placeholder, whenever we do not care about the type.
462   QualType IntTy = ACtx.IntTy;
463   QualType SizeTy = ACtx.getSizeType();
464   QualType SSizeTy = ACtx.getIntTypeForBitwidth(ACtx.getTypeSize(SizeTy), true);
465 
466   // Don't worry about truncation here, it'd be cast back to SIZE_MAX when used.
467   LLVM_ATTRIBUTE_UNUSED int64_t SizeMax =
468       BVF.getMaxValue(SizeTy).getLimitedValue();
469   int64_t SSizeMax =
470     BVF.getMaxValue(SSizeTy).getLimitedValue();
471 
472   // We are finally ready to define specifications for all supported functions.
473   //
474   // The signature needs to have the correct number of arguments.
475   // However, we insert `Irrelevant' when the type is insignificant.
476   //
477   // Argument ranges should always cover all variants. If return value
478   // is completely unknown, omit it from the respective range set.
479   //
480   // All types in the spec need to be canonical.
481   //
482   // Every item in the list of range sets represents a particular
483   // execution path the analyzer would need to explore once
484   // the call is modeled - a new program state is constructed
485   // for every range set, and each range line in the range set
486   // corresponds to a specific constraint within this state.
487   //
488   // Upon comparing to another argument, the other argument is casted
489   // to the current argument's type. This avoids proper promotion but
490   // seems useful. For example, read() receives size_t argument,
491   // and its return value, which is of type ssize_t, cannot be greater
492   // than this argument. If we made a promotion, and the size argument
493   // is equal to, say, 10, then we'd impose a range of [0, 10] on the
494   // return value, however the correct range is [-1, 10].
495   //
496   // Please update the list of functions in the header after editing!
497   //
498   // The format is as follows:
499   //
500   //{ "function name",
501   //  { spec:
502   //    { argument types list, ... },
503   //    return type, purity, { range set list:
504   //      { range list:
505   //        { argument index, within or out of, {{from, to}, ...} },
506   //        { argument index, compares to argument, {{how, which}} },
507   //        ...
508   //      }
509   //    }
510   //  }
511   //}
512 
513 #define SUMMARY(identifier, argument_types, return_type,                       \
514                 invalidation_approach)                                         \
515   {#identifier, {argument_types, return_type, invalidation_approach, {
516 #define END_SUMMARY }}},
517 #define ARGUMENT_TYPES(...) { __VA_ARGS__ }
518 #define RETURN_TYPE(x) x
519 #define INVALIDATION_APPROACH(x) x
520 #define CASE {
521 #define END_CASE },
522 #define ARGUMENT_CONDITION(argument_number, condition_kind)                    \
523   {argument_number, condition_kind, {
524 #define END_ARGUMENT_CONDITION }},
525 #define RETURN_VALUE_CONDITION(condition_kind)                                 \
526   { Ret, condition_kind, {
527 #define END_RETURN_VALUE_CONDITION }},
528 #define ARG_NO(x) x##U
529 #define RANGE(x, y) { x, y },
530 #define SINGLE_VALUE(x) RANGE(x, x)
531 #define IS_LESS_THAN(arg) { BO_LE, arg }
532 
533   FunctionSummaryMap = {
534     // The isascii() family of functions.
535     SUMMARY(isalnum, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
536             INVALIDATION_APPROACH(EvalCallAsPure))
537       CASE // Boils down to isupper() or islower() or isdigit()
538         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
539           RANGE('0', '9')
540           RANGE('A', 'Z')
541           RANGE('a', 'z')
542         END_ARGUMENT_CONDITION
543         RETURN_VALUE_CONDITION(OutOfRange)
544           SINGLE_VALUE(0)
545         END_RETURN_VALUE_CONDITION
546       END_CASE
547       CASE // The locale-specific range.
548         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
549           RANGE(128, 255)
550         END_ARGUMENT_CONDITION
551         // No post-condition. We are completely unaware of
552         // locale-specific return values.
553       END_CASE
554       CASE
555         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
556           RANGE('0', '9')
557           RANGE('A', 'Z')
558           RANGE('a', 'z')
559           RANGE(128, 255)
560         END_ARGUMENT_CONDITION
561         RETURN_VALUE_CONDITION(WithinRange)
562           SINGLE_VALUE(0)
563         END_RETURN_VALUE_CONDITION
564       END_CASE
565     END_SUMMARY
566     SUMMARY(isalpha, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
567             INVALIDATION_APPROACH(EvalCallAsPure))
568       CASE // isupper() or islower(). Note that 'Z' is less than 'a'.
569         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
570           RANGE('A', 'Z')
571           RANGE('a', 'z')
572         END_ARGUMENT_CONDITION
573         RETURN_VALUE_CONDITION(OutOfRange)
574           SINGLE_VALUE(0)
575         END_RETURN_VALUE_CONDITION
576       END_CASE
577       CASE // The locale-specific range.
578         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
579           RANGE(128, 255)
580         END_ARGUMENT_CONDITION
581       END_CASE
582       CASE // Other.
583         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
584           RANGE('A', 'Z')
585           RANGE('a', 'z')
586           RANGE(128, 255)
587         END_ARGUMENT_CONDITION
588         RETURN_VALUE_CONDITION(WithinRange)
589           SINGLE_VALUE(0)
590         END_RETURN_VALUE_CONDITION
591       END_CASE
592     END_SUMMARY
593     SUMMARY(isascii, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
594             INVALIDATION_APPROACH(EvalCallAsPure))
595       CASE // Is ASCII.
596         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
597           RANGE(0, 127)
598         END_ARGUMENT_CONDITION
599         RETURN_VALUE_CONDITION(OutOfRange)
600           SINGLE_VALUE(0)
601         END_RETURN_VALUE_CONDITION
602       END_CASE
603       CASE
604         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
605           RANGE(0, 127)
606         END_ARGUMENT_CONDITION
607         RETURN_VALUE_CONDITION(WithinRange)
608           SINGLE_VALUE(0)
609         END_RETURN_VALUE_CONDITION
610       END_CASE
611     END_SUMMARY
612     SUMMARY(isblank, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
613             INVALIDATION_APPROACH(EvalCallAsPure))
614       CASE
615         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
616           SINGLE_VALUE('\t')
617           SINGLE_VALUE(' ')
618         END_ARGUMENT_CONDITION
619         RETURN_VALUE_CONDITION(OutOfRange)
620           SINGLE_VALUE(0)
621         END_RETURN_VALUE_CONDITION
622       END_CASE
623       CASE
624         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
625           SINGLE_VALUE('\t')
626           SINGLE_VALUE(' ')
627         END_ARGUMENT_CONDITION
628         RETURN_VALUE_CONDITION(WithinRange)
629           SINGLE_VALUE(0)
630         END_RETURN_VALUE_CONDITION
631       END_CASE
632     END_SUMMARY
633     SUMMARY(iscntrl, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
634             INVALIDATION_APPROACH(EvalCallAsPure))
635       CASE // 0..31 or 127
636         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
637           RANGE(0, 32)
638           SINGLE_VALUE(127)
639         END_ARGUMENT_CONDITION
640         RETURN_VALUE_CONDITION(OutOfRange)
641           SINGLE_VALUE(0)
642         END_RETURN_VALUE_CONDITION
643       END_CASE
644       CASE
645         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
646           RANGE(0, 32)
647           SINGLE_VALUE(127)
648         END_ARGUMENT_CONDITION
649         RETURN_VALUE_CONDITION(WithinRange)
650           SINGLE_VALUE(0)
651         END_RETURN_VALUE_CONDITION
652       END_CASE
653     END_SUMMARY
654     SUMMARY(isdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
655             INVALIDATION_APPROACH(EvalCallAsPure))
656       CASE // Is a digit.
657         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
658           RANGE('0', '9')
659         END_ARGUMENT_CONDITION
660         RETURN_VALUE_CONDITION(OutOfRange)
661           SINGLE_VALUE(0)
662         END_RETURN_VALUE_CONDITION
663       END_CASE
664       CASE
665         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
666           RANGE('0', '9')
667         END_ARGUMENT_CONDITION
668         RETURN_VALUE_CONDITION(WithinRange)
669           SINGLE_VALUE(0)
670         END_RETURN_VALUE_CONDITION
671       END_CASE
672     END_SUMMARY
673     SUMMARY(isgraph, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
674             INVALIDATION_APPROACH(EvalCallAsPure))
675       CASE
676         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
677           RANGE(33, 126)
678         END_ARGUMENT_CONDITION
679         RETURN_VALUE_CONDITION(OutOfRange)
680           SINGLE_VALUE(0)
681         END_RETURN_VALUE_CONDITION
682       END_CASE
683       CASE
684         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
685           RANGE(33, 126)
686         END_ARGUMENT_CONDITION
687         RETURN_VALUE_CONDITION(WithinRange)
688           SINGLE_VALUE(0)
689         END_RETURN_VALUE_CONDITION
690       END_CASE
691     END_SUMMARY
692     SUMMARY(islower, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
693             INVALIDATION_APPROACH(EvalCallAsPure))
694       CASE // Is certainly lowercase.
695         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
696           RANGE('a', 'z')
697         END_ARGUMENT_CONDITION
698         RETURN_VALUE_CONDITION(OutOfRange)
699           SINGLE_VALUE(0)
700         END_RETURN_VALUE_CONDITION
701       END_CASE
702       CASE // Is ascii but not lowercase.
703         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
704           RANGE(0, 127)
705         END_ARGUMENT_CONDITION
706         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
707           RANGE('a', 'z')
708         END_ARGUMENT_CONDITION
709         RETURN_VALUE_CONDITION(WithinRange)
710           SINGLE_VALUE(0)
711         END_RETURN_VALUE_CONDITION
712       END_CASE
713       CASE // The locale-specific range.
714         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
715           RANGE(128, 255)
716         END_ARGUMENT_CONDITION
717       END_CASE
718       CASE // Is not an unsigned char.
719         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
720           RANGE(0, 255)
721         END_ARGUMENT_CONDITION
722         RETURN_VALUE_CONDITION(WithinRange)
723           SINGLE_VALUE(0)
724         END_RETURN_VALUE_CONDITION
725       END_CASE
726     END_SUMMARY
727     SUMMARY(isprint, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
728             INVALIDATION_APPROACH(EvalCallAsPure))
729       CASE
730         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
731           RANGE(32, 126)
732         END_ARGUMENT_CONDITION
733         RETURN_VALUE_CONDITION(OutOfRange)
734           SINGLE_VALUE(0)
735         END_RETURN_VALUE_CONDITION
736       END_CASE
737       CASE
738         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
739           RANGE(32, 126)
740         END_ARGUMENT_CONDITION
741         RETURN_VALUE_CONDITION(WithinRange)
742           SINGLE_VALUE(0)
743         END_RETURN_VALUE_CONDITION
744       END_CASE
745     END_SUMMARY
746     SUMMARY(ispunct, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
747             INVALIDATION_APPROACH(EvalCallAsPure))
748       CASE
749         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
750           RANGE('!', '/')
751           RANGE(':', '@')
752           RANGE('[', '`')
753           RANGE('{', '~')
754         END_ARGUMENT_CONDITION
755         RETURN_VALUE_CONDITION(OutOfRange)
756           SINGLE_VALUE(0)
757         END_RETURN_VALUE_CONDITION
758       END_CASE
759       CASE
760         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
761           RANGE('!', '/')
762           RANGE(':', '@')
763           RANGE('[', '`')
764           RANGE('{', '~')
765         END_ARGUMENT_CONDITION
766         RETURN_VALUE_CONDITION(WithinRange)
767           SINGLE_VALUE(0)
768         END_RETURN_VALUE_CONDITION
769       END_CASE
770     END_SUMMARY
771     SUMMARY(isspace, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
772             INVALIDATION_APPROACH(EvalCallAsPure))
773       CASE // Space, '\f', '\n', '\r', '\t', '\v'.
774         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
775           RANGE(9, 13)
776           SINGLE_VALUE(' ')
777         END_ARGUMENT_CONDITION
778         RETURN_VALUE_CONDITION(OutOfRange)
779           SINGLE_VALUE(0)
780         END_RETURN_VALUE_CONDITION
781       END_CASE
782       CASE // The locale-specific range.
783         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
784           RANGE(128, 255)
785         END_ARGUMENT_CONDITION
786       END_CASE
787       CASE
788         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
789           RANGE(9, 13)
790           SINGLE_VALUE(' ')
791           RANGE(128, 255)
792         END_ARGUMENT_CONDITION
793         RETURN_VALUE_CONDITION(WithinRange)
794           SINGLE_VALUE(0)
795         END_RETURN_VALUE_CONDITION
796       END_CASE
797     END_SUMMARY
798     SUMMARY(isupper, ARGUMENT_TYPES(IntTy), RETURN_TYPE (IntTy),
799             INVALIDATION_APPROACH(EvalCallAsPure))
800       CASE // Is certainly uppercase.
801         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
802           RANGE('A', 'Z')
803         END_ARGUMENT_CONDITION
804         RETURN_VALUE_CONDITION(OutOfRange)
805           SINGLE_VALUE(0)
806         END_RETURN_VALUE_CONDITION
807       END_CASE
808       CASE // The locale-specific range.
809         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
810           RANGE(128, 255)
811         END_ARGUMENT_CONDITION
812       END_CASE
813       CASE // Other.
814         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
815           RANGE('A', 'Z') RANGE(128, 255)
816         END_ARGUMENT_CONDITION
817         RETURN_VALUE_CONDITION(WithinRange)
818           SINGLE_VALUE(0)
819         END_RETURN_VALUE_CONDITION
820       END_CASE
821     END_SUMMARY
822     SUMMARY(isxdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
823             INVALIDATION_APPROACH(EvalCallAsPure))
824       CASE
825         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
826           RANGE('0', '9')
827           RANGE('A', 'F')
828           RANGE('a', 'f')
829         END_ARGUMENT_CONDITION
830         RETURN_VALUE_CONDITION(OutOfRange)
831           SINGLE_VALUE(0)
832         END_RETURN_VALUE_CONDITION
833       END_CASE
834       CASE
835         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
836           RANGE('0', '9')
837           RANGE('A', 'F')
838           RANGE('a', 'f')
839         END_ARGUMENT_CONDITION
840         RETURN_VALUE_CONDITION(WithinRange)
841           SINGLE_VALUE(0)
842         END_RETURN_VALUE_CONDITION
843       END_CASE
844     END_SUMMARY
845 
846     // The getc() family of functions that returns either a char or an EOF.
847     SUMMARY(getc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
848             INVALIDATION_APPROACH(NoEvalCall))
849       CASE // FIXME: EOF is assumed to be defined as -1.
850         RETURN_VALUE_CONDITION(WithinRange)
851           RANGE(-1, 255)
852         END_RETURN_VALUE_CONDITION
853       END_CASE
854     END_SUMMARY
855     SUMMARY(fgetc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
856             INVALIDATION_APPROACH(NoEvalCall))
857       CASE // FIXME: EOF is assumed to be defined as -1.
858         RETURN_VALUE_CONDITION(WithinRange)
859           RANGE(-1, 255)
860         END_RETURN_VALUE_CONDITION
861       END_CASE
862     END_SUMMARY
863     SUMMARY(getchar, ARGUMENT_TYPES(), RETURN_TYPE(IntTy),
864             INVALIDATION_APPROACH(NoEvalCall))
865       CASE // FIXME: EOF is assumed to be defined as -1.
866         RETURN_VALUE_CONDITION(WithinRange)
867           RANGE(-1, 255)
868         END_RETURN_VALUE_CONDITION
869       END_CASE
870     END_SUMMARY
871 
872     // read()-like functions that never return more than buffer size.
873     SUMMARY(read, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
874             RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall))
875       CASE
876         RETURN_VALUE_CONDITION(ComparesToArgument)
877           IS_LESS_THAN(ARG_NO(2))
878         END_RETURN_VALUE_CONDITION
879         RETURN_VALUE_CONDITION(WithinRange)
880           RANGE(-1, SSizeMax)
881         END_RETURN_VALUE_CONDITION
882       END_CASE
883     END_SUMMARY
884     SUMMARY(write, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
885             RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall))
886       CASE
887         RETURN_VALUE_CONDITION(ComparesToArgument)
888           IS_LESS_THAN(ARG_NO(2))
889         END_RETURN_VALUE_CONDITION
890         RETURN_VALUE_CONDITION(WithinRange)
891           RANGE(-1, SSizeMax)
892         END_RETURN_VALUE_CONDITION
893       END_CASE
894     END_SUMMARY
895     SUMMARY(fread,
896             ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
897             RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
898       CASE
899         RETURN_VALUE_CONDITION(ComparesToArgument)
900           IS_LESS_THAN(ARG_NO(2))
901         END_RETURN_VALUE_CONDITION
902       END_CASE
903     END_SUMMARY
904     SUMMARY(fwrite,
905             ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
906             RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
907       CASE
908         RETURN_VALUE_CONDITION(ComparesToArgument)
909           IS_LESS_THAN(ARG_NO(2))
910         END_RETURN_VALUE_CONDITION
911       END_CASE
912     END_SUMMARY
913 
914     // getline()-like functions either fail or read at least the delimiter.
915     SUMMARY(getline, ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
916             RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall))
917       CASE
918         RETURN_VALUE_CONDITION(WithinRange)
919           SINGLE_VALUE(-1)
920           RANGE(1, SSizeMax)
921         END_RETURN_VALUE_CONDITION
922       END_CASE
923     END_SUMMARY
924     SUMMARY(getdelim,
925             ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
926             RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall))
927       CASE
928         RETURN_VALUE_CONDITION(WithinRange)
929           SINGLE_VALUE(-1)
930           RANGE(1, SSizeMax)
931         END_RETURN_VALUE_CONDITION
932       END_CASE
933     END_SUMMARY
934   };
935 }
936 
937 void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
938   // If this checker grows large enough to support C++, Objective-C, or other
939   // standard libraries, we could use multiple register...Checker() functions,
940   // which would register various checkers with the help of the same Checker
941   // class, turning on different function summaries.
942   mgr.registerChecker<StdLibraryFunctionsChecker>();
943 }
944