diff options
-rw-r--r-- | compiler-rt/lib/asan/asan_interceptors.cc | 36 | ||||
-rw-r--r-- | compiler-rt/lib/asan/asan_interceptors.h | 3 | ||||
-rw-r--r-- | compiler-rt/lib/asan/mach_override/mach_override.c | 1 | ||||
-rw-r--r-- | compiler-rt/lib/asan/tests/asan_test.cc | 45 |
4 files changed, 75 insertions, 10 deletions
diff --git a/compiler-rt/lib/asan/asan_interceptors.cc b/compiler-rt/lib/asan/asan_interceptors.cc index 3deeca02a0e..9fb2570d6ef 100644 --- a/compiler-rt/lib/asan/asan_interceptors.cc +++ b/compiler-rt/lib/asan/asan_interceptors.cc @@ -33,6 +33,7 @@ memcpy_f real_memcpy; memmove_f real_memmove; memset_f real_memset; strcasecmp_f real_strcasecmp; +strcat_f real_strcat; strchr_f real_strchr; strcmp_f real_strcmp; strcpy_f real_strcpy; @@ -80,18 +81,17 @@ static void AccessAddress(uintptr_t address, bool isWrite) { // Behavior of functions like "memcpy" or "strcpy" is undefined // if memory intervals overlap. We report error in this case. // Macro is used to avoid creation of new frames. -static inline bool RangesOverlap(const char *offset1, const char *offset2, - size_t length) { - return !((offset1 + length <= offset2) || (offset2 + length <= offset1)); +static inline bool RangesOverlap(const char *offset1, size_t length1, + const char *offset2, size_t length2) { + return !((offset1 + length1 <= offset2) || (offset2 + length2 <= offset1)); } -#define CHECK_RANGES_OVERLAP(_offset1, _offset2, length) do { \ +#define CHECK_RANGES_OVERLAP(_offset1, length1, _offset2, length2) do { \ const char *offset1 = (const char*)_offset1; \ const char *offset2 = (const char*)_offset2; \ - if (RangesOverlap((const char*)offset1, (const char*)offset2, \ - length)) { \ + if (RangesOverlap(offset1, length1, offset2, length2)) { \ Report("ERROR: AddressSanitizer strcpy-param-overlap: " \ "memory ranges [%p,%p) and [%p, %p) overlap\n", \ - offset1, offset1 + length, offset2, offset2 + length); \ + offset1, offset1 + length1, offset2, offset2 + length2); \ PRINT_CURRENT_STACK(); \ ShowStatsAndAbort(); \ } \ @@ -130,6 +130,7 @@ void InitializeAsanInterceptors() { INTERCEPT_FUNCTION(memmove); INTERCEPT_FUNCTION(memset); INTERCEPT_FUNCTION(strcasecmp); + INTERCEPT_FUNCTION(strcat); // NOLINT INTERCEPT_FUNCTION(strchr); INTERCEPT_FUNCTION(strcmp); INTERCEPT_FUNCTION(strcpy); // NOLINT @@ -185,7 +186,7 @@ void *WRAP(memcpy)(void *to, const void *from, size_t size) { } ENSURE_ASAN_INITED(); if (FLAG_replace_intrin) { - CHECK_RANGES_OVERLAP(to, from, size); + CHECK_RANGES_OVERLAP(to, size, from, size); ASAN_WRITE_RANGE(from, size); ASAN_READ_RANGE(to, size); } @@ -246,6 +247,21 @@ int WRAP(strcasecmp)(const char *s1, const char *s2) { return CharCaseCmp(c1, c2); } +char *WRAP(strcat)(char *to, const char *from) { // NOLINT + ENSURE_ASAN_INITED(); + if (FLAG_replace_str) { + size_t from_length = real_strlen(from); + ASAN_READ_RANGE(from, from_length + 1); + if (from_length > 0) { + size_t to_length = real_strlen(to); + ASAN_READ_RANGE(to, to_length); + ASAN_WRITE_RANGE(to + to_length, from_length + 1); + CHECK_RANGES_OVERLAP(to, to_length + 1, from, from_length + 1); + } + } + return real_strcat(to, from); +} + int WRAP(strcmp)(const char *s1, const char *s2) { // strcmp is called from malloc_default_purgeable_zone() // in __asan::ReplaceSystemAlloc() on Mac. @@ -273,7 +289,7 @@ char *WRAP(strcpy)(char *to, const char *from) { // NOLINT ENSURE_ASAN_INITED(); if (FLAG_replace_str) { size_t from_size = real_strlen(from) + 1; - CHECK_RANGES_OVERLAP(to, from, from_size); + CHECK_RANGES_OVERLAP(to, from_size, from, from_size); ASAN_READ_RANGE(from, from_size); ASAN_WRITE_RANGE(to, from_size); } @@ -339,7 +355,7 @@ char *WRAP(strncpy)(char *to, const char *from, size_t size) { ENSURE_ASAN_INITED(); if (FLAG_replace_str) { size_t from_size = Min(size, internal_strnlen(from, size) + 1); - CHECK_RANGES_OVERLAP(to, from, from_size); + CHECK_RANGES_OVERLAP(to, from_size, from, from_size); ASAN_READ_RANGE(from, from_size); ASAN_WRITE_RANGE(to, size); } diff --git a/compiler-rt/lib/asan/asan_interceptors.h b/compiler-rt/lib/asan/asan_interceptors.h index c8d9209805b..11fecc83444 100644 --- a/compiler-rt/lib/asan/asan_interceptors.h +++ b/compiler-rt/lib/asan/asan_interceptors.h @@ -72,6 +72,7 @@ void *WRAP(memcpy)(void *to, const void *from, size_t size); void *WRAP(memmove)(void *to, const void *from, size_t size); void *WRAP(memset)(void *block, int c, size_t size); int WRAP(strcasecmp)(const char *s1, const char *s2); +char *WRAP(strcat)(char *to, const char *from); // NOLINT char *WRAP(strchr)(const char *string, int c); int WRAP(strcmp)(const char *s1, const char *s2); char *WRAP(strcpy)(char *to, const char *from); // NOLINT @@ -90,6 +91,7 @@ typedef void* (*memcpy_f)(void *to, const void *from, size_t size); typedef void* (*memmove_f)(void *to, const void *from, size_t size); typedef void* (*memset_f)(void *block, int c, size_t size); typedef int (*strcasecmp_f)(const char *s1, const char *s2); +typedef char* (*strcat_f)(char *to, const char *from); typedef char* (*strchr_f)(const char *str, int c); typedef int (*strcmp_f)(const char *s1, const char *s2); typedef char* (*strcpy_f)(char *to, const char *from); @@ -107,6 +109,7 @@ extern memcpy_f real_memcpy; extern memmove_f real_memmove; extern memset_f real_memset; extern strcasecmp_f real_strcasecmp; +extern strcat_f real_strcat; extern strchr_f real_strchr; extern strcmp_f real_strcmp; extern strcpy_f real_strcpy; diff --git a/compiler-rt/lib/asan/mach_override/mach_override.c b/compiler-rt/lib/asan/mach_override/mach_override.c index 7c259000891..640d03d5897 100644 --- a/compiler-rt/lib/asan/mach_override/mach_override.c +++ b/compiler-rt/lib/asan/mach_override/mach_override.c @@ -627,6 +627,7 @@ static AsmInstructionMatch possibleInstructions[] = { { 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax { 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg { 0x3, {0xFF, 0x4C, 0x00}, {0x8B, 0x40, 0x00} }, // mov $imm(%eax-%edx), %reg + { 0x3, {0xFF, 0xCF, 0x00}, {0x8B, 0x4D, 0x00} }, // mov $imm(%rpb), %reg { 0x3, {0xFF, 0x4F, 0x00}, {0x8A, 0x4D, 0x00} }, // mov $imm(%ebp), %cl { 0x4, {0xFF, 0xFF, 0xFF, 0x00}, {0x8B, 0x4C, 0x24, 0x00} }, // mov $imm(%esp), %ecx { 0x4, {0xFF, 0x00, 0x00, 0x00}, {0x8B, 0x00, 0x00, 0x00} }, // mov r16,r/m16 or r32,r/m32 diff --git a/compiler-rt/lib/asan/tests/asan_test.cc b/compiler-rt/lib/asan/tests/asan_test.cc index eddb28fb49b..627e5a16872 100644 --- a/compiler-rt/lib/asan/tests/asan_test.cc +++ b/compiler-rt/lib/asan/tests/asan_test.cc @@ -1324,6 +1324,39 @@ TEST(AddressSanitizer, MemCmpOOBTest) { free(s2); } +TEST(AddressSanitizer, StrCatOOBTest) { + size_t to_size = Ident(100); + char *to = MallocAndMemsetString(to_size); + to[0] = '\0'; + size_t from_size = Ident(20); + char *from = MallocAndMemsetString(from_size); + from[from_size - 1] = '\0'; + // Normal strcat calls. + 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); + // One of arguments points to not allocated memory. + EXPECT_DEATH(strcat(to - 1, from), LeftOOBErrorMessage(1)); + EXPECT_DEATH(strcat(to, from - 1), LeftOOBErrorMessage(1)); + EXPECT_DEATH(strcat(to + to_size, from), RightOOBErrorMessage(0)); + EXPECT_DEATH(strcat(to, from + from_size), RightOOBErrorMessage(0)); + + // "from" is not zero-terminated. + from[from_size - 1] = 'z'; + EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0)); + from[from_size - 1] = '\0'; + // "to" is not zero-terminated. + memset(to, 'z', to_size); + EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0)); + // "to" is too short to fit "from". + to[to_size - from_size + 1] = '\0'; + EXPECT_DEATH(strcat(to, from), RightOOBErrorMessage(0)); + // length of "to" is just enough. + strcat(to, from + 1); +} + static const char *kOverlapErrorMessage = "strcpy-param-overlap"; TEST(AddressSanitizer, StrArgsOverlapTest) { @@ -1357,6 +1390,18 @@ TEST(AddressSanitizer, StrArgsOverlapTest) { strncpy(str + 11, str, 20); EXPECT_DEATH(strncpy(str + 10, str, 20), kOverlapErrorMessage); + // Check "strcat". + memset(str, 'z', size); + str[10] = '\0'; + str[20] = '\0'; + strcat(str, str + 10); + strcat(str, str + 11); + str[10] = '\0'; + strcat(str + 11, str); + EXPECT_DEATH(strcat(str, str + 9), kOverlapErrorMessage); + EXPECT_DEATH(strcat(str + 9, str), kOverlapErrorMessage); + EXPECT_DEATH(strcat(str + 10, str), kOverlapErrorMessage); + free(str); } |