xref: /llvm-project/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp (revision be1d49a508bdd2bbe43926418df5996a61f22406)
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   int64_t SizeMax =
468       BVF.getMaxValue(SizeTy).getLimitedValue();
469   int64_t SSizeMax =
470     BVF.getMaxValue(SSizeTy).getLimitedValue();
471   (void)SizeMax;
472 
473   // We are finally ready to define specifications for all supported functions.
474   //
475   // The signature needs to have the correct number of arguments.
476   // However, we insert `Irrelevant' when the type is insignificant.
477   //
478   // Argument ranges should always cover all variants. If return value
479   // is completely unknown, omit it from the respective range set.
480   //
481   // All types in the spec need to be canonical.
482   //
483   // Every item in the list of range sets represents a particular
484   // execution path the analyzer would need to explore once
485   // the call is modeled - a new program state is constructed
486   // for every range set, and each range line in the range set
487   // corresponds to a specific constraint within this state.
488   //
489   // Upon comparing to another argument, the other argument is casted
490   // to the current argument's type. This avoids proper promotion but
491   // seems useful. For example, read() receives size_t argument,
492   // and its return value, which is of type ssize_t, cannot be greater
493   // than this argument. If we made a promotion, and the size argument
494   // is equal to, say, 10, then we'd impose a range of [0, 10] on the
495   // return value, however the correct range is [-1, 10].
496   //
497   // Please update the list of functions in the header after editing!
498   //
499   // The format is as follows:
500   //
501   //{ "function name",
502   //  { spec:
503   //    { argument types list, ... },
504   //    return type, purity, { range set list:
505   //      { range list:
506   //        { argument index, within or out of, {{from, to}, ...} },
507   //        { argument index, compares to argument, {{how, which}} },
508   //        ...
509   //      }
510   //    }
511   //  }
512   //}
513 
514 #define SUMMARY(identifier, argument_types, return_type,                       \
515                 invalidation_approach)                                         \
516   {#identifier, {argument_types, return_type, invalidation_approach, {
517 #define END_SUMMARY }}},
518 #define ARGUMENT_TYPES(...) { __VA_ARGS__ }
519 #define RETURN_TYPE(x) x
520 #define INVALIDATION_APPROACH(x) x
521 #define CASE {
522 #define END_CASE },
523 #define ARGUMENT_CONDITION(argument_number, condition_kind)                    \
524   {argument_number, condition_kind, {
525 #define END_ARGUMENT_CONDITION }},
526 #define RETURN_VALUE_CONDITION(condition_kind)                                 \
527   { Ret, condition_kind, {
528 #define END_RETURN_VALUE_CONDITION }},
529 #define ARG_NO(x) x##U
530 #define RANGE(x, y) { x, y },
531 #define SINGLE_VALUE(x) RANGE(x, x)
532 #define IS_LESS_THAN(arg) { BO_LE, arg }
533 
534   FunctionSummaryMap = {
535     // The isascii() family of functions.
536     SUMMARY(isalnum, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
537             INVALIDATION_APPROACH(EvalCallAsPure))
538       CASE // Boils down to isupper() or islower() or isdigit()
539         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
540           RANGE('0', '9')
541           RANGE('A', 'Z')
542           RANGE('a', 'z')
543         END_ARGUMENT_CONDITION
544         RETURN_VALUE_CONDITION(OutOfRange)
545           SINGLE_VALUE(0)
546         END_RETURN_VALUE_CONDITION
547       END_CASE
548       CASE // The locale-specific range.
549         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
550           RANGE(128, 255)
551         END_ARGUMENT_CONDITION
552         // No post-condition. We are completely unaware of
553         // locale-specific return values.
554       END_CASE
555       CASE
556         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
557           RANGE('0', '9')
558           RANGE('A', 'Z')
559           RANGE('a', 'z')
560           RANGE(128, 255)
561         END_ARGUMENT_CONDITION
562         RETURN_VALUE_CONDITION(WithinRange)
563           SINGLE_VALUE(0)
564         END_RETURN_VALUE_CONDITION
565       END_CASE
566     END_SUMMARY
567     SUMMARY(isalpha, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
568             INVALIDATION_APPROACH(EvalCallAsPure))
569       CASE // isupper() or islower(). Note that 'Z' is less than 'a'.
570         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
571           RANGE('A', 'Z')
572           RANGE('a', 'z')
573         END_ARGUMENT_CONDITION
574         RETURN_VALUE_CONDITION(OutOfRange)
575           SINGLE_VALUE(0)
576         END_RETURN_VALUE_CONDITION
577       END_CASE
578       CASE // The locale-specific range.
579         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
580           RANGE(128, 255)
581         END_ARGUMENT_CONDITION
582       END_CASE
583       CASE // Other.
584         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
585           RANGE('A', 'Z')
586           RANGE('a', 'z')
587           RANGE(128, 255)
588         END_ARGUMENT_CONDITION
589         RETURN_VALUE_CONDITION(WithinRange)
590           SINGLE_VALUE(0)
591         END_RETURN_VALUE_CONDITION
592       END_CASE
593     END_SUMMARY
594     SUMMARY(isascii, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
595             INVALIDATION_APPROACH(EvalCallAsPure))
596       CASE // Is ASCII.
597         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
598           RANGE(0, 127)
599         END_ARGUMENT_CONDITION
600         RETURN_VALUE_CONDITION(OutOfRange)
601           SINGLE_VALUE(0)
602         END_RETURN_VALUE_CONDITION
603       END_CASE
604       CASE
605         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
606           RANGE(0, 127)
607         END_ARGUMENT_CONDITION
608         RETURN_VALUE_CONDITION(WithinRange)
609           SINGLE_VALUE(0)
610         END_RETURN_VALUE_CONDITION
611       END_CASE
612     END_SUMMARY
613     SUMMARY(isblank, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
614             INVALIDATION_APPROACH(EvalCallAsPure))
615       CASE
616         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
617           SINGLE_VALUE('\t')
618           SINGLE_VALUE(' ')
619         END_ARGUMENT_CONDITION
620         RETURN_VALUE_CONDITION(OutOfRange)
621           SINGLE_VALUE(0)
622         END_RETURN_VALUE_CONDITION
623       END_CASE
624       CASE
625         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
626           SINGLE_VALUE('\t')
627           SINGLE_VALUE(' ')
628         END_ARGUMENT_CONDITION
629         RETURN_VALUE_CONDITION(WithinRange)
630           SINGLE_VALUE(0)
631         END_RETURN_VALUE_CONDITION
632       END_CASE
633     END_SUMMARY
634     SUMMARY(iscntrl, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
635             INVALIDATION_APPROACH(EvalCallAsPure))
636       CASE // 0..31 or 127
637         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
638           RANGE(0, 32)
639           SINGLE_VALUE(127)
640         END_ARGUMENT_CONDITION
641         RETURN_VALUE_CONDITION(OutOfRange)
642           SINGLE_VALUE(0)
643         END_RETURN_VALUE_CONDITION
644       END_CASE
645       CASE
646         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
647           RANGE(0, 32)
648           SINGLE_VALUE(127)
649         END_ARGUMENT_CONDITION
650         RETURN_VALUE_CONDITION(WithinRange)
651           SINGLE_VALUE(0)
652         END_RETURN_VALUE_CONDITION
653       END_CASE
654     END_SUMMARY
655     SUMMARY(isdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
656             INVALIDATION_APPROACH(EvalCallAsPure))
657       CASE // Is a digit.
658         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
659           RANGE('0', '9')
660         END_ARGUMENT_CONDITION
661         RETURN_VALUE_CONDITION(OutOfRange)
662           SINGLE_VALUE(0)
663         END_RETURN_VALUE_CONDITION
664       END_CASE
665       CASE
666         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
667           RANGE('0', '9')
668         END_ARGUMENT_CONDITION
669         RETURN_VALUE_CONDITION(WithinRange)
670           SINGLE_VALUE(0)
671         END_RETURN_VALUE_CONDITION
672       END_CASE
673     END_SUMMARY
674     SUMMARY(isgraph, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
675             INVALIDATION_APPROACH(EvalCallAsPure))
676       CASE
677         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
678           RANGE(33, 126)
679         END_ARGUMENT_CONDITION
680         RETURN_VALUE_CONDITION(OutOfRange)
681           SINGLE_VALUE(0)
682         END_RETURN_VALUE_CONDITION
683       END_CASE
684       CASE
685         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
686           RANGE(33, 126)
687         END_ARGUMENT_CONDITION
688         RETURN_VALUE_CONDITION(WithinRange)
689           SINGLE_VALUE(0)
690         END_RETURN_VALUE_CONDITION
691       END_CASE
692     END_SUMMARY
693     SUMMARY(islower, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
694             INVALIDATION_APPROACH(EvalCallAsPure))
695       CASE // Is certainly lowercase.
696         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
697           RANGE('a', 'z')
698         END_ARGUMENT_CONDITION
699         RETURN_VALUE_CONDITION(OutOfRange)
700           SINGLE_VALUE(0)
701         END_RETURN_VALUE_CONDITION
702       END_CASE
703       CASE // Is ascii but not lowercase.
704         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
705           RANGE(0, 127)
706         END_ARGUMENT_CONDITION
707         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
708           RANGE('a', 'z')
709         END_ARGUMENT_CONDITION
710         RETURN_VALUE_CONDITION(WithinRange)
711           SINGLE_VALUE(0)
712         END_RETURN_VALUE_CONDITION
713       END_CASE
714       CASE // The locale-specific range.
715         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
716           RANGE(128, 255)
717         END_ARGUMENT_CONDITION
718       END_CASE
719       CASE // Is not an unsigned char.
720         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
721           RANGE(0, 255)
722         END_ARGUMENT_CONDITION
723         RETURN_VALUE_CONDITION(WithinRange)
724           SINGLE_VALUE(0)
725         END_RETURN_VALUE_CONDITION
726       END_CASE
727     END_SUMMARY
728     SUMMARY(isprint, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
729             INVALIDATION_APPROACH(EvalCallAsPure))
730       CASE
731         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
732           RANGE(32, 126)
733         END_ARGUMENT_CONDITION
734         RETURN_VALUE_CONDITION(OutOfRange)
735           SINGLE_VALUE(0)
736         END_RETURN_VALUE_CONDITION
737       END_CASE
738       CASE
739         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
740           RANGE(32, 126)
741         END_ARGUMENT_CONDITION
742         RETURN_VALUE_CONDITION(WithinRange)
743           SINGLE_VALUE(0)
744         END_RETURN_VALUE_CONDITION
745       END_CASE
746     END_SUMMARY
747     SUMMARY(ispunct, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
748             INVALIDATION_APPROACH(EvalCallAsPure))
749       CASE
750         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
751           RANGE('!', '/')
752           RANGE(':', '@')
753           RANGE('[', '`')
754           RANGE('{', '~')
755         END_ARGUMENT_CONDITION
756         RETURN_VALUE_CONDITION(OutOfRange)
757           SINGLE_VALUE(0)
758         END_RETURN_VALUE_CONDITION
759       END_CASE
760       CASE
761         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
762           RANGE('!', '/')
763           RANGE(':', '@')
764           RANGE('[', '`')
765           RANGE('{', '~')
766         END_ARGUMENT_CONDITION
767         RETURN_VALUE_CONDITION(WithinRange)
768           SINGLE_VALUE(0)
769         END_RETURN_VALUE_CONDITION
770       END_CASE
771     END_SUMMARY
772     SUMMARY(isspace, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
773             INVALIDATION_APPROACH(EvalCallAsPure))
774       CASE // Space, '\f', '\n', '\r', '\t', '\v'.
775         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
776           RANGE(9, 13)
777           SINGLE_VALUE(' ')
778         END_ARGUMENT_CONDITION
779         RETURN_VALUE_CONDITION(OutOfRange)
780           SINGLE_VALUE(0)
781         END_RETURN_VALUE_CONDITION
782       END_CASE
783       CASE // The locale-specific range.
784         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
785           RANGE(128, 255)
786         END_ARGUMENT_CONDITION
787       END_CASE
788       CASE
789         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
790           RANGE(9, 13)
791           SINGLE_VALUE(' ')
792           RANGE(128, 255)
793         END_ARGUMENT_CONDITION
794         RETURN_VALUE_CONDITION(WithinRange)
795           SINGLE_VALUE(0)
796         END_RETURN_VALUE_CONDITION
797       END_CASE
798     END_SUMMARY
799     SUMMARY(isupper, ARGUMENT_TYPES(IntTy), RETURN_TYPE (IntTy),
800             INVALIDATION_APPROACH(EvalCallAsPure))
801       CASE // Is certainly uppercase.
802         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
803           RANGE('A', 'Z')
804         END_ARGUMENT_CONDITION
805         RETURN_VALUE_CONDITION(OutOfRange)
806           SINGLE_VALUE(0)
807         END_RETURN_VALUE_CONDITION
808       END_CASE
809       CASE // The locale-specific range.
810         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
811           RANGE(128, 255)
812         END_ARGUMENT_CONDITION
813       END_CASE
814       CASE // Other.
815         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
816           RANGE('A', 'Z') RANGE(128, 255)
817         END_ARGUMENT_CONDITION
818         RETURN_VALUE_CONDITION(WithinRange)
819           SINGLE_VALUE(0)
820         END_RETURN_VALUE_CONDITION
821       END_CASE
822     END_SUMMARY
823     SUMMARY(isxdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
824             INVALIDATION_APPROACH(EvalCallAsPure))
825       CASE
826         ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
827           RANGE('0', '9')
828           RANGE('A', 'F')
829           RANGE('a', 'f')
830         END_ARGUMENT_CONDITION
831         RETURN_VALUE_CONDITION(OutOfRange)
832           SINGLE_VALUE(0)
833         END_RETURN_VALUE_CONDITION
834       END_CASE
835       CASE
836         ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
837           RANGE('0', '9')
838           RANGE('A', 'F')
839           RANGE('a', 'f')
840         END_ARGUMENT_CONDITION
841         RETURN_VALUE_CONDITION(WithinRange)
842           SINGLE_VALUE(0)
843         END_RETURN_VALUE_CONDITION
844       END_CASE
845     END_SUMMARY
846 
847     // The getc() family of functions that returns either a char or an EOF.
848     SUMMARY(getc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
849             INVALIDATION_APPROACH(NoEvalCall))
850       CASE // FIXME: EOF is assumed to be defined as -1.
851         RETURN_VALUE_CONDITION(WithinRange)
852           RANGE(-1, 255)
853         END_RETURN_VALUE_CONDITION
854       END_CASE
855     END_SUMMARY
856     SUMMARY(fgetc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
857             INVALIDATION_APPROACH(NoEvalCall))
858       CASE // FIXME: EOF is assumed to be defined as -1.
859         RETURN_VALUE_CONDITION(WithinRange)
860           RANGE(-1, 255)
861         END_RETURN_VALUE_CONDITION
862       END_CASE
863     END_SUMMARY
864     SUMMARY(getchar, ARGUMENT_TYPES(), RETURN_TYPE(IntTy),
865             INVALIDATION_APPROACH(NoEvalCall))
866       CASE // FIXME: EOF is assumed to be defined as -1.
867         RETURN_VALUE_CONDITION(WithinRange)
868           RANGE(-1, 255)
869         END_RETURN_VALUE_CONDITION
870       END_CASE
871     END_SUMMARY
872 
873     // read()-like functions that never return more than buffer size.
874     SUMMARY(read, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
875             RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall))
876       CASE
877         RETURN_VALUE_CONDITION(ComparesToArgument)
878           IS_LESS_THAN(ARG_NO(2))
879         END_RETURN_VALUE_CONDITION
880         RETURN_VALUE_CONDITION(WithinRange)
881           RANGE(-1, SSizeMax)
882         END_RETURN_VALUE_CONDITION
883       END_CASE
884     END_SUMMARY
885     SUMMARY(write, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
886             RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall))
887       CASE
888         RETURN_VALUE_CONDITION(ComparesToArgument)
889           IS_LESS_THAN(ARG_NO(2))
890         END_RETURN_VALUE_CONDITION
891         RETURN_VALUE_CONDITION(WithinRange)
892           RANGE(-1, SSizeMax)
893         END_RETURN_VALUE_CONDITION
894       END_CASE
895     END_SUMMARY
896     SUMMARY(fread,
897             ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
898             RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
899       CASE
900         RETURN_VALUE_CONDITION(ComparesToArgument)
901           IS_LESS_THAN(ARG_NO(2))
902         END_RETURN_VALUE_CONDITION
903       END_CASE
904     END_SUMMARY
905     SUMMARY(fwrite,
906             ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
907             RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
908       CASE
909         RETURN_VALUE_CONDITION(ComparesToArgument)
910           IS_LESS_THAN(ARG_NO(2))
911         END_RETURN_VALUE_CONDITION
912       END_CASE
913     END_SUMMARY
914 
915     // getline()-like functions either fail or read at least the delimiter.
916     SUMMARY(getline, ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
917             RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall))
918       CASE
919         RETURN_VALUE_CONDITION(WithinRange)
920           SINGLE_VALUE(-1)
921           RANGE(1, SSizeMax)
922         END_RETURN_VALUE_CONDITION
923       END_CASE
924     END_SUMMARY
925     SUMMARY(getdelim,
926             ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
927             RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall))
928       CASE
929         RETURN_VALUE_CONDITION(WithinRange)
930           SINGLE_VALUE(-1)
931           RANGE(1, SSizeMax)
932         END_RETURN_VALUE_CONDITION
933       END_CASE
934     END_SUMMARY
935   };
936 }
937 
938 void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
939   // If this checker grows large enough to support C++, Objective-C, or other
940   // standard libraries, we could use multiple register...Checker() functions,
941   // which would register various checkers with the help of the same Checker
942   // class, turning on different function summaries.
943   mgr.registerChecker<StdLibraryFunctionsChecker>();
944 }
945