Index: lib/asan/asan_interceptors.h =================================================================== --- lib/asan/asan_interceptors.h +++ lib/asan/asan_interceptors.h @@ -74,8 +74,12 @@ #if SANITIZER_LINUX && !SANITIZER_ANDROID # define ASAN_INTERCEPT___STRDUP 1 +# define ASAN_INTERCEPT_STRNDUP 1 +# define ASAN_INTERCEPT___STRNDUP 1 #else # define ASAN_INTERCEPT___STRDUP 0 +# define ASAN_INTERCEPT_STRNDUP 0 +# define ASAN_INTERCEPT___STRNDUP 0 #endif DECLARE_REAL(int, memcmp, const void *a1, const void *a2, uptr size) Index: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -570,6 +570,42 @@ } #endif // ASAN_INTERCEPT___STRDUP +#if ASAN_INTERCEPT_STRNDUP +INTERCEPTOR(char*, strndup, const char *s, uptr size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strndup); + if (UNLIKELY(!asan_inited)) return internal_strndup(s, size); + ENSURE_ASAN_INITED(); + uptr from_length = REAL(strlen)(s); + uptr copy_length = Min(size, from_length + 1); + if (flags()->replace_str) { + ASAN_READ_RANGE(ctx, s, copy_length); + } + GET_STACK_TRACE_MALLOC; + void *new_mem = asan_malloc(copy_length, &stack); + REAL(memcpy)(new_mem, s, copy_length - 1); + return reinterpret_cast(new_mem); +} +#endif // ASAN_INTERCEPT_STRNDUP + +#if ASAN_INTERCEPT___STRNDUP +INTERCEPTOR(char*, __strndup, const char *s, uptr size) { + void *ctx; + ASAN_INTERCEPTOR_ENTER(ctx, strndup); + if (UNLIKELY(!asan_inited)) return internal_strndup(s, size); + ENSURE_ASAN_INITED(); + uptr from_length = REAL(strlen)(s); + uptr copy_length = Min(size, from_length + 1); + if (flags()->replace_str) { + ASAN_READ_RANGE(ctx, s, copy_length); + } + GET_STACK_TRACE_MALLOC; + void *new_mem = asan_malloc(copy_length, &stack); + REAL(memcpy)(new_mem, s, copy_length - 1); + return reinterpret_cast(new_mem); +} +#endif // ASAN_INTERCEPT___STRNDUP + INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, wcslen); @@ -722,6 +758,12 @@ #if ASAN_INTERCEPT___STRDUP ASAN_INTERCEPT_FUNC(__strdup); #endif +#if ASAN_INTERCEPT_STRNDUP + ASAN_INTERCEPT_FUNC(strndup); +#endif +#if ASAN_INTERCEPT___STRNDUP + ASAN_INTERCEPT_FUNC(__strndup); +#endif #if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX ASAN_INTERCEPT_FUNC(index); #endif Index: lib/asan/tests/asan_str_test.cc =================================================================== --- lib/asan/tests/asan_str_test.cc +++ lib/asan/tests/asan_str_test.cc @@ -154,6 +154,27 @@ free(str); } +#if SANITIZER_TEST_HAS_STRNDUP +TEST(AddressSanitizer, MAYBE_StrNDupOOBTest) { + size_t size = Ident(42); + char *str = MallocAndMemsetString(size); + char *new_str; + // Normal strdup calls. + str[size - 1] = '\0'; + new_str = strndup(str, size - 13); + free(new_str); + new_str = strndup(str + size - 1, 13); + free(new_str); + // Argument points to not allocated memory. + EXPECT_DEATH(Ident(strndup(str - 1, 13)), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strndup(str + size, 13)), RightOOBReadMessage(0)); + // Overwrite the terminating '\0' and hit unallocated memory. + str[size - 1] = 'z'; + EXPECT_DEATH(Ident(strndup(str, size + 13)), RightOOBReadMessage(0)); + free(str); +} +#endif // SANITIZER_TEST_HAS_STRNDUP + TEST(AddressSanitizer, StrCpyOOBTest) { size_t to_size = Ident(30); size_t from_size = Ident(6); // less than to_size Index: lib/sanitizer_common/tests/sanitizer_test_utils.h =================================================================== --- lib/sanitizer_common/tests/sanitizer_test_utils.h +++ lib/sanitizer_common/tests/sanitizer_test_utils.h @@ -124,4 +124,10 @@ # define SANITIZER_TEST_HAS_PRINTF_L 0 #endif +#if defined(__linux__) && !defined(__ANDROID__) +# define SANITIZER_TEST_HAS_STRNDUP 1 +#else +# define SANITIZER_TEST_HAS_STRNDUP 0 +#endif + #endif // SANITIZER_TEST_UTILS_H Index: test/asan/TestCases/strndup_oob_test.cc =================================================================== --- /dev/null +++ test/asan/TestCases/strndup_oob_test.cc @@ -0,0 +1,27 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// When built as C on Linux, strndup is transformed to __strndup. +// RUN: %clangxx_asan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck %s + +// REQUIRES: linux +// XFAIL: android + +#include + +char kString[] = "foo"; + +int main(int argc, char **argv) { + char *copy = strndup(kString, 2); + int x = copy[2 + argc]; // BOOM + // CHECK: AddressSanitizer: heap-buffer-overflow + // CHECK: #0 {{.*}}main {{.*}}strndup_oob_test.cc:[[@LINE-2]] + // CHECK-LABEL: allocated by thread T{{.*}} here: + // CHECK: #{{[01]}} {{.*}}strndup + // CHECK: #{{.*}}main {{.*}}strndup_oob_test.cc:[[@LINE-6]] + // CHECK-LABEL: SUMMARY + // CHECK: strndup_oob_test.cc:[[@LINE-7]] + return x; +}