From 47fec16cd11d6d8b64295ece07df00ed46604412 Mon Sep 17 00:00:00 2001 From: Artem Dergachev Date: Wed, 2 Nov 2016 19:35:20 +0000 Subject: [PATCH] [analyzer] StdLibraryFunctions: provide platform-specific function summaries. Because standard functions can be defined differently on different platforms, this commit introduces a method for constructing summaries with multiple variants, whichever matches better. It is also useful for supporting overloads. Differential Revision: https://reviews.llvm.org/D25940 llvm-svn: 285852 --- .../Checkers/StdLibraryFunctionsChecker.cpp | 225 +++++++++++++----- clang/test/Analysis/std-c-library-functions.c | 6 +- 2 files changed, 173 insertions(+), 58 deletions(-) diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp index 5d7a14d3be14..6983e0e99b59 100644 --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -79,11 +79,15 @@ class StdLibraryFunctionsChecker : public Checker { /// 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> IntRangeVectorTy; + typedef std::vector> IntRangeVectorTy; /// A reference to an argument or return value by its number. /// ArgNo in CallExpr and CallEvent is defined as Unsigned, but @@ -190,9 +194,16 @@ class StdLibraryFunctionsChecker : public Checker { 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 FunctionVariantsTy; + // The map of all functions supported by the checker. It is initialized // lazily, and it doesn't change after initialization. - typedef llvm::StringMap FunctionSummaryMapTy; + typedef llvm::StringMap FunctionSummaryMapTy; mutable FunctionSummaryMapTy FunctionSummaryMap; // Auxiliary functions to support ArgNoTy within all structures @@ -442,11 +453,12 @@ StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD, // Strict checking is important because we will be conducting // very integral-type-sensitive operations on arguments and // return values. - const FunctionSummaryTy &Spec = FSMI->second; - if (!Spec.matchesCall(CE)) - return None; + const FunctionVariantsTy &SpecVariants = FSMI->second; + for (const FunctionSummaryTy &Spec : SpecVariants) + if (Spec.matchesCall(CE)) + return Spec; - return Spec; + return None; } void StdLibraryFunctionsChecker::initFunctionSummaries( @@ -458,17 +470,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // 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(); - QualType SSizeTy = ACtx.getIntTypeForBitwidth(ACtx.getTypeSize(SizeTy), true); - // Don't worry about truncation here, it'd be cast back to SIZE_MAX when used. - int64_t SizeMax = - BVF.getMaxValue(SizeTy).getLimitedValue(); - int64_t SSizeMax = - BVF.getMaxValue(SSizeTy).getLimitedValue(); - (void)SizeMax; + 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. // @@ -511,17 +526,22 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( // } //} +#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 }}}, + { #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, { + { argument_number, condition_kind, { #define END_ARGUMENT_CONDITION }}, #define RETURN_VALUE_CONDITION(condition_kind) \ { Ret, condition_kind, { @@ -871,28 +891,80 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( END_SUMMARY // read()-like functions that never return more than buffer size. - SUMMARY(read, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), - RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall)) - CASE - RETURN_VALUE_CONDITION(ComparesToArgument) - IS_LESS_THAN(ARG_NO(2)) - END_RETURN_VALUE_CONDITION - RETURN_VALUE_CONDITION(WithinRange) - RANGE(-1, SSizeMax) - END_RETURN_VALUE_CONDITION - END_CASE - END_SUMMARY - SUMMARY(write, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), - RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall)) - CASE - RETURN_VALUE_CONDITION(ComparesToArgument) - IS_LESS_THAN(ARG_NO(2)) - END_RETURN_VALUE_CONDITION - RETURN_VALUE_CONDITION(WithinRange) - RANGE(-1, SSizeMax) - END_RETURN_VALUE_CONDITION - END_CASE - END_SUMMARY + // 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)) @@ -913,25 +985,64 @@ void StdLibraryFunctionsChecker::initFunctionSummaries( END_SUMMARY // getline()-like functions either fail or read at least the delimiter. - SUMMARY(getline, ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), - RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall)) - CASE - RETURN_VALUE_CONDITION(WithinRange) - SINGLE_VALUE(-1) - RANGE(1, SSizeMax) - END_RETURN_VALUE_CONDITION - END_CASE - END_SUMMARY - SUMMARY(getdelim, - ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), - RETURN_TYPE(SSizeTy), INVALIDATION_APPROACH(NoEvalCall)) - CASE - RETURN_VALUE_CONDITION(WithinRange) - SINGLE_VALUE(-1) - RANGE(1, SSizeMax) - END_RETURN_VALUE_CONDITION - END_CASE - END_SUMMARY + 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 }; } diff --git a/clang/test/Analysis/std-c-library-functions.c b/clang/test/Analysis/std-c-library-functions.c index c7c47c3c3da7..6b78a26d44bb 100644 --- a/clang/test/Analysis/std-c-library-functions.c +++ b/clang/test/Analysis/std-c-library-functions.c @@ -1,4 +1,8 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=unix.StdCLibraryFunctions,debug.ExprInspection -verify %s +// RUN: %clang_cc1 -triple i686-unknown-linux -analyze -analyzer-checker=unix.StdCLibraryFunctions,debug.ExprInspection -verify %s // RUN: %clang_cc1 -triple x86_64-unknown-linux -analyze -analyzer-checker=unix.StdCLibraryFunctions,debug.ExprInspection -verify %s +// RUN: %clang_cc1 -triple armv7-a15-linux -analyze -analyzer-checker=unix.StdCLibraryFunctions,debug.ExprInspection -verify %s +// RUN: %clang_cc1 -triple thumbv7-a15-linux -analyze -analyzer-checker=unix.StdCLibraryFunctions,debug.ExprInspection -verify %s void clang_analyzer_eval(int); @@ -23,7 +27,7 @@ void test_fgets(FILE *fp) { } -typedef unsigned long size_t; +typedef typeof(sizeof(int)) size_t; typedef signed long ssize_t; ssize_t read(int, void *, size_t); ssize_t write(int, const void *, size_t);