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