Skip to content

Commit

Permalink
[asan] Add strndup/__strndup interceptors.
Browse files Browse the repository at this point in the history
Recommit of r302781 with Vitaly Buka's fix for non zero terminated strings.

Differential Revision: https://reviews.llvm.org/D31457

llvm-svn: 304399
  • Loading branch information
goussepi committed Jun 1, 2017
1 parent f51decb commit 183d136
Show file tree
Hide file tree
Showing 11 changed files with 191 additions and 31 deletions.
4 changes: 4 additions & 0 deletions compiler-rt/lib/asan/asan_flags.cc
Expand Up @@ -194,6 +194,10 @@ void InitializeFlags() {
Report("WARNING: strchr* interceptors are enabled even though "
"replace_str=0. Use intercept_strchr=0 to disable them.");
}
if (!f->replace_str && common_flags()->intercept_strndup) {
Report("WARNING: strndup* interceptors are enabled even though "
"replace_str=0. Use intercept_strndup=0 to disable them.");
}
}

} // namespace __asan
Expand Down
27 changes: 27 additions & 0 deletions compiler-rt/lib/asan/tests/asan_str_test.cc
Expand Up @@ -154,6 +154,33 @@ TEST(AddressSanitizer, MAYBE_StrDupOOBTest) {
free(str);
}

#if SANITIZER_TEST_HAS_STRNDUP
TEST(AddressSanitizer, MAYBE_StrNDupOOBTest) {
size_t size = Ident(42);
char *str = MallocAndMemsetString(size);
char *new_str;
// Normal strndup 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));
// Check handling of non 0 terminated strings.
Ident(new_str = strndup(str + size - 1, 0));
free(new_str);
Ident(new_str = strndup(str + size - 1, 1));
free(new_str);
EXPECT_DEATH(Ident(strndup(str + size - 1, 2)), 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
Expand Down
36 changes: 7 additions & 29 deletions compiler-rt/lib/msan/msan_interceptors.cc
Expand Up @@ -341,33 +341,6 @@ INTERCEPTOR(char *, __strdup, char *src) {
#define MSAN_MAYBE_INTERCEPT___STRDUP
#endif

INTERCEPTOR(char *, strndup, char *src, SIZE_T n) {
ENSURE_MSAN_INITED();
GET_STORE_STACK_TRACE;
// On FreeBSD strndup() leverages strnlen().
InterceptorScope interceptor_scope;
SIZE_T copy_size = REAL(strnlen)(src, n);
char *res = REAL(strndup)(src, n);
CopyShadowAndOrigin(res, src, copy_size, &stack);
__msan_unpoison(res + copy_size, 1); // \0
return res;
}

#if !SANITIZER_FREEBSD
INTERCEPTOR(char *, __strndup, char *src, SIZE_T n) {
ENSURE_MSAN_INITED();
GET_STORE_STACK_TRACE;
SIZE_T copy_size = REAL(strnlen)(src, n);
char *res = REAL(__strndup)(src, n);
CopyShadowAndOrigin(res, src, copy_size, &stack);
__msan_unpoison(res + copy_size, 1); // \0
return res;
}
#define MSAN_MAYBE_INTERCEPT___STRNDUP INTERCEPT_FUNCTION(__strndup)
#else
#define MSAN_MAYBE_INTERCEPT___STRNDUP
#endif

INTERCEPTOR(char *, gcvt, double number, SIZE_T ndigit, char *buf) {
ENSURE_MSAN_INITED();
char *res = REAL(gcvt)(number, ndigit, buf);
Expand Down Expand Up @@ -1373,6 +1346,13 @@ int OnExit() {
return __msan_memcpy(to, from, size); \
}

#define COMMON_INTERCEPTOR_COPY_STRING(ctx, to, from, size) \
do { \
GET_STORE_STACK_TRACE; \
CopyShadowAndOrigin(to, from, size, &stack); \
__msan_unpoison(to + size, 1); \
} while (false)

#include "sanitizer_common/sanitizer_platform_interceptors.h"
#include "sanitizer_common/sanitizer_common_interceptors.inc"

Expand Down Expand Up @@ -1540,8 +1520,6 @@ void InitializeInterceptors() {
INTERCEPT_FUNCTION(stpcpy); // NOLINT
INTERCEPT_FUNCTION(strdup);
MSAN_MAYBE_INTERCEPT___STRDUP;
INTERCEPT_FUNCTION(strndup);
MSAN_MAYBE_INTERCEPT___STRNDUP;
INTERCEPT_FUNCTION(strncpy); // NOLINT
INTERCEPT_FUNCTION(gcvt);
INTERCEPT_FUNCTION(strcat); // NOLINT
Expand Down
13 changes: 11 additions & 2 deletions compiler-rt/lib/msan/tests/msan_test.cc
Expand Up @@ -1581,19 +1581,28 @@ TEST(MemorySanitizer, strdup) {
TEST(MemorySanitizer, strndup) {
char buf[4] = "abc";
__msan_poison(buf + 2, sizeof(*buf));
char *x = strndup(buf, 3);
char *x;
EXPECT_UMR(x = strndup(buf, 3));
EXPECT_NOT_POISONED(x[0]);
EXPECT_NOT_POISONED(x[1]);
EXPECT_POISONED(x[2]);
EXPECT_NOT_POISONED(x[3]);
free(x);
// Check handling of non 0 terminated strings.
buf[3] = 'z';
__msan_poison(buf + 3, sizeof(*buf));
EXPECT_UMR(x = strndup(buf + 3, 1));
EXPECT_POISONED(x[0]);
EXPECT_NOT_POISONED(x[1]);
free(x);
}

TEST(MemorySanitizer, strndup_short) {
char buf[4] = "abc";
__msan_poison(buf + 1, sizeof(*buf));
__msan_poison(buf + 2, sizeof(*buf));
char *x = strndup(buf, 2);
char *x;
EXPECT_UMR(x = strndup(buf, 2));
EXPECT_NOT_POISONED(x[0]);
EXPECT_POISONED(x[1]);
EXPECT_NOT_POISONED(x[2]);
Expand Down
42 changes: 42 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_common_interceptors.inc
Expand Up @@ -34,6 +34,8 @@
// COMMON_INTERCEPTOR_MEMSET_IMPL
// COMMON_INTERCEPTOR_MEMMOVE_IMPL
// COMMON_INTERCEPTOR_MEMCPY_IMPL
// COMMON_INTERCEPTOR_COPY_STRING
// COMMON_INTERCEPTOR_STRNDUP_IMPL
//===----------------------------------------------------------------------===//

#include "interception/interception.h"
Expand Down Expand Up @@ -217,6 +219,24 @@ bool PlatformHasDifferentMemcpyAndMemmove();
}
#endif

#ifndef COMMON_INTERCEPTOR_COPY_STRING
#define COMMON_INTERCEPTOR_COPY_STRING(ctx, to, from, size) {}
#endif

#ifndef COMMON_INTERCEPTOR_STRNDUP_IMPL
#define COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size) \
COMMON_INTERCEPTOR_ENTER(ctx, strndup, s, size); \
uptr copy_length = internal_strnlen(s, size); \
char *new_mem = (char *)WRAP(malloc)(copy_length + 1); \
if (common_flags()->intercept_strndup) { \
COMMON_INTERCEPTOR_READ_STRING(ctx, s, Min(size, copy_length + 1)); \
} \
COMMON_INTERCEPTOR_COPY_STRING(ctx, new_mem, s, copy_length); \
internal_memcpy(new_mem, s, copy_length); \
new_mem[copy_length] = '\0'; \
return new_mem;
#endif

struct FileMetadata {
// For open_memstream().
char **addr;
Expand Down Expand Up @@ -300,6 +320,26 @@ INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T maxlen) {
#define INIT_STRNLEN
#endif

#if SANITIZER_INTERCEPT_STRNDUP
INTERCEPTOR(char*, strndup, const char *s, uptr size) {
void *ctx;
COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size);
}
#define INIT_STRNDUP COMMON_INTERCEPT_FUNCTION(strndup)
#else
#define INIT_STRNDUP
#endif // SANITIZER_INTERCEPT_STRNDUP

#if SANITIZER_INTERCEPT___STRNDUP
INTERCEPTOR(char*, __strndup, const char *s, uptr size) {
void *ctx;
COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size);
}
#define INIT___STRNDUP COMMON_INTERCEPT_FUNCTION(__strndup)
#else
#define INIT___STRNDUP
#endif // SANITIZER_INTERCEPT___STRNDUP

#if SANITIZER_INTERCEPT_TEXTDOMAIN
INTERCEPTOR(char*, textdomain, const char *domainname) {
void *ctx;
Expand Down Expand Up @@ -6163,6 +6203,8 @@ static void InitializeCommonInterceptors() {
INIT_TEXTDOMAIN;
INIT_STRLEN;
INIT_STRNLEN;
INIT_STRNDUP;
INIT___STRNDUP;
INIT_STRCMP;
INIT_STRNCMP;
INIT_STRCASECMP;
Expand Down
3 changes: 3 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
Expand Up @@ -197,6 +197,9 @@ COMMON_FLAG(bool, intercept_strpbrk, true,
COMMON_FLAG(bool, intercept_strlen, true,
"If set, uses custom wrappers for strlen and strnlen functions "
"to find more errors.")
COMMON_FLAG(bool, intercept_strndup, true,
"If set, uses custom wrappers for strndup functions "
"to find more errors.")
COMMON_FLAG(bool, intercept_strchr, true,
"If set, uses custom wrappers for strchr, strchrnul, and strrchr "
"functions to find more errors.")
Expand Down
14 changes: 14 additions & 0 deletions compiler-rt/lib/sanitizer_common/sanitizer_platform_interceptors.h
Expand Up @@ -25,6 +25,12 @@
# define SI_NOT_WINDOWS 0
#endif

#if SANITIZER_POSIX
# define SI_POSIX 1
#else
# define SI_POSIX 0
#endif

#if SANITIZER_LINUX && !SANITIZER_ANDROID
# define SI_LINUX_NOT_ANDROID 1
#else
Expand Down Expand Up @@ -69,6 +75,12 @@
# define SI_UNIX_NOT_MAC 0
#endif

#if SANITIZER_LINUX && !SANITIZER_FREEBSD
# define SI_LINUX_NOT_FREEBSD 1
# else
# define SI_LINUX_NOT_FREEBSD 0
#endif

#define SANITIZER_INTERCEPT_STRLEN 1
#define SANITIZER_INTERCEPT_STRNLEN SI_NOT_MAC
#define SANITIZER_INTERCEPT_STRCMP 1
Expand All @@ -86,6 +98,8 @@
#define SANITIZER_INTERCEPT_MEMMOVE 1
#define SANITIZER_INTERCEPT_MEMCPY 1
#define SANITIZER_INTERCEPT_MEMCMP 1
#define SANITIZER_INTERCEPT_STRNDUP SI_POSIX
#define SANITIZER_INTERCEPT___STRNDUP SI_LINUX_NOT_FREEBSD
#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \
__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070
# define SI_MAC_DEPLOYMENT_BELOW_10_7 1
Expand Down
6 changes: 6 additions & 0 deletions compiler-rt/lib/sanitizer_common/tests/sanitizer_test_utils.h
Expand Up @@ -124,4 +124,10 @@ static inline uint32_t my_rand() {
# define SANITIZER_TEST_HAS_PRINTF_L 0
#endif

#if !defined(_MSC_VER)
# define SANITIZER_TEST_HAS_STRNDUP 1
#else
# define SANITIZER_TEST_HAS_STRNDUP 0
#endif

#endif // SANITIZER_TEST_UTILS_H
27 changes: 27 additions & 0 deletions compiler-rt/test/asan/TestCases/Posix/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

// Unwind problem on arm: "main" is missing from the allocation stack trace.
// UNSUPPORTED: win32,s390,armv7l-unknown-linux-gnueabihf

#include <string.h>

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;
}
22 changes: 22 additions & 0 deletions compiler-rt/test/asan/TestCases/Posix/strndup_oob_test2.cc
@@ -0,0 +1,22 @@
// RUN: %clang_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clang_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clang_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s
// RUN: %clang_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s

// When built as C on Linux, strndup is transformed to __strndup.
// RUN: %clang_asan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck %s

// Unwind problem on arm: "main" is missing from the allocation stack trace.
// UNSUPPORTED: win32,s390,armv7l-unknown-linux-gnueabihf

#include <string.h>

char kChars[] = { 'f', 'o', 'o' };

int main(int argc, char **argv) {
char *copy = strndup(kChars, 3);
copy = strndup(kChars, 10);
// CHECK: AddressSanitizer: global-buffer-overflow
// CHECK: {{.*}}main {{.*}}.cc:[[@LINE-2]]
return *copy;
}
28 changes: 28 additions & 0 deletions compiler-rt/test/msan/strndup.cc
@@ -0,0 +1,28 @@
// RUN: %clangxx_msan %s -o %t && not %run %t 2>&1 | FileCheck --check-prefix=ON %s
// RUN: %clangxx_msan %s -o %t && MSAN_OPTIONS=intercept_strndup=0 %run %t 2>&1 | FileCheck --check-prefix=OFF --allow-empty %s

// When built as C on Linux, strndup is transformed to __strndup.
// RUN: %clangxx_msan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck --check-prefix=ON %s

// UNSUPPORTED: win32

#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sanitizer/msan_interface.h>

int main(int argc, char **argv) {
char kString[4] = "abc";
__msan_poison(kString + 2, 1);
char *copy = strndup(kString, 4); // BOOM
assert(__msan_test_shadow(copy, 4) == 2); // Poisoning is preserved.
free(copy);
return 0;
// ON: Uninitialized bytes in __interceptor_{{(__)?}}strndup at offset 2 inside [{{.*}}, 4)
// ON: MemorySanitizer: use-of-uninitialized-value
// ON: #0 {{.*}}main {{.*}}strndup.cc:[[@LINE-6]]
// ON-LABEL: SUMMARY
// ON: {{.*}}strndup.cc:[[@LINE-8]]
// OFF-NOT: MemorySanitizer
}

0 comments on commit 183d136

Please sign in to comment.