Make strcat() and strncat() more standard-compliant (check for invalid parameters even if zero bytes is copied, more accurate overlap check)
Fix the tests that were relying on the incorrect behavior.


git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@161167 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/asan/asan_interceptors.cc b/lib/asan/asan_interceptors.cc
index b0571f8..ec03fff 100644
--- a/lib/asan/asan_interceptors.cc
+++ b/lib/asan/asan_interceptors.cc
@@ -408,16 +408,22 @@
 # endif
 #endif  // ASAN_INTERCEPT_INDEX
 
+// For both strcat() and strncat() we need to check the validity of |to|
+// argument irrespective of the |from| length.
 INTERCEPTOR(char*, strcat, char *to, const char *from) {  // NOLINT
   ENSURE_ASAN_INITED();
   if (flags()->replace_str) {
     uptr from_length = REAL(strlen)(from);
     ASAN_READ_RANGE(from, from_length + 1);
+    uptr to_length = REAL(strlen)(to);
+    ASAN_READ_RANGE(to, to_length);
+    ASAN_WRITE_RANGE(to + to_length, from_length + 1);
+    // If the copying actually happens, the |from| string should not overlap
+    // with the resulting string starting at |to|, which has a length of
+    // to_length + from_length + 1.
     if (from_length > 0) {
-      uptr to_length = REAL(strlen)(to);
-      ASAN_READ_RANGE(to, to_length);
-      ASAN_WRITE_RANGE(to + to_length, from_length + 1);
-      CHECK_RANGES_OVERLAP("strcat", to, to_length + 1, from, from_length + 1);
+      CHECK_RANGES_OVERLAP("strcat", to, from_length + to_length + 1,
+                           from, from_length + 1);
     }
   }
   return REAL(strcat)(to, from);  // NOLINT
@@ -425,15 +431,16 @@
 
 INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) {
   ENSURE_ASAN_INITED();
-  if (flags()->replace_str && size > 0) {
+  if (flags()->replace_str) {
     uptr from_length = MaybeRealStrnlen(from, size);
-    ASAN_READ_RANGE(from, Min(size, from_length + 1));
+    uptr copy_length = Min(size, from_length + 1);
+    ASAN_READ_RANGE(from, copy_length);
     uptr to_length = REAL(strlen)(to);
     ASAN_READ_RANGE(to, to_length);
     ASAN_WRITE_RANGE(to + to_length, from_length + 1);
     if (from_length > 0) {
-      CHECK_RANGES_OVERLAP("strncat", to, to_length + 1,
-                           from, Min(size, from_length + 1));
+      CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1,
+                           from, copy_length);
     }
   }
   return REAL(strncat)(to, from, size);
diff --git a/lib/asan/tests/asan_test.cc b/lib/asan/tests/asan_test.cc
index 8e967e9..994e78a 100644
--- a/lib/asan/tests/asan_test.cc
+++ b/lib/asan/tests/asan_test.cc
@@ -1226,8 +1226,9 @@
   strcat(to, from);
   strcat(to, from);
   strcat(to + from_size, from + from_size - 2);
-  // Catenate empty string is not always an error.
-  strcat(to - 1, from + from_size - 1);
+  // Passing an invalid pointer is an error even when concatenating an empty
+  // string.
+  EXPECT_DEATH(strcat(to - 1, from + from_size - 1), LeftOOBErrorMessage(1));
   // One of arguments points to not allocated memory.
   EXPECT_DEATH(strcat(to - 1, from), LeftOOBErrorMessage(1));
   EXPECT_DEATH(strcat(to, from - 1), LeftOOBErrorMessage(1));
@@ -1262,8 +1263,8 @@
   strncat(to, from, from_size);
   from[from_size - 1] = '\0';
   strncat(to, from, 2 * from_size);
-  // Catenating empty string is not an error.
-  strncat(to - 1, from, 0);
+  // Catenating empty string with an invalid string is still an error.
+  EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBErrorMessage(1));
   strncat(to, from + from_size - 1, 10);
   // One of arguments points to not allocated memory.
   EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBErrorMessage(1));
@@ -1336,7 +1337,7 @@
   str[10] = '\0';
   str[20] = '\0';
   strcat(str, str + 10);
-  strcat(str, str + 11);
+  EXPECT_DEATH(strcat(str, str + 11), OverlapErrorMessage("strcat"));
   str[10] = '\0';
   strcat(str + 11, str);
   EXPECT_DEATH(strcat(str, str + 9), OverlapErrorMessage("strcat"));
@@ -1347,7 +1348,7 @@
   memset(str, 'z', size);
   str[10] = '\0';
   strncat(str, str + 10, 10);  // from is empty
-  strncat(str, str + 11, 10);
+  EXPECT_DEATH(strncat(str, str + 11, 10), OverlapErrorMessage("strncat"));
   str[10] = '\0';
   str[20] = '\0';
   strncat(str + 5, str, 5);