martong updated this revision to Diff 285289.
martong marked 2 inline comments as done.
martong added a comment.

- Handle minimum buffer sizes
- Fix copy paste error


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D84248/new/

https://reviews.llvm.org/D84248

Files:
  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

Index: clang/test/Analysis/std-c-library-functions-arg-constraints.c
===================================================================
--- clang/test/Analysis/std-c-library-functions-arg-constraints.c
+++ clang/test/Analysis/std-c-library-functions-arg-constraints.c
@@ -149,6 +149,7 @@
   // 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}}
@@ -173,3 +174,13 @@
   // 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}}
+}
Index: clang/test/Analysis/std-c-library-functions-POSIX.c
===================================================================
--- clang/test/Analysis/std-c-library-functions-POSIX.c
+++ 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 @@
 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);
Index: clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -242,15 +242,21 @@
     }
   };
 
-  // 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.
@@ -259,9 +265,10 @@
     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) {}
@@ -272,14 +279,27 @@
       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);
 
@@ -1986,6 +2006,162 @@
                   BufferSize(/*Buffer=*/ArgNo(4), /*BufSize=*/ArgNo(5)))
               .ArgConstraint(
                   ArgumentCondition(5, WithinRange, Range(0, *Socklen_tMax))));
+
+    Optional<QualType> StructUtimbufTy = lookupType("utimbuf", ACtx);
+    Optional<QualType> StructUtimbufPtrTy;
+    if (StructUtimbufTy)
+      StructUtimbufPtrTy = ACtx.getPointerType(*StructUtimbufTy);
+
+    if (StructUtimbufPtrTy)
+      // int utime(const char *filename, struct utimbuf *buf);
+      addToFunctionSummaryMap(
+          "utime", Summary(ArgTypes{ConstCharPtrTy, *StructUtimbufPtrTy},
+                           RetType{IntTy}, NoEvalCall)
+                       .ArgConstraint(NotNull(ArgNo(0))));
+
+    Optional<QualType> StructTimespecTy = lookupType("timespec", ACtx);
+    Optional<QualType> StructTimespecPtrTy, ConstStructTimespecPtrTy;
+    if (StructTimespecTy) {
+      StructTimespecPtrTy = ACtx.getPointerType(*StructTimespecTy);
+      ConstStructTimespecPtrTy =
+          ACtx.getPointerType(StructTimespecTy->withConst());
+    }
+
+    if (ConstStructTimespecPtrTy) {
+      // 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 = lookupType("timeval", ACtx);
+    Optional<QualType> ConstStructTimevalPtrTy;
+    if (StructTimevalTy)
+      ConstStructTimevalPtrTy =
+          ACtx.getPointerType(StructTimevalTy->withConst());
+
+    if (ConstStructTimevalPtrTy)
+      // int utimes(const char *filename, const struct timeval times[2]);
+      addToFunctionSummaryMap(
+          "utimes", Summary(ArgTypes{ConstCharPtrTy, *ConstStructTimevalPtrTy},
+                            RetType{IntTy}, NoEvalCall)
+                        .ArgConstraint(NotNull(ArgNo(0))));
+
+    if (ConstStructTimespecPtrTy && StructTimespecPtrTy)
+      // 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 = lookupType("time_t", ACtx);
+    Optional<QualType> Time_tPtrTy, Time_tPtrRestrictTy, ConstTime_tPtrTy,
+        ConstTime_tPtrRestrictTy;
+    if (Time_tTy) {
+      Time_tPtrTy = ACtx.getPointerType(*Time_tTy);
+      Time_tPtrRestrictTy = getRestrictTy(*Time_tPtrTy);
+      ConstTime_tPtrTy = ACtx.getPointerType(Time_tTy->withConst());
+      ConstTime_tPtrRestrictTy = getRestrictTy(*ConstTime_tPtrTy);
+    }
+
+    Optional<QualType> StructTmTy = lookupType("tm", ACtx);
+    Optional<QualType> StructTmPtrTy, StructTmPtrRestrictTy, ConstStructTmPtrTy,
+        ConstStructTmPtrRestrictTy;
+    if (StructTmTy) {
+      StructTmPtrTy = ACtx.getPointerType(*StructTmTy);
+      StructTmPtrRestrictTy = getRestrictTy(*StructTmPtrTy);
+      ConstStructTmPtrTy = ACtx.getPointerType(StructTmTy->withConst());
+      ConstStructTmPtrRestrictTy = getRestrictTy(*ConstStructTmPtrTy);
+    }
+
+    if (ConstTime_tPtrTy && StructTmPtrTy)
+      // struct tm * localtime(const time_t *tp);
+      addToFunctionSummaryMap("localtime",
+                              Summary(ArgTypes{*ConstTime_tPtrTy},
+                                      RetType{*StructTmPtrTy}, NoEvalCall)
+                                  .ArgConstraint(NotNull(ArgNo(0))));
+
+    if (ConstTime_tPtrRestrictTy && StructTmPtrRestrictTy && StructTmPtrTy)
+      // 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))));
+
+    if (ConstStructTmPtrRestrictTy)
+      // 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))));
+
+    if (ConstTime_tPtrTy)
+      // 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))));
+
+    if (ConstTime_tPtrRestrictTy && StructTmPtrRestrictTy && StructTmPtrTy)
+      // 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))));
+
+    if (ConstTime_tPtrTy && StructTmPtrTy)
+      // struct tm * gmtime(const time_t *tp);
+      addToFunctionSummaryMap("gmtime",
+                              Summary(ArgTypes{*ConstTime_tPtrTy},
+                                      RetType{*StructTmPtrTy}, NoEvalCall)
+                                  .ArgConstraint(NotNull(ArgNo(0))));
+
+    Optional<QualType> Clockid_tTy = lookupType("clockid_t", ACtx);
+
+    if (Clockid_tTy && StructTimespecPtrTy)
+      // 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 = lookupType("itimerval", ACtx);
+    Optional<QualType> StructItimervalPtrTy;
+    if (StructItimervalTy)
+      StructItimervalPtrTy = ACtx.getPointerType(*StructItimervalTy);
+
+    if (StructItimervalPtrTy)
+      // int getitimer(int which, struct itimerval *curr_value);
+      addToFunctionSummaryMap("getitimer",
+                              Summary(ArgTypes{IntTy, *StructItimervalPtrTy},
+                                      RetType{IntTy}, NoEvalCall)
+                                  .ArgConstraint(NotNull(ArgNo(1))));
   }
 
   // Functions for testing.
@@ -2021,6 +2197,11 @@
                 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))));
   }
 }
 
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to