diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -40,12 +40,12 @@ // // The following standard C functions are currently supported: // -// fgetc getline isdigit isupper +// fgetc getline isdigit isupper toascii // fread isalnum isgraph isxdigit // fwrite isalpha islower read // getc isascii isprint write -// getchar isblank ispunct -// getdelim iscntrl isspace +// getchar isblank ispunct toupper +// getdelim iscntrl isspace tolower // //===----------------------------------------------------------------------===// @@ -147,9 +147,7 @@ RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Args) : ValueConstraint(ArgN), Kind(Kind), Args(Args) {} - const IntRangeVector &getRanges() const { - return Args; - } + const IntRangeVector &getRanges() const { return Args; } private: ProgramStateRef applyAsOutOfRange(ProgramStateRef State, @@ -158,6 +156,7 @@ ProgramStateRef applyAsWithinRange(ProgramStateRef State, const CallEvent &Call, const Summary &Summary) const; + public: ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, const Summary &Summary, @@ -408,7 +407,7 @@ return *this; } - Summary &Case(ConstraintSet&& CS) { + Summary &Case(ConstraintSet &&CS) { CaseConstraints.push_back(std::move(CS)); return *this; } @@ -796,13 +795,13 @@ const QualType LongLongTy = ACtx.LongLongTy; const QualType SizeTy = ACtx.getSizeType(); - const QualType VoidPtrTy = ACtx.VoidPtrTy; // void * + const QualType VoidPtrTy = ACtx.VoidPtrTy; // void * const QualType IntPtrTy = ACtx.getPointerType(IntTy); // int * const QualType UnsignedIntPtrTy = ACtx.getPointerType(UnsignedIntTy); // unsigned int * const QualType VoidPtrRestrictTy = getRestrictTy(VoidPtrTy); const QualType ConstVoidPtrTy = - ACtx.getPointerType(ACtx.VoidTy.withConst()); // const void * + ACtx.getPointerType(ACtx.VoidTy.withConst()); // const void * const QualType CharPtrTy = ACtx.getPointerType(ACtx.CharTy); // char * const QualType CharPtrRestrictTy = getRestrictTy(CharPtrTy); const QualType ConstCharPtrTy = @@ -1117,6 +1116,18 @@ .Case({ArgumentCondition(0U, OutOfRange, {{'0', '9'}, {'A', 'F'}, {'a', 'f'}}), ReturnValueCondition(WithinRange, SingleValue(0))})); + addToFunctionSummaryMap( + "toupper", Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + addToFunctionSummaryMap( + "tolower", Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + addToFunctionSummaryMap( + "toascii", Summary(ArgTypes{IntTy}, RetType{IntTy}, EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); // The getc() family of functions that returns either a char or an EOF. if (FilePtrTy) { @@ -2033,7 +2044,8 @@ mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "ModelPOSIX"); } -bool ento::shouldRegisterStdCLibraryFunctionsChecker(const CheckerManager &mgr) { +bool ento::shouldRegisterStdCLibraryFunctionsChecker( + const CheckerManager &mgr) { return true; } diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints.c b/clang/test/Analysis/std-c-library-functions-arg-constraints.c --- a/clang/test/Analysis/std-c-library-functions-arg-constraints.c +++ b/clang/test/Analysis/std-c-library-functions-arg-constraints.c @@ -45,7 +45,6 @@ // bugpath-note{{TRUE}} \ // bugpath-note{{Left side of '&&' is true}} \ // bugpath-note{{'x' is <= 255}} - } void test_alnum_symbolic2(int x) { @@ -62,6 +61,114 @@ } } +int toupper(int); + +void test_toupper_concrete(int v) { + int ret = toupper(256); // \ + // report-warning{{Function argument constraint is not satisfied}} \ + // bugpath-warning{{Function argument constraint is not satisfied}} \ + // bugpath-note{{Function argument constraint is not satisfied}} + (void)ret; +} + +void test_toupper_symbolic(int x) { + int ret = toupper(x); + (void)ret; + + clang_analyzer_eval(EOF <= x && x <= 255); // \ + // report-warning{{TRUE}} \ + // bugpath-warning{{TRUE}} \ + // bugpath-note{{TRUE}} \ + // bugpath-note{{Left side of '&&' is true}} \ + // bugpath-note{{'x' is <= 255}} +} + +void test_toupper_symbolic2(int x) { + if (x > 255) { // \ + // bugpath-note{{Assuming 'x' is > 255}} \ + // bugpath-note{{Taking true branch}} + + int ret = toupper(x); // \ + // report-warning{{Function argument constraint is not satisfied}} \ + // bugpath-warning{{Function argument constraint is not satisfied}} \ + // bugpath-note{{Function argument constraint is not satisfied}} + + (void)ret; + } +} + +int tolower(int); + +void test_tolower_concrete(int v) { + int ret = tolower(256); // \ + // report-warning{{Function argument constraint is not satisfied}} \ + // bugpath-warning{{Function argument constraint is not satisfied}} \ + // bugpath-note{{Function argument constraint is not satisfied}} + (void)ret; +} + +void test_tolower_symbolic(int x) { + int ret = tolower(x); + (void)ret; + + clang_analyzer_eval(EOF <= x && x <= 255); // \ + // report-warning{{TRUE}} \ + // bugpath-warning{{TRUE}} \ + // bugpath-note{{TRUE}} \ + // bugpath-note{{Left side of '&&' is true}} \ + // bugpath-note{{'x' is <= 255}} +} + +void test_tolower_symbolic2(int x) { + if (x > 255) { // \ + // bugpath-note{{Assuming 'x' is > 255}} \ + // bugpath-note{{Taking true branch}} + + int ret = tolower(x); // \ + // report-warning{{Function argument constraint is not satisfied}} \ + // bugpath-warning{{Function argument constraint is not satisfied}} \ + // bugpath-note{{Function argument constraint is not satisfied}} + + (void)ret; + } +} + +int toascii(int); + +void test_toascii_concrete(int v) { + int ret = toascii(256); // \ + // report-warning{{Function argument constraint is not satisfied}} \ + // bugpath-warning{{Function argument constraint is not satisfied}} \ + // bugpath-note{{Function argument constraint is not satisfied}} + (void)ret; +} + +void test_toascii_symbolic(int x) { + int ret = toascii(x); + (void)ret; + + clang_analyzer_eval(EOF <= x && x <= 255); // \ + // report-warning{{TRUE}} \ + // bugpath-warning{{TRUE}} \ + // bugpath-note{{TRUE}} \ + // bugpath-note{{Left side of '&&' is true}} \ + // bugpath-note{{'x' is <= 255}} +} + +void test_toascii_symbolic2(int x) { + if (x > 255) { // \ + // bugpath-note{{Assuming 'x' is > 255}} \ + // bugpath-note{{Taking true branch}} + + int ret = toascii(x); // \ + // report-warning{{Function argument constraint is not satisfied}} \ + // bugpath-warning{{Function argument constraint is not satisfied}} \ + // bugpath-note{{Function argument constraint is not satisfied}} + + (void)ret; + } +} + typedef struct FILE FILE; typedef typeof(sizeof(int)) size_t; size_t fread(void *restrict, size_t, size_t, FILE *restrict); @@ -80,7 +187,7 @@ // bugpath-note{{'buf' is not equal to null}} } void test_notnull_symbolic2(FILE *fp, int *buf) { - if (!buf) // bugpath-note{{Assuming 'buf' is null}} \ + if (!buf) // bugpath-note{{Assuming 'buf' is null}} \ // bugpath-note{{Taking true branch}} fread(buf, sizeof(int), 10, fp); // \ // report-warning{{Function argument constraint is not satisfied}} \ @@ -151,7 +258,7 @@ } int __buf_size_arg_constraint_mul(const void *, size_t, size_t); void test_buf_size_concrete_with_multiplication() { - short buf[3]; // bugpath-note{{'buf' initialized here}} + short buf[3]; // bugpath-note{{'buf' initialized here}} __buf_size_arg_constraint_mul(buf, 4, sizeof(short)); // \ // report-warning{{Function argument constraint is not satisfied}} \ // bugpath-warning{{Function argument constraint is not satisfied}} \