123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065 |
- //=== StdLibraryFunctionsChecker.cpp - Model standard functions -*- C++ -*-===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // This checker improves modeling of a few simple library functions.
- // It does not generate warnings.
- //
- // This checker provides a specification format - `FunctionSummaryTy' - and
- // contains descriptions of some library functions in this format. Each
- // specification contains a list of branches for splitting the program state
- // upon call, and range constraints on argument and return-value symbols that
- // are satisfied on each branch. This spec can be expanded to include more
- // items, like external effects of the function.
- //
- // The main difference between this approach and the body farms technique is
- // in more explicit control over how many branches are produced. For example,
- // consider standard C function `ispunct(int x)', which returns a non-zero value
- // iff `x' is a punctuation character, that is, when `x' is in range
- // ['!', '/'] [':', '@'] U ['[', '\`'] U ['{', '~'].
- // `FunctionSummaryTy' provides only two branches for this function. However,
- // any attempt to describe this range with if-statements in the body farm
- // would result in many more branches. Because each branch needs to be analyzed
- // independently, this significantly reduces performance. Additionally,
- // once we consider a branch on which `x' is in range, say, ['!', '/'],
- // we assume that such branch is an important separate path through the program,
- // which may lead to false positives because considering this particular path
- // was not consciously intended, and therefore it might have been unreachable.
- //
- // This checker uses eval::Call for modeling "pure" functions, for which
- // their `FunctionSummaryTy' is a precise model. This avoids unnecessary
- // invalidation passes. Conflicts with other checkers are unlikely because
- // if the function has no other effects, other checkers would probably never
- // want to improve upon the modeling done by this checker.
- //
- // Non-"pure" functions, for which only partial improvement over the default
- // behavior is expected, are modeled via check::PostCall, non-intrusively.
- //
- // The following standard C functions are currently supported:
- //
- // fgetc getline isdigit isupper
- // fread isalnum isgraph isxdigit
- // fwrite isalpha islower read
- // getc isascii isprint write
- // getchar isblank ispunct
- // getdelim iscntrl isspace
- //
- //===----------------------------------------------------------------------===//
- #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
- #include "clang/StaticAnalyzer/Core/Checker.h"
- #include "clang/StaticAnalyzer/Core/CheckerManager.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
- using namespace clang;
- using namespace clang::ento;
- namespace {
- class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> {
- /// Below is a series of typedefs necessary to define function specs.
- /// We avoid nesting types here because each additional qualifier
- /// would need to be repeated in every function spec.
- struct FunctionSummaryTy;
- /// Specify how much the analyzer engine should entrust modeling this function
- /// to us. If he doesn't, he performs additional invalidations.
- enum InvalidationKindTy { NoEvalCall, EvalCallAsPure };
- /// A pair of ValueRangeKindTy and IntRangeVectorTy would describe a range
- /// imposed on a particular argument or return value symbol.
- ///
- /// Given a range, should the argument stay inside or outside this range?
- /// The special `ComparesToArgument' value indicates that we should
- /// impose a constraint that involves other argument or return value symbols.
- enum ValueRangeKindTy { OutOfRange, WithinRange, ComparesToArgument };
- // The universal integral type to use in value range descriptions.
- // Unsigned to make sure overflows are well-defined.
- typedef uint64_t RangeIntTy;
- /// Normally, describes a single range constraint, eg. {{0, 1}, {3, 4}} is
- /// a non-negative integer, which less than 5 and not equal to 2. For
- /// `ComparesToArgument', holds information about how exactly to compare to
- /// the argument.
- typedef std::vector<std::pair<RangeIntTy, RangeIntTy>> IntRangeVectorTy;
- /// A reference to an argument or return value by its number.
- /// ArgNo in CallExpr and CallEvent is defined as Unsigned, but
- /// obviously uint32_t should be enough for all practical purposes.
- typedef uint32_t ArgNoTy;
- static const ArgNoTy Ret = std::numeric_limits<ArgNoTy>::max();
- /// Incapsulates a single range on a single symbol within a branch.
- class ValueRange {
- ArgNoTy ArgNo; // Argument to which we apply the range.
- ValueRangeKindTy Kind; // Kind of range definition.
- IntRangeVectorTy Args; // Polymorphic arguments.
- public:
- ValueRange(ArgNoTy ArgNo, ValueRangeKindTy Kind,
- const IntRangeVectorTy &Args)
- : ArgNo(ArgNo), Kind(Kind), Args(Args) {}
- ArgNoTy getArgNo() const { return ArgNo; }
- ValueRangeKindTy getKind() const { return Kind; }
- BinaryOperator::Opcode getOpcode() const {
- assert(Kind == ComparesToArgument);
- assert(Args.size() == 1);
- BinaryOperator::Opcode Op =
- static_cast<BinaryOperator::Opcode>(Args[0].first);
- assert(BinaryOperator::isComparisonOp(Op) &&
- "Only comparison ops are supported for ComparesToArgument");
- return Op;
- }
- ArgNoTy getOtherArgNo() const {
- assert(Kind == ComparesToArgument);
- assert(Args.size() == 1);
- return static_cast<ArgNoTy>(Args[0].second);
- }
- const IntRangeVectorTy &getRanges() const {
- assert(Kind != ComparesToArgument);
- return Args;
- }
- // We avoid creating a virtual apply() method because
- // it makes initializer lists harder to write.
- private:
- ProgramStateRef
- applyAsOutOfRange(ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const;
- ProgramStateRef
- applyAsWithinRange(ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const;
- ProgramStateRef
- applyAsComparesToArgument(ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const;
- public:
- ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const {
- switch (Kind) {
- case OutOfRange:
- return applyAsOutOfRange(State, Call, Summary);
- case WithinRange:
- return applyAsWithinRange(State, Call, Summary);
- case ComparesToArgument:
- return applyAsComparesToArgument(State, Call, Summary);
- }
- llvm_unreachable("Unknown ValueRange kind!");
- }
- };
- /// The complete list of ranges that defines a single branch.
- typedef std::vector<ValueRange> ValueRangeSet;
- /// Includes information about function prototype (which is necessary to
- /// ensure we're modeling the right function and casting values properly),
- /// approach to invalidation, and a list of branches - essentially, a list
- /// of list of ranges - essentially, a list of lists of lists of segments.
- struct FunctionSummaryTy {
- const std::vector<QualType> ArgTypes;
- const QualType RetType;
- const InvalidationKindTy InvalidationKind;
- const std::vector<ValueRangeSet> Ranges;
- private:
- static void assertTypeSuitableForSummary(QualType T) {
- assert(!T->isVoidType() &&
- "We should have had no significant void types in the spec");
- assert(T.isCanonical() &&
- "We should only have canonical types in the spec");
- // FIXME: lift this assert (but not the ones above!)
- assert(T->isIntegralOrEnumerationType() &&
- "We only support integral ranges in the spec");
- }
- public:
- QualType getArgType(ArgNoTy ArgNo) const {
- QualType T = (ArgNo == Ret) ? RetType : ArgTypes[ArgNo];
- assertTypeSuitableForSummary(T);
- return T;
- }
- /// Try our best to figure out if the call expression is the call of
- /// *the* library function to which this specification applies.
- bool matchesCall(const CallExpr *CE) const;
- };
- // The same function (as in, function identifier) may have different
- // summaries assigned to it, with different argument and return value types.
- // We call these "variants" of the function. This can be useful for handling
- // C++ function overloads, and also it can be used when the same function
- // may have different definitions on different platforms.
- typedef std::vector<FunctionSummaryTy> FunctionVariantsTy;
- // The map of all functions supported by the checker. It is initialized
- // lazily, and it doesn't change after initialization.
- typedef llvm::StringMap<FunctionVariantsTy> FunctionSummaryMapTy;
- mutable FunctionSummaryMapTy FunctionSummaryMap;
- // Auxiliary functions to support ArgNoTy within all structures
- // in a unified manner.
- static QualType getArgType(const FunctionSummaryTy &Summary, ArgNoTy ArgNo) {
- return Summary.getArgType(ArgNo);
- }
- static QualType getArgType(const CallEvent &Call, ArgNoTy ArgNo) {
- return ArgNo == Ret ? Call.getResultType().getCanonicalType()
- : Call.getArgExpr(ArgNo)->getType().getCanonicalType();
- }
- static QualType getArgType(const CallExpr *CE, ArgNoTy ArgNo) {
- return ArgNo == Ret ? CE->getType().getCanonicalType()
- : CE->getArg(ArgNo)->getType().getCanonicalType();
- }
- static SVal getArgSVal(const CallEvent &Call, ArgNoTy ArgNo) {
- return ArgNo == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgNo);
- }
- public:
- void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
- bool evalCall(const CallEvent &Call, CheckerContext &C) const;
- private:
- Optional<FunctionSummaryTy> findFunctionSummary(const FunctionDecl *FD,
- const CallExpr *CE,
- CheckerContext &C) const;
- void initFunctionSummaries(BasicValueFactory &BVF) const;
- };
- } // end of anonymous namespace
- ProgramStateRef StdLibraryFunctionsChecker::ValueRange::applyAsOutOfRange(
- ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const {
- ProgramStateManager &Mgr = State->getStateManager();
- SValBuilder &SVB = Mgr.getSValBuilder();
- BasicValueFactory &BVF = SVB.getBasicValueFactory();
- ConstraintManager &CM = Mgr.getConstraintManager();
- QualType T = getArgType(Summary, getArgNo());
- SVal V = getArgSVal(Call, getArgNo());
- if (auto N = V.getAs<NonLoc>()) {
- const IntRangeVectorTy &R = getRanges();
- size_t E = R.size();
- for (size_t I = 0; I != E; ++I) {
- const llvm::APSInt &Min = BVF.getValue(R[I].first, T);
- const llvm::APSInt &Max = BVF.getValue(R[I].second, T);
- assert(Min <= Max);
- State = CM.assumeInclusiveRange(State, *N, Min, Max, false);
- if (!State)
- break;
- }
- }
- return State;
- }
- ProgramStateRef
- StdLibraryFunctionsChecker::ValueRange::applyAsWithinRange(
- ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const {
- ProgramStateManager &Mgr = State->getStateManager();
- SValBuilder &SVB = Mgr.getSValBuilder();
- BasicValueFactory &BVF = SVB.getBasicValueFactory();
- ConstraintManager &CM = Mgr.getConstraintManager();
- QualType T = getArgType(Summary, getArgNo());
- SVal V = getArgSVal(Call, getArgNo());
- // "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R".
- // We cut off [T_MIN, min(R) - 1] and [max(R) + 1, T_MAX] if necessary,
- // and then cut away all holes in R one by one.
- if (auto N = V.getAs<NonLoc>()) {
- const IntRangeVectorTy &R = getRanges();
- size_t E = R.size();
- const llvm::APSInt &MinusInf = BVF.getMinValue(T);
- const llvm::APSInt &PlusInf = BVF.getMaxValue(T);
- const llvm::APSInt &Left = BVF.getValue(R[0].first - 1ULL, T);
- if (Left != PlusInf) {
- assert(MinusInf <= Left);
- State = CM.assumeInclusiveRange(State, *N, MinusInf, Left, false);
- if (!State)
- return nullptr;
- }
- const llvm::APSInt &Right = BVF.getValue(R[E - 1].second + 1ULL, T);
- if (Right != MinusInf) {
- assert(Right <= PlusInf);
- State = CM.assumeInclusiveRange(State, *N, Right, PlusInf, false);
- if (!State)
- return nullptr;
- }
- for (size_t I = 1; I != E; ++I) {
- const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, T);
- const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, T);
- assert(Min <= Max);
- State = CM.assumeInclusiveRange(State, *N, Min, Max, false);
- if (!State)
- return nullptr;
- }
- }
- return State;
- }
- ProgramStateRef
- StdLibraryFunctionsChecker::ValueRange::applyAsComparesToArgument(
- ProgramStateRef State, const CallEvent &Call,
- const FunctionSummaryTy &Summary) const {
- ProgramStateManager &Mgr = State->getStateManager();
- SValBuilder &SVB = Mgr.getSValBuilder();
- QualType CondT = SVB.getConditionType();
- QualType T = getArgType(Summary, getArgNo());
- SVal V = getArgSVal(Call, getArgNo());
- BinaryOperator::Opcode Op = getOpcode();
- ArgNoTy OtherArg = getOtherArgNo();
- SVal OtherV = getArgSVal(Call, OtherArg);
- QualType OtherT = getArgType(Call, OtherArg);
- // Note: we avoid integral promotion for comparison.
- OtherV = SVB.evalCast(OtherV, T, OtherT);
- if (auto CompV = SVB.evalBinOp(State, Op, V, OtherV, CondT)
- .getAs<DefinedOrUnknownSVal>())
- State = State->assume(*CompV, true);
- return State;
- }
- void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
- CheckerContext &C) const {
- const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!FD)
- return;
- const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
- if (!CE)
- return;
- Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C);
- if (!FoundSummary)
- return;
- // Now apply ranges.
- const FunctionSummaryTy &Summary = *FoundSummary;
- ProgramStateRef State = C.getState();
- for (const auto &VRS: Summary.Ranges) {
- ProgramStateRef NewState = State;
- for (const auto &VR: VRS) {
- NewState = VR.apply(NewState, Call, Summary);
- if (!NewState)
- break;
- }
- if (NewState && NewState != State)
- C.addTransition(NewState);
- }
- }
- bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call,
- CheckerContext &C) const {
- const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
- if (!FD)
- return false;
- const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
- if (!CE)
- return false;
- Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C);
- if (!FoundSummary)
- return false;
- const FunctionSummaryTy &Summary = *FoundSummary;
- switch (Summary.InvalidationKind) {
- case EvalCallAsPure: {
- ProgramStateRef State = C.getState();
- const LocationContext *LC = C.getLocationContext();
- SVal V = C.getSValBuilder().conjureSymbolVal(
- CE, LC, CE->getType().getCanonicalType(), C.blockCount());
- State = State->BindExpr(CE, LC, V);
- C.addTransition(State);
- return true;
- }
- case NoEvalCall:
- // Summary tells us to avoid performing eval::Call. The function is possibly
- // evaluated by another checker, or evaluated conservatively.
- return false;
- }
- llvm_unreachable("Unknown invalidation kind!");
- }
- bool StdLibraryFunctionsChecker::FunctionSummaryTy::matchesCall(
- const CallExpr *CE) const {
- // Check number of arguments:
- if (CE->getNumArgs() != ArgTypes.size())
- return false;
- // Check return type if relevant:
- if (!RetType.isNull() && RetType != CE->getType().getCanonicalType())
- return false;
- // Check argument types when relevant:
- for (size_t I = 0, E = ArgTypes.size(); I != E; ++I) {
- QualType FormalT = ArgTypes[I];
- // Null type marks irrelevant arguments.
- if (FormalT.isNull())
- continue;
- assertTypeSuitableForSummary(FormalT);
- QualType ActualT = StdLibraryFunctionsChecker::getArgType(CE, I);
- assert(ActualT.isCanonical());
- if (ActualT != FormalT)
- return false;
- }
- return true;
- }
- Optional<StdLibraryFunctionsChecker::FunctionSummaryTy>
- StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD,
- const CallExpr *CE,
- CheckerContext &C) const {
- // Note: we cannot always obtain FD from CE
- // (eg. virtual call, or call by pointer).
- assert(CE);
- if (!FD)
- return None;
- SValBuilder &SVB = C.getSValBuilder();
- BasicValueFactory &BVF = SVB.getBasicValueFactory();
- initFunctionSummaries(BVF);
- IdentifierInfo *II = FD->getIdentifier();
- if (!II)
- return None;
- StringRef Name = II->getName();
- if (Name.empty() || !C.isCLibraryFunction(FD, Name))
- return None;
- auto FSMI = FunctionSummaryMap.find(Name);
- if (FSMI == FunctionSummaryMap.end())
- return None;
- // Verify that function signature matches the spec in advance.
- // Otherwise we might be modeling the wrong function.
- // Strict checking is important because we will be conducting
- // very integral-type-sensitive operations on arguments and
- // return values.
- const FunctionVariantsTy &SpecVariants = FSMI->second;
- for (const FunctionSummaryTy &Spec : SpecVariants)
- if (Spec.matchesCall(CE))
- return Spec;
- return None;
- }
- void StdLibraryFunctionsChecker::initFunctionSummaries(
- BasicValueFactory &BVF) const {
- if (!FunctionSummaryMap.empty())
- return;
- ASTContext &ACtx = BVF.getContext();
- // These types are useful for writing specifications quickly,
- // New specifications should probably introduce more types.
- // Some types are hard to obtain from the AST, eg. "ssize_t".
- // In such cases it should be possible to provide multiple variants
- // of function summary for common cases (eg. ssize_t could be int or long
- // or long long, so three summary variants would be enough).
- // Of course, function variants are also useful for C++ overloads.
- QualType Irrelevant; // A placeholder, whenever we do not care about the type.
- QualType IntTy = ACtx.IntTy;
- QualType LongTy = ACtx.LongTy;
- QualType LongLongTy = ACtx.LongLongTy;
- QualType SizeTy = ACtx.getSizeType();
- RangeIntTy IntMax = BVF.getMaxValue(IntTy).getLimitedValue();
- RangeIntTy LongMax = BVF.getMaxValue(LongTy).getLimitedValue();
- RangeIntTy LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue();
- // We are finally ready to define specifications for all supported functions.
- //
- // The signature needs to have the correct number of arguments.
- // However, we insert `Irrelevant' when the type is insignificant.
- //
- // Argument ranges should always cover all variants. If return value
- // is completely unknown, omit it from the respective range set.
- //
- // All types in the spec need to be canonical.
- //
- // Every item in the list of range sets represents a particular
- // execution path the analyzer would need to explore once
- // the call is modeled - a new program state is constructed
- // for every range set, and each range line in the range set
- // corresponds to a specific constraint within this state.
- //
- // Upon comparing to another argument, the other argument is casted
- // to the current argument's type. This avoids proper promotion but
- // seems useful. For example, read() receives size_t argument,
- // and its return value, which is of type ssize_t, cannot be greater
- // than this argument. If we made a promotion, and the size argument
- // is equal to, say, 10, then we'd impose a range of [0, 10] on the
- // return value, however the correct range is [-1, 10].
- //
- // Please update the list of functions in the header after editing!
- //
- // The format is as follows:
- //
- //{ "function name",
- // { spec:
- // { argument types list, ... },
- // return type, purity, { range set list:
- // { range list:
- // { argument index, within or out of, {{from, to}, ...} },
- // { argument index, compares to argument, {{how, which}} },
- // ...
- // }
- // }
- // }
- //}
- #define SUMMARY_WITH_VARIANTS(identifier) {#identifier, {
- #define END_SUMMARY_WITH_VARIANTS }},
- #define VARIANT(argument_types, return_type, invalidation_approach) \
- { argument_types, return_type, invalidation_approach, {
- #define END_VARIANT } },
- #define SUMMARY(identifier, argument_types, return_type, \
- invalidation_approach) \
- { #identifier, { { argument_types, return_type, invalidation_approach, {
- #define END_SUMMARY } } } },
- #define ARGUMENT_TYPES(...) { __VA_ARGS__ }
- #define RETURN_TYPE(x) x
- #define INVALIDATION_APPROACH(x) x
- #define CASE {
- #define END_CASE },
- #define ARGUMENT_CONDITION(argument_number, condition_kind) \
- { argument_number, condition_kind, {
- #define END_ARGUMENT_CONDITION }},
- #define RETURN_VALUE_CONDITION(condition_kind) \
- { Ret, condition_kind, {
- #define END_RETURN_VALUE_CONDITION }},
- #define ARG_NO(x) x##U
- #define RANGE(x, y) { x, y },
- #define SINGLE_VALUE(x) RANGE(x, x)
- #define IS_LESS_THAN(arg) { BO_LE, arg }
- FunctionSummaryMap = {
- // The isascii() family of functions.
- SUMMARY(isalnum, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Boils down to isupper() or islower() or isdigit()
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('0', '9')
- RANGE('A', 'Z')
- RANGE('a', 'z')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // The locale-specific range.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- // No post-condition. We are completely unaware of
- // locale-specific return values.
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('0', '9')
- RANGE('A', 'Z')
- RANGE('a', 'z')
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isalpha, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // isupper() or islower(). Note that 'Z' is less than 'a'.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('A', 'Z')
- RANGE('a', 'z')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // The locale-specific range.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- END_CASE
- CASE // Other.
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('A', 'Z')
- RANGE('a', 'z')
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isascii, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Is ASCII.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(0, 127)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(0, 127)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isblank, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- SINGLE_VALUE('\t')
- SINGLE_VALUE(' ')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- SINGLE_VALUE('\t')
- SINGLE_VALUE(' ')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(iscntrl, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // 0..31 or 127
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(0, 32)
- SINGLE_VALUE(127)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(0, 32)
- SINGLE_VALUE(127)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Is a digit.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('0', '9')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('0', '9')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isgraph, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(33, 126)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(33, 126)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(islower, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Is certainly lowercase.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('a', 'z')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // Is ascii but not lowercase.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(0, 127)
- END_ARGUMENT_CONDITION
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('a', 'z')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // The locale-specific range.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- END_CASE
- CASE // Is not an unsigned char.
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(0, 255)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isprint, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(32, 126)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(32, 126)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(ispunct, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('!', '/')
- RANGE(':', '@')
- RANGE('[', '`')
- RANGE('{', '~')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('!', '/')
- RANGE(':', '@')
- RANGE('[', '`')
- RANGE('{', '~')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isspace, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Space, '\f', '\n', '\r', '\t', '\v'.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(9, 13)
- SINGLE_VALUE(' ')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // The locale-specific range.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE(9, 13)
- SINGLE_VALUE(' ')
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isupper, ARGUMENT_TYPES(IntTy), RETURN_TYPE (IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE // Is certainly uppercase.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('A', 'Z')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE // The locale-specific range.
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE(128, 255)
- END_ARGUMENT_CONDITION
- END_CASE
- CASE // Other.
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('A', 'Z') RANGE(128, 255)
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(isxdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(EvalCallAsPure))
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
- RANGE('0', '9')
- RANGE('A', 'F')
- RANGE('a', 'f')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(OutOfRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- CASE
- ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
- RANGE('0', '9')
- RANGE('A', 'F')
- RANGE('a', 'f')
- END_ARGUMENT_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(0)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- // The getc() family of functions that returns either a char or an EOF.
- SUMMARY(getc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(NoEvalCall))
- CASE // FIXME: EOF is assumed to be defined as -1.
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, 255)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(fgetc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(NoEvalCall))
- CASE // FIXME: EOF is assumed to be defined as -1.
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, 255)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(getchar, ARGUMENT_TYPES(), RETURN_TYPE(IntTy),
- INVALIDATION_APPROACH(NoEvalCall))
- CASE // FIXME: EOF is assumed to be defined as -1.
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, 255)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- // read()-like functions that never return more than buffer size.
- // We are not sure how ssize_t is defined on every platform, so we provide
- // three variants that should cover common cases.
- SUMMARY_WITH_VARIANTS(read)
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, IntMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, LongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, LongLongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- END_SUMMARY_WITH_VARIANTS
- SUMMARY_WITH_VARIANTS(write)
- // Again, due to elusive nature of ssize_t, we have duplicate
- // our summaries to cover different variants.
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, IntMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, LongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
- RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- RETURN_VALUE_CONDITION(WithinRange)
- RANGE(-1, LongLongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- END_SUMMARY_WITH_VARIANTS
- SUMMARY(fread,
- ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
- RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- SUMMARY(fwrite,
- ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
- RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(ComparesToArgument)
- IS_LESS_THAN(ARG_NO(2))
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_SUMMARY
- // getline()-like functions either fail or read at least the delimiter.
- SUMMARY_WITH_VARIANTS(getline)
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, IntMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, LongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, LongLongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- END_SUMMARY_WITH_VARIANTS
- SUMMARY_WITH_VARIANTS(getdelim)
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, IntMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, LongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
- RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
- CASE
- RETURN_VALUE_CONDITION(WithinRange)
- SINGLE_VALUE(-1)
- RANGE(1, LongLongMax)
- END_RETURN_VALUE_CONDITION
- END_CASE
- END_VARIANT
- END_SUMMARY_WITH_VARIANTS
- };
- }
- void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
- // If this checker grows large enough to support C++, Objective-C, or other
- // standard libraries, we could use multiple register...Checker() functions,
- // which would register various checkers with the help of the same Checker
- // class, turning on different function summaries.
- mgr.registerChecker<StdLibraryFunctionsChecker>();
- }
- bool ento::shouldRegisterStdCLibraryFunctionsChecker(const LangOptions &LO) {
- return true;
- }
|