forked from OSchip/llvm-project
[asan] interceptor for strcat. Patch by samsonov@google.com
llvm-svn: 147316
This commit is contained in:
parent
6579e355c0
commit
50bc2a71b2
|
@ -33,6 +33,7 @@ memcpy_f real_memcpy;
|
||||||
memmove_f real_memmove;
|
memmove_f real_memmove;
|
||||||
memset_f real_memset;
|
memset_f real_memset;
|
||||||
strcasecmp_f real_strcasecmp;
|
strcasecmp_f real_strcasecmp;
|
||||||
|
strcat_f real_strcat;
|
||||||
strchr_f real_strchr;
|
strchr_f real_strchr;
|
||||||
strcmp_f real_strcmp;
|
strcmp_f real_strcmp;
|
||||||
strcpy_f real_strcpy;
|
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
|
// Behavior of functions like "memcpy" or "strcpy" is undefined
|
||||||
// if memory intervals overlap. We report error in this case.
|
// if memory intervals overlap. We report error in this case.
|
||||||
// Macro is used to avoid creation of new frames.
|
// Macro is used to avoid creation of new frames.
|
||||||
static inline bool RangesOverlap(const char *offset1, const char *offset2,
|
static inline bool RangesOverlap(const char *offset1, size_t length1,
|
||||||
size_t length) {
|
const char *offset2, size_t length2) {
|
||||||
return !((offset1 + length <= offset2) || (offset2 + length <= offset1));
|
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 *offset1 = (const char*)_offset1; \
|
||||||
const char *offset2 = (const char*)_offset2; \
|
const char *offset2 = (const char*)_offset2; \
|
||||||
if (RangesOverlap((const char*)offset1, (const char*)offset2, \
|
if (RangesOverlap(offset1, length1, offset2, length2)) { \
|
||||||
length)) { \
|
|
||||||
Report("ERROR: AddressSanitizer strcpy-param-overlap: " \
|
Report("ERROR: AddressSanitizer strcpy-param-overlap: " \
|
||||||
"memory ranges [%p,%p) and [%p, %p) overlap\n", \
|
"memory ranges [%p,%p) and [%p, %p) overlap\n", \
|
||||||
offset1, offset1 + length, offset2, offset2 + length); \
|
offset1, offset1 + length1, offset2, offset2 + length2); \
|
||||||
PRINT_CURRENT_STACK(); \
|
PRINT_CURRENT_STACK(); \
|
||||||
ShowStatsAndAbort(); \
|
ShowStatsAndAbort(); \
|
||||||
} \
|
} \
|
||||||
|
@ -130,6 +130,7 @@ void InitializeAsanInterceptors() {
|
||||||
INTERCEPT_FUNCTION(memmove);
|
INTERCEPT_FUNCTION(memmove);
|
||||||
INTERCEPT_FUNCTION(memset);
|
INTERCEPT_FUNCTION(memset);
|
||||||
INTERCEPT_FUNCTION(strcasecmp);
|
INTERCEPT_FUNCTION(strcasecmp);
|
||||||
|
INTERCEPT_FUNCTION(strcat); // NOLINT
|
||||||
INTERCEPT_FUNCTION(strchr);
|
INTERCEPT_FUNCTION(strchr);
|
||||||
INTERCEPT_FUNCTION(strcmp);
|
INTERCEPT_FUNCTION(strcmp);
|
||||||
INTERCEPT_FUNCTION(strcpy); // NOLINT
|
INTERCEPT_FUNCTION(strcpy); // NOLINT
|
||||||
|
@ -185,7 +186,7 @@ void *WRAP(memcpy)(void *to, const void *from, size_t size) {
|
||||||
}
|
}
|
||||||
ENSURE_ASAN_INITED();
|
ENSURE_ASAN_INITED();
|
||||||
if (FLAG_replace_intrin) {
|
if (FLAG_replace_intrin) {
|
||||||
CHECK_RANGES_OVERLAP(to, from, size);
|
CHECK_RANGES_OVERLAP(to, size, from, size);
|
||||||
ASAN_WRITE_RANGE(from, size);
|
ASAN_WRITE_RANGE(from, size);
|
||||||
ASAN_READ_RANGE(to, size);
|
ASAN_READ_RANGE(to, size);
|
||||||
}
|
}
|
||||||
|
@ -246,6 +247,21 @@ int WRAP(strcasecmp)(const char *s1, const char *s2) {
|
||||||
return CharCaseCmp(c1, c2);
|
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) {
|
int WRAP(strcmp)(const char *s1, const char *s2) {
|
||||||
// strcmp is called from malloc_default_purgeable_zone()
|
// strcmp is called from malloc_default_purgeable_zone()
|
||||||
// in __asan::ReplaceSystemAlloc() on Mac.
|
// in __asan::ReplaceSystemAlloc() on Mac.
|
||||||
|
@ -273,7 +289,7 @@ char *WRAP(strcpy)(char *to, const char *from) { // NOLINT
|
||||||
ENSURE_ASAN_INITED();
|
ENSURE_ASAN_INITED();
|
||||||
if (FLAG_replace_str) {
|
if (FLAG_replace_str) {
|
||||||
size_t from_size = real_strlen(from) + 1;
|
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_READ_RANGE(from, from_size);
|
||||||
ASAN_WRITE_RANGE(to, 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();
|
ENSURE_ASAN_INITED();
|
||||||
if (FLAG_replace_str) {
|
if (FLAG_replace_str) {
|
||||||
size_t from_size = Min(size, internal_strnlen(from, size) + 1);
|
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_READ_RANGE(from, from_size);
|
||||||
ASAN_WRITE_RANGE(to, size);
|
ASAN_WRITE_RANGE(to, size);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(memmove)(void *to, const void *from, size_t size);
|
||||||
void *WRAP(memset)(void *block, int c, size_t size);
|
void *WRAP(memset)(void *block, int c, size_t size);
|
||||||
int WRAP(strcasecmp)(const char *s1, const char *s2);
|
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);
|
char *WRAP(strchr)(const char *string, int c);
|
||||||
int WRAP(strcmp)(const char *s1, const char *s2);
|
int WRAP(strcmp)(const char *s1, const char *s2);
|
||||||
char *WRAP(strcpy)(char *to, const char *from); // NOLINT
|
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* (*memmove_f)(void *to, const void *from, size_t size);
|
||||||
typedef void* (*memset_f)(void *block, int c, 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 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 char* (*strchr_f)(const char *str, int c);
|
||||||
typedef int (*strcmp_f)(const char *s1, const char *s2);
|
typedef int (*strcmp_f)(const char *s1, const char *s2);
|
||||||
typedef char* (*strcpy_f)(char *to, const char *from);
|
typedef char* (*strcpy_f)(char *to, const char *from);
|
||||||
|
@ -107,6 +109,7 @@ extern memcpy_f real_memcpy;
|
||||||
extern memmove_f real_memmove;
|
extern memmove_f real_memmove;
|
||||||
extern memset_f real_memset;
|
extern memset_f real_memset;
|
||||||
extern strcasecmp_f real_strcasecmp;
|
extern strcasecmp_f real_strcasecmp;
|
||||||
|
extern strcat_f real_strcat;
|
||||||
extern strchr_f real_strchr;
|
extern strchr_f real_strchr;
|
||||||
extern strcmp_f real_strcmp;
|
extern strcmp_f real_strcmp;
|
||||||
extern strcpy_f real_strcpy;
|
extern strcpy_f real_strcpy;
|
||||||
|
|
|
@ -627,6 +627,7 @@ static AsmInstructionMatch possibleInstructions[] = {
|
||||||
{ 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
|
{ 0x2, {0xFF, 0xFF}, {0x31, 0xC0} }, // xor %eax, %eax
|
||||||
{ 0x3, {0xFF, 0x4F, 0x00}, {0x8B, 0x45, 0x00} }, // mov $imm(%ebp), %reg
|
{ 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, 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
|
{ 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, 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
|
{ 0x4, {0xFF, 0x00, 0x00, 0x00}, {0x8B, 0x00, 0x00, 0x00} }, // mov r16,r/m16 or r32,r/m32
|
||||||
|
|
|
@ -1324,6 +1324,39 @@ TEST(AddressSanitizer, MemCmpOOBTest) {
|
||||||
free(s2);
|
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";
|
static const char *kOverlapErrorMessage = "strcpy-param-overlap";
|
||||||
|
|
||||||
TEST(AddressSanitizer, StrArgsOverlapTest) {
|
TEST(AddressSanitizer, StrArgsOverlapTest) {
|
||||||
|
@ -1357,6 +1390,18 @@ TEST(AddressSanitizer, StrArgsOverlapTest) {
|
||||||
strncpy(str + 11, str, 20);
|
strncpy(str + 11, str, 20);
|
||||||
EXPECT_DEATH(strncpy(str + 10, str, 20), kOverlapErrorMessage);
|
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);
|
free(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue