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 @@ -947,7 +947,6 @@ const QualType IntTy = ACtx.IntTy; const QualType UnsignedIntTy = ACtx.UnsignedIntTy; const QualType LongTy = ACtx.LongTy; - const QualType LongLongTy = ACtx.LongLongTy; const QualType SizeTy = ACtx.getSizeType(); const QualType VoidPtrTy = getPointerTy(VoidTy); // void * @@ -973,7 +972,6 @@ const RangeInt UnsignedIntMax = BVF.getMaxValue(UnsignedIntTy).getLimitedValue(); const RangeInt LongMax = BVF.getMaxValue(LongTy).getLimitedValue(); - const RangeInt LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue(); const RangeInt SizeMax = BVF.getMaxValue(SizeTy).getLimitedValue(); // Set UCharRangeMax to min of int or uchar maximum value. @@ -1076,6 +1074,12 @@ return IntRangeVector{std::pair{b, *e}}; return IntRangeVector{}; } + auto operator()(std::pair i0, + std::pair> i1) { + if (i1.second) + return IntRangeVector{i0, {i1.first, *(i1.second)}}; + return IntRangeVector{i0}; + } } Range; auto SingleValue = [](RangeInt v) { return IntRangeVector{std::pair{v, v}}; @@ -1089,19 +1093,6 @@ Optional FilePtrTy = getPointerTy(FileTy); Optional FilePtrRestrictTy = getRestrictTy(FilePtrTy); - // Templates for summaries that are reused by many functions. - auto Read = [&](RetType R, RangeInt Max) { - return Summary(ArgTypes{Irrelevant, Irrelevant, SizeTy}, RetType{R}, - NoEvalCall) - .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), - ReturnValueCondition(WithinRange, Range(-1, Max))}); - }; - auto Getline = [&](RetType R, RangeInt Max) { - return Summary(ArgTypes{Irrelevant, Irrelevant, Irrelevant}, RetType{R}, - NoEvalCall) - .Case({ReturnValueCondition(WithinRange, {{-1, -1}, {1, Max}})}); - }; - // We are finally ready to define specifications for all supported functions. // // Argument ranges should always cover all variants. If return value @@ -1296,27 +1287,52 @@ RetType{SizeTy}), FreadSummary); - // We are not sure how ssize_t is defined on every platform, so we - // provide three variants that should cover common cases. - // FIXME Use lookupTy("ssize_t") instead of the `Read` lambda. + Optional Ssize_tTy = lookupTy("ssize_t"); + Optional Ssize_tMax = getMaxValue(Ssize_tTy); + + auto ReadSummary = + Summary(NoEvalCall) + .Case({ReturnValueCondition(LessThanOrEq, ArgNo(2)), + ReturnValueCondition(WithinRange, Range(-1, Ssize_tMax))}); + // FIXME these are actually defined by POSIX and not by the C standard, we // should handle them together with the rest of the POSIX functions. - addToFunctionSummaryMap("read", {Read(IntTy, IntMax), Read(LongTy, LongMax), - Read(LongLongTy, LongLongMax)}); - addToFunctionSummaryMap("write", {Read(IntTy, IntMax), Read(LongTy, LongMax), - Read(LongLongTy, LongLongMax)}); + // ssize_t read(int fildes, void *buf, size_t nbyte); + addToFunctionSummaryMap( + "read", Signature(ArgTypes{IntTy, VoidPtrTy, SizeTy}, RetType{Ssize_tTy}), + ReadSummary); + // ssize_t write(int fildes, const void *buf, size_t nbyte); + addToFunctionSummaryMap( + "write", + Signature(ArgTypes{IntTy, ConstVoidPtrTy, SizeTy}, RetType{Ssize_tTy}), + ReadSummary); + + auto GetLineSummary = + Summary(NoEvalCall) + .Case({ReturnValueCondition(WithinRange, + Range({-1, -1}, {1, Ssize_tMax}))}); + + QualType CharPtrPtrRestrictTy = getRestrictTy(getPointerTy(CharPtrTy)); // getline()-like functions either fail or read at least the delimiter. - // FIXME Use lookupTy("ssize_t") instead of the `Getline` lambda. // FIXME these are actually defined by POSIX and not by the C standard, we // should handle them together with the rest of the POSIX functions. - addToFunctionSummaryMap("getline", - {Getline(IntTy, IntMax), Getline(LongTy, LongMax), - Getline(LongLongTy, LongLongMax)}); - // FIXME getdelim's signature is different than getline's! - addToFunctionSummaryMap("getdelim", - {Getline(IntTy, IntMax), Getline(LongTy, LongMax), - Getline(LongLongTy, LongLongMax)}); + // ssize_t getline(char **restrict lineptr, size_t *restrict n, + // FILE *restrict stream); + addToFunctionSummaryMap( + "getline", + Signature( + ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, FilePtrRestrictTy}, + RetType{Ssize_tTy}), + GetLineSummary); + // ssize_t getdelim(char **restrict lineptr, size_t *restrict n, + // int delimiter, FILE *restrict stream); + addToFunctionSummaryMap( + "getdelim", + Signature(ArgTypes{CharPtrPtrRestrictTy, SizePtrRestrictTy, IntTy, + FilePtrRestrictTy}, + RetType{Ssize_tTy}), + GetLineSummary); if (ModelPOSIX) { @@ -1747,8 +1763,6 @@ .ArgConstraint( ArgumentCondition(0, WithinRange, Range(0, IntMax)))); - Optional Ssize_tTy = lookupTy("ssize_t"); - // ssize_t readlink(const char *restrict path, char *restrict buf, // size_t bufsize); addToFunctionSummaryMap( diff --git a/clang/test/Analysis/std-c-library-functions.c b/clang/test/Analysis/std-c-library-functions.c --- a/clang/test/Analysis/std-c-library-functions.c +++ b/clang/test/Analysis/std-c-library-functions.c @@ -57,7 +57,9 @@ // CHECK-NEXT: Loaded summary for: unsigned int fwrite(const void *restrict, size_t, size_t, FILE *restrict) // CHECK-NEXT: Loaded summary for: ssize_t read(int, void *, size_t) // CHECK-NEXT: Loaded summary for: ssize_t write(int, const void *, size_t) -// CHECK-NEXT: Loaded summary for: ssize_t getline(char **, size_t *, FILE *) +// CHECK-NEXT: Loaded summary for: ssize_t getline(char **restrict, size_t *restrict, FILE *restrict) +// CHECK-NEXT: Loaded summary for: ssize_t getdelim(char **restrict, size_t *restrict, int, FILE *restrict) + void clang_analyzer_eval(int); @@ -126,7 +128,8 @@ (void)fread(ptr, sz, nmem, fp); // expected-warning {{1st function call argument is an uninitialized value}} } -ssize_t getline(char **, size_t *, FILE *); +ssize_t getline(char **restrict, size_t *restrict, FILE *restrict); +ssize_t getdelim(char **restrict, size_t *restrict, int, FILE *restrict); void test_getline(FILE *fp) { char *line = 0; size_t n = 0;