Index: lib/asan/asan_interceptors.h =================================================================== --- lib/asan/asan_interceptors.h +++ lib/asan/asan_interceptors.h @@ -75,8 +75,16 @@ #if SANITIZER_LINUX && !SANITIZER_ANDROID # define ASAN_INTERCEPT___LONGJMP_CHK 1 +# define ASAN_INTERCEPT___STRCPY_CHK 1 +# define ASAN_INTERCEPT___STRNCPY_CHK 1 +# define ASAN_INTERCEPT___STRCAT_CHK 1 +# define ASAN_INTERCEPT___STRNCAT_CHK 1 #else # define ASAN_INTERCEPT___LONGJMP_CHK 0 +# define ASAN_INTERCEPT___STRCPY_CHK 0 +# define ASAN_INTERCEPT___STRNCPY_CHK 0 +# define ASAN_INTERCEPT___STRCAT_CHK 0 +# define ASAN_INTERCEPT___STRNCAT_CHK 0 #endif // Android bug: https://code.google.com/p/android/issues/detail?id=61799 Index: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -355,6 +355,29 @@ return REAL(strcat)(to, from); // NOLINT } +#if ASAN_INTERCEPT___STRCAT_CHK +INTERCEPTOR(char*, __strcat_chk, char *to, const char *from, uptr to_size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, __strcat_chk); // NOLINT + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_length = REAL(strlen)(from); + ASAN_READ_RANGE(ctx, from, from_length + 1); + uptr to_length = REAL(strlen)(to); + ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); + ASAN_WRITE_RANGE(ctx, 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) { + CHECK_RANGES_OVERLAP("__strcat_chk", to, from_length + to_length + 1, + from, from_length + 1); + } + } + return REAL(strcat)(to, from); // NOLINT +} +#endif + INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strncat); @@ -374,6 +397,28 @@ return REAL(strncat)(to, from, size); } +#if ASAN_INTERCEPT___STRNCAT_CHK +INTERCEPTOR(char*, __strncat_chk, char *to, const char *from, uptr size, + uptr to_size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, __strncat_chk); + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_length = MaybeRealStrnlen(from, size); + uptr copy_length = Min(size, from_length + 1); + ASAN_READ_RANGE(ctx, from, copy_length); + uptr to_length = REAL(strlen)(to); + ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); + ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); + if (from_length > 0) { + CHECK_RANGES_OVERLAP("__strncat_chk", to, to_length + copy_length + 1, + from, copy_length); + } + } + return REAL(strncat)(to, from, size); +} +#endif + INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strcpy); // NOLINT @@ -395,6 +440,30 @@ return REAL(strcpy)(to, from); // NOLINT } +#if ASAN_INTERCEPT___STRCPY_CHK +INTERCEPTOR(char*, __strcpy_chk, char *to, const char *from, + uptr to_size) { // NOLINT + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, __strcpy_chk); // NOLINT +#if SANITIZER_MAC + if (UNLIKELY(!asan_inited)) return REAL(strcpy)(to, from); // NOLINT +#endif + // strcpy is called from malloc_default_purgeable_zone() + // in __asan::ReplaceSystemAlloc() on Mac. + if (asan_init_is_running) { + return REAL(strcpy)(to, from); // NOLINT + } + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_size = REAL(strlen)(from) + 1; + CHECK_RANGES_OVERLAP("__strcpy_chk", to, from_size, from, from_size); + ASAN_READ_RANGE(ctx, from, from_size); + ASAN_WRITE_RANGE(ctx, to, from_size); + } + return REAL(strcpy)(to, from); // NOLINT +} +#endif + INTERCEPTOR(char*, strdup, const char *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strdup); @@ -440,6 +509,22 @@ return REAL(strncpy)(to, from, size); } +#if ASAN_INTERCEPT___STRNCPY_CHK +INTERCEPTOR(char*, __strncpy_chk, char *to, const char *from, uptr size, + uptr to_size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, __strncpy_chk); + ENSURE_ASAN_INITED(); + if (flags()->replace_str) { + uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1); + CHECK_RANGES_OVERLAP("__strncpy_chk", to, from_size, from, from_size); + ASAN_READ_RANGE(ctx, from, from_size); + ASAN_WRITE_RANGE(ctx, to, size); + } + return REAL(strncpy)(to, from, size); +} +#endif + INTERCEPTOR(long, strtol, const char *nptr, // NOLINT char **endptr, int base) { void *ctx; @@ -618,6 +703,19 @@ ASAN_INTERCEPT_FUNC(fork); #endif +#if ASAN_INTERCEPT___STRCPY_CHK + ASAN_INTERCEPT_FUNC(__strcpy_chk); +#endif +#if ASAN_INTERCEPT___STRNCPY_CHK + ASAN_INTERCEPT_FUNC(__strncpy_chk); +#endif +#if ASAN_INTERCEPT___STRCAT_CHK + ASAN_INTERCEPT_FUNC(__strcat_chk); +#endif +#if ASAN_INTERCEPT___STRNCAT_CHK + ASAN_INTERCEPT_FUNC(__strncat_chk); +#endif + InitializePlatformInterceptors(); VReport(1, "AddressSanitizer: libc interceptors initialized\n"); Index: test/asan/TestCases/Linux/strcat-fortify.c =================================================================== --- /dev/null +++ test/asan/TestCases/Linux/strcat-fortify.c @@ -0,0 +1,19 @@ +// RUN: %clang -fPIC -shared -D_DSO %s -o %t.so +// RUN: %clang_asan %s -o %t %t.so +// RUN: not %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: android +#ifdef _DSO +#include +extern char *__strcat_chk(char *, const char *, size_t); +int foo() { + char *write_buffer = (char *)malloc(2); + const char *read_buffer = "abcdeabcde"; + write_buffer[1] = '\0'; + // CHECK: AddressSanitizer: heap-buffer-overflow + __strcat_chk(write_buffer, read_buffer, 2); + return write_buffer[0]; +} +#else +extern int foo (); +int main() { return foo(); } +#endif Index: test/asan/TestCases/Linux/strcpy-fortify.c =================================================================== --- /dev/null +++ test/asan/TestCases/Linux/strcpy-fortify.c @@ -0,0 +1,19 @@ +// RUN: %clang -fPIC -shared -D_DSO %s -o %t.so +// RUN: %clang_asan %s -o %t %t.so +// RUN: not %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: android +#ifdef _DSO +#include +extern char *__strcpy_chk(char *, const char *, size_t); +int foo() { + char *write_buffer = (char *)malloc(1); + const char *read_buffer = "abcdeabcde"; + // CHECK: AddressSanitizer: heap-buffer-overflow + __strcpy_chk(write_buffer, read_buffer, 10); + return write_buffer[0]; +} +#else +extern int foo(); +int main() { return foo(); } +#endif + Index: test/asan/TestCases/Linux/strncat-fortify.c =================================================================== --- /dev/null +++ test/asan/TestCases/Linux/strncat-fortify.c @@ -0,0 +1,19 @@ +// RUN: %clang -fPIC -shared -O0 -D_DSO %s -o %t.so +// RUN: %clang_asan %s -o %t %t.so +// RUN: not %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: android +#ifdef _DSO +#include +extern char *__strncat_chk(char *, const char *, size_t, size_t); +int foo() { + char *write_buffer = (char *)malloc(2); + const char *read_buffer = "abcdeabcde"; + write_buffer[1] = '\0'; + // CHECK: AddressSanitizer: heap-buffer-overflow + __strncat_chk(write_buffer, read_buffer, 10, 2); + return write_buffer[0]; +} +#else +extern int foo(); +int main() { return foo(); } +#endif Index: test/asan/TestCases/Linux/strncpy-fortify.c =================================================================== --- /dev/null +++ test/asan/TestCases/Linux/strncpy-fortify.c @@ -0,0 +1,18 @@ +// RUN: %clang -fPIC -shared -D_DSO %s -o %t.so +// RUN: %clang_asan %s -o %t %t.so +// RUN: not %run %t 2>&1 | FileCheck %s +// UNSUPPORTED: android +#ifdef _DSO +#include +extern char *__strncpy_chk(char *, const char *, size_t, size_t); +int foo() { + char *write_buffer = (char *)malloc(1); + const char *read_buffer = "abcdeabcde"; + // CHECK: AddressSanitizer: heap-buffer-overflow + __strncpy_chk(write_buffer, read_buffer, 10, 1); + return write_buffer[0]; +} +#else +extern int foo(); +int main() { return foo(); } +#endif