[clang] f0b9dbc - [analyzer][StdLibraryFunctionsChecker] Add POSIX time handling functions
Gabor Marton via cfe-commits
cfe-commits at lists.llvm.org
Fri Sep 4 09:45:21 PDT 2020
Author: Gabor Marton
Date: 2020-09-04T18:44:12+02:00
New Revision: f0b9dbcfc7ba2a217cab3217d6217fc270c88b58
URL: https://github.com/llvm/llvm-project/commit/f0b9dbcfc7ba2a217cab3217d6217fc270c88b58
DIFF: https://github.com/llvm/llvm-project/commit/f0b9dbcfc7ba2a217cab3217d6217fc270c88b58.diff
LOG: [analyzer][StdLibraryFunctionsChecker] Add POSIX time handling functions
Differential Revision: https://reviews.llvm.org/D84248
Added:
Modified:
clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
clang/test/Analysis/std-c-library-functions-POSIX.c
clang/test/Analysis/std-c-library-functions-arg-constraints.c
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 2c20422a9cc4..ddde629f44a5 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -249,15 +249,21 @@ class StdLibraryFunctionsChecker
}
};
- // Represents a buffer argument with an additional size argument.
- // E.g. the first two arguments here:
+ // Represents a buffer argument with an additional size constraint. The
+ // constraint may be a concrete value, or a symbolic value in an argument.
+ // Example 1. Concrete value as the minimum buffer size.
+ // char *asctime_r(const struct tm *restrict tm, char *restrict buf);
+ // // `buf` size must be at least 26 bytes according the POSIX standard.
+ // Example 2. Argument as a buffer size.
// ctime_s(char *buffer, rsize_t bufsz, const time_t *time);
- // Another example:
+ // Example 3. The size is computed as a multiplication of other args.
// size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
// // Here, ptr is the buffer, and its minimum size is `size * nmemb`.
class BufferSizeConstraint : public ValueConstraint {
+ // The concrete value which is the minimum size for the buffer.
+ llvm::Optional<llvm::APSInt> ConcreteSize;
// The argument which holds the size of the buffer.
- ArgNo SizeArgN;
+ llvm::Optional<ArgNo> SizeArgN;
// The argument which is a multiplier to size. This is set in case of
// `fread` like functions where the size is computed as a multiplication of
// two arguments.
@@ -266,9 +272,10 @@ class StdLibraryFunctionsChecker
BinaryOperator::Opcode Op = BO_LE;
public:
+ BufferSizeConstraint(ArgNo Buffer, llvm::APSInt BufMinSize)
+ : ValueConstraint(Buffer), ConcreteSize(BufMinSize) {}
BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize)
: ValueConstraint(Buffer), SizeArgN(BufSize) {}
-
BufferSizeConstraint(ArgNo Buffer, ArgNo BufSize, ArgNo BufSizeMultiplier)
: ValueConstraint(Buffer), SizeArgN(BufSize),
SizeMultiplierArgN(BufSizeMultiplier) {}
@@ -279,14 +286,27 @@ class StdLibraryFunctionsChecker
SValBuilder &SvalBuilder = C.getSValBuilder();
// The buffer argument.
SVal BufV = getArgSVal(Call, getArgNo());
- // The size argument.
- SVal SizeV = getArgSVal(Call, SizeArgN);
- // Multiply with another argument if given.
- if (SizeMultiplierArgN) {
- SVal SizeMulV = getArgSVal(Call, *SizeMultiplierArgN);
- SizeV = SvalBuilder.evalBinOp(State, BO_Mul, SizeV, SizeMulV,
- Summary.getArgType(SizeArgN));
- }
+
+ // Get the size constraint.
+ const SVal SizeV = [this, &State, &Call, &Summary, &SvalBuilder]() {
+ if (ConcreteSize) {
+ return SVal(SvalBuilder.makeIntVal(*ConcreteSize));
+ } else if (SizeArgN) {
+ // The size argument.
+ SVal SizeV = getArgSVal(Call, *SizeArgN);
+ // Multiply with another argument if given.
+ if (SizeMultiplierArgN) {
+ SVal SizeMulV = getArgSVal(Call, *SizeMultiplierArgN);
+ SizeV = SvalBuilder.evalBinOp(State, BO_Mul, SizeV, SizeMulV,
+ Summary.getArgType(*SizeArgN));
+ }
+ return SizeV;
+ } else {
+ llvm_unreachable("The constraint must be either a concrete value or "
+ "encoded in an arguement.");
+ }
+ }();
+
// The dynamic size of the buffer argument, got from the analyzer engine.
SVal BufDynSize = getDynamicSizeWithOffset(State, BufV);
@@ -2036,6 +2056,132 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
BufferSize(/*Buffer=*/ArgNo(4), /*BufSize=*/ArgNo(5)))
.ArgConstraint(
ArgumentCondition(5, WithinRange, Range(0, Socklen_tMax))));
+
+ Optional<QualType> StructUtimbufTy = lookupTy("utimbuf");
+ Optional<QualType> StructUtimbufPtrTy = getPointerTy(StructUtimbufTy);
+
+ // int utime(const char *filename, struct utimbuf *buf);
+ addToFunctionSummaryMap(
+ "utime", Summary(ArgTypes{ConstCharPtrTy, StructUtimbufPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ Optional<QualType> StructTimespecTy = lookupTy("timespec");
+ Optional<QualType> StructTimespecPtrTy = getPointerTy(StructTimespecTy);
+ Optional<QualType> ConstStructTimespecPtrTy =
+ getPointerTy(getConstTy(StructTimespecTy));
+
+ // int futimens(int fd, const struct timespec times[2]);
+ addToFunctionSummaryMap(
+ "futimens", Summary(ArgTypes{IntTy, ConstStructTimespecPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(ArgumentCondition(0, WithinRange,
+ Range(0, IntMax))));
+
+ // int utimensat(int dirfd, const char *pathname,
+ // const struct timespec times[2], int flags);
+ addToFunctionSummaryMap("utimensat",
+ Summary(ArgTypes{IntTy, ConstCharPtrTy,
+ ConstStructTimespecPtrTy, IntTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ Optional<QualType> StructTimevalTy = lookupTy("timeval");
+ Optional<QualType> ConstStructTimevalPtrTy =
+ getPointerTy(getConstTy(StructTimevalTy));
+
+ // int utimes(const char *filename, const struct timeval times[2]);
+ addToFunctionSummaryMap(
+ "utimes", Summary(ArgTypes{ConstCharPtrTy, ConstStructTimevalPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
+ addToFunctionSummaryMap(
+ "nanosleep",
+ Summary(ArgTypes{ConstStructTimespecPtrTy, StructTimespecPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ Optional<QualType> Time_tTy = lookupTy("time_t");
+ Optional<QualType> ConstTime_tPtrTy = getPointerTy(getConstTy(Time_tTy));
+ Optional<QualType> ConstTime_tPtrRestrictTy =
+ getRestrictTy(ConstTime_tPtrTy);
+
+ Optional<QualType> StructTmTy = lookupTy("tm");
+ Optional<QualType> StructTmPtrTy = getPointerTy(StructTmTy);
+ Optional<QualType> StructTmPtrRestrictTy = getRestrictTy(StructTmPtrTy);
+ Optional<QualType> ConstStructTmPtrTy =
+ getPointerTy(getConstTy(StructTmTy));
+ Optional<QualType> ConstStructTmPtrRestrictTy =
+ getRestrictTy(ConstStructTmPtrTy);
+
+ // struct tm * localtime(const time_t *tp);
+ addToFunctionSummaryMap(
+ "localtime",
+ Summary(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ // struct tm *localtime_r(const time_t *restrict timer,
+ // struct tm *restrict result);
+ addToFunctionSummaryMap(
+ "localtime_r",
+ Summary(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy},
+ RetType{StructTmPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // char *asctime_r(const struct tm *restrict tm, char *restrict buf);
+ addToFunctionSummaryMap(
+ "asctime_r",
+ Summary(ArgTypes{ConstStructTmPtrRestrictTy, CharPtrRestrictTy},
+ RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(1),
+ /*MinBufSize=*/BVF.getValue(26, IntTy))));
+
+ // char *ctime_r(const time_t *timep, char *buf);
+ addToFunctionSummaryMap("ctime_r",
+ Summary(ArgTypes{ConstTime_tPtrTy, CharPtrTy},
+ RetType{CharPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1)))
+ .ArgConstraint(BufferSize(
+ /*Buffer=*/ArgNo(1),
+ /*MinBufSize=*/BVF.getValue(26, IntTy))));
+
+ // struct tm *gmtime_r(const time_t *restrict timer,
+ // struct tm *restrict result);
+ addToFunctionSummaryMap(
+ "gmtime_r",
+ Summary(ArgTypes{ConstTime_tPtrRestrictTy, StructTmPtrRestrictTy},
+ RetType{StructTmPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ // struct tm * gmtime(const time_t *tp);
+ addToFunctionSummaryMap(
+ "gmtime",
+ Summary(ArgTypes{ConstTime_tPtrTy}, RetType{StructTmPtrTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0))));
+
+ Optional<QualType> Clockid_tTy = lookupTy("clockid_t");
+
+ // int clock_gettime(clockid_t clock_id, struct timespec *tp);
+ addToFunctionSummaryMap("clock_gettime",
+ Summary(ArgTypes{Clockid_tTy, StructTimespecPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(1))));
+
+ Optional<QualType> StructItimervalTy = lookupTy("itimerval");
+ Optional<QualType> StructItimervalPtrTy = getPointerTy(StructItimervalTy);
+
+ // int getitimer(int which, struct itimerval *curr_value);
+ addToFunctionSummaryMap("getitimer",
+ Summary(ArgTypes{IntTy, StructItimervalPtrTy},
+ RetType{IntTy}, NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(1))));
}
// Functions for testing.
@@ -2071,6 +2217,11 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
EvalCallAsPure)
.ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0), /*BufSize=*/ArgNo(1),
/*BufSizeMultiplier=*/ArgNo(2))));
+ addToFunctionSummaryMap(
+ "__buf_size_arg_constraint_concrete",
+ Summary(ArgTypes{ConstVoidPtrTy}, RetType{IntTy}, EvalCallAsPure)
+ .ArgConstraint(BufferSize(/*Buffer=*/ArgNo(0),
+ /*BufSize=*/BVF.getValue(10, IntTy))));
addToFunctionSummaryMap(
{"__test_restrict_param_0", "__test_restrict_param_1",
"__test_restrict_param_2"},
diff --git a/clang/test/Analysis/std-c-library-functions-POSIX.c b/clang/test/Analysis/std-c-library-functions-POSIX.c
index 3638ad100240..d65e9f029b6b 100644
--- a/clang/test/Analysis/std-c-library-functions-POSIX.c
+++ b/clang/test/Analysis/std-c-library-functions-POSIX.c
@@ -95,6 +95,19 @@
// CHECK: Loaded summary for: ssize_t send(int sockfd, const void *buf, size_t len, int flags)
// CHECK: Loaded summary for: int socketpair(int domain, int type, int protocol, int sv[2])
// CHECK: Loaded summary for: int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, char *restrict node, socklen_t nodelen, char *restrict service, socklen_t servicelen, int flags)
+// CHECK: Loaded summary for: int utime(const char *filename, struct utimbuf *buf)
+// CHECK: Loaded summary for: int futimens(int fd, const struct timespec times[2])
+// CHECK: Loaded summary for: int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags)
+// CHECK: Loaded summary for: int utimes(const char *filename, const struct timeval times[2])
+// CHECK: Loaded summary for: int nanosleep(const struct timespec *rqtp, struct timespec *rmtp)
+// CHECK: Loaded summary for: struct tm *localtime(const time_t *tp)
+// CHECK: Loaded summary for: struct tm *localtime_r(const time_t *restrict timer, struct tm *restrict result)
+// CHECK: Loaded summary for: char *asctime_r(const struct tm *restrict tm, char *restrict buf)
+// CHECK: Loaded summary for: char *ctime_r(const time_t *timep, char *buf)
+// CHECK: Loaded summary for: struct tm *gmtime_r(const time_t *restrict timer, struct tm *restrict result)
+// CHECK: Loaded summary for: struct tm *gmtime(const time_t *tp)
+// CHECK: Loaded summary for: int clock_gettime(clockid_t clock_id, struct timespec *tp)
+// CHECK: Loaded summary for: int getitimer(int which, struct itimerval *curr_value)
long a64l(const char *str64);
char *l64a(long value);
@@ -226,6 +239,25 @@ int getsockopt(int socket, int level, int option_name, void *restrict option_val
ssize_t send(int sockfd, const void *buf, size_t len, int flags);
int socketpair(int domain, int type, int protocol, int sv[2]);
int getnameinfo(const struct sockaddr *restrict sa, socklen_t salen, char *restrict node, socklen_t nodelen, char *restrict service, socklen_t servicelen, int flags);
+struct utimbuf;
+struct timespec { int x; };
+struct timeval { int x; };
+int utime(const char *filename, struct utimbuf *buf);
+int futimens(int fd, const struct timespec times[2]);
+int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags);
+int utimes(const char *filename, const struct timeval times[2]);
+int nanosleep(const struct timespec *rqtp, struct timespec *rmtp);
+typedef unsigned long time_t;
+struct tm *localtime(const time_t *tp);
+struct tm *localtime_r(const time_t *restrict timer, struct tm *restrict result);
+char *asctime_r(const struct tm *restrict tm, char *restrict buf);
+char *ctime_r(const time_t *timep, char *buf);
+struct tm *gmtime_r(const time_t *restrict timer, struct tm *restrict result);
+struct tm *gmtime(const time_t *tp);
+typedef unsigned long clockid_t;
+int clock_gettime(clockid_t clock_id, struct timespec *tp);
+struct itimerval;
+int getitimer(int which, struct itimerval *curr_value);
// Must have at least one call expression to initialize the summary map.
int bar(void);
diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints.c b/clang/test/Analysis/std-c-library-functions-arg-constraints.c
index e926cd15384d..28979abd43b5 100644
--- a/clang/test/Analysis/std-c-library-functions-arg-constraints.c
+++ b/clang/test/Analysis/std-c-library-functions-arg-constraints.c
@@ -256,6 +256,7 @@ void test_buf_size_symbolic_and_offset(int s) {
// bugpath-note{{TRUE}} \
// bugpath-note{{'s' is <= 2}}
}
+
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}}
@@ -280,3 +281,13 @@ void test_buf_size_symbolic_and_offset_with_multiplication(size_t s) {
// bugpath-warning{{TRUE}} \
// bugpath-note{{TRUE}}
}
+
+// The minimum buffer size for this function is set to 10.
+int __buf_size_arg_constraint_concrete(const void *);
+void test_min_buf_size() {
+ char buf[9];// bugpath-note{{'buf' initialized here}}
+ __buf_size_arg_constraint_concrete(buf); // \
+ // report-warning{{Function argument constraint is not satisfied}} \
+ // bugpath-warning{{Function argument constraint is not satisfied}} \
+ // bugpath-note{{Function argument constraint is not satisfied}}
+}
More information about the cfe-commits
mailing list