Index: lib/asan/tests/asan_mem_test.cc =================================================================== --- lib/asan/tests/asan_mem_test.cc +++ lib/asan/tests/asan_mem_test.cc @@ -10,6 +10,11 @@ // //===----------------------------------------------------------------------===// #include "asan_test_utils.h" +#ifdef _GNU_SOURCE +#include // for bcmp +#elif defined(__NetBSD__) +#include // for bcmp +#endif #include template @@ -205,37 +210,44 @@ MemTransferOOBTestTemplate(1024); } - -TEST(AddressSanitizer, MemCmpOOBTest) { +template +void CmpOOBTestCommon() { size_t size = Ident(100); char *s1 = MallocAndMemsetString(size); char *s2 = MallocAndMemsetString(size); - // Normal memcmp calls. - Ident(memcmp(s1, s2, size)); - Ident(memcmp(s1 + size - 1, s2 + size - 1, 1)); - Ident(memcmp(s1 - 1, s2 - 1, 0)); + // Normal cmpfn calls. + Ident(cmpfn(s1, s2, size)); + Ident(cmpfn(s1 + size - 1, s2 + size - 1, 1)); + Ident(cmpfn(s1 - 1, s2 - 1, 0)); // One of arguments points to not allocated memory. - EXPECT_DEATH(Ident(memcmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(memcmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1)); - EXPECT_DEATH(Ident(memcmp)(s1 + size, s2, 1), RightOOBReadMessage(0)); - EXPECT_DEATH(Ident(memcmp)(s1, s2 + size, 1), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(cmpfn)(s1 - 1, s2, 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(cmpfn)(s1, s2 - 1, 1), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(cmpfn)(s1 + size, s2, 1), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(cmpfn)(s1, s2 + size, 1), RightOOBReadMessage(0)); // Hit unallocated memory and die. - EXPECT_DEATH(Ident(memcmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0)); - EXPECT_DEATH(Ident(memcmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(cmpfn)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(cmpfn)(s1 + size - 1, s2, 2), RightOOBReadMessage(0)); // Zero bytes are not terminators and don't prevent from OOB. s1[size - 1] = '\0'; s2[size - 1] = '\0'; - EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(cmpfn)(s1, s2, size + 1), RightOOBReadMessage(0)); // Even if the buffers differ in the first byte, we still assume that - // memcmp may access the whole buffer and thus reporting the overflow here: + // cmpfn may access the whole buffer and thus reporting the overflow here: s1[0] = 1; s2[0] = 123; - EXPECT_DEATH(Ident(memcmp)(s1, s2, size + 1), RightOOBReadMessage(0)); + EXPECT_DEATH(Ident(cmpfn)(s1, s2, size + 1), RightOOBReadMessage(0)); free(s1); free(s2); } +TEST(AddressSanitizer, MemCmpOOBTest) { + CmpOOBTestCommon(); +} - +TEST(AddressSanitizer, BCmpOOBTest) { +#if defined(_GNU_SOURCE) || defined(__NetBSD__) + CmpOOBTestCommon(); +#endif +} Index: lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- lib/sanitizer_common/sanitizer_common_interceptors.inc +++ lib/sanitizer_common/sanitizer_common_interceptors.inc @@ -865,6 +865,49 @@ #define INIT_MEMCMP #endif +#if SANITIZER_INTERCEPT_BCMP + +INTERCEPTOR(int, bcmp, const void *a1, const void *a2, uptr size) { + if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) + return internal_memcmp(a1, a2, size); // memcmp() is an ok bcmp() impl. + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, bcmp, a1, a2, size); + if (common_flags()->intercept_memcmp) { + if (common_flags()->strict_memcmp) { + // Check the entire regions even if the first bytes of the buffers are + // different. + COMMON_INTERCEPTOR_READ_RANGE(ctx, a1, size); + COMMON_INTERCEPTOR_READ_RANGE(ctx, a2, size); + // Fallthrough to REAL(bcmp) below. + } else { + unsigned char c1 = 0, c2 = 0; + const unsigned char *s1 = (const unsigned char*)a1; + const unsigned char *s2 = (const unsigned char*)a2; + uptr i; + for (i = 0; i < size; i++) { + c1 = s1[i]; + c2 = s2[i]; + if (c1 != c2) break; + } + COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size)); + int r = CharCmpX(c1, c2); + CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), + a1, a2, size, r); + return r; + } + } + int result = REAL(bcmp(a1, a2, size)); + CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1, + a2, size, result); + return result; +} + +#define INIT_BCMP COMMON_INTERCEPT_FUNCTION(bcmp) +#else +#define INIT_BCMP +#endif + #if SANITIZER_INTERCEPT_MEMCHR INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) { if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) @@ -9473,6 +9516,7 @@ INIT_MEMCPY; INIT_MEMCHR; INIT_MEMCMP; + INIT_BCMP; INIT_MEMRCHR; INIT_MEMMEM; INIT_READ; Index: lib/sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- lib/sanitizer_common/sanitizer_platform_interceptors.h +++ lib/sanitizer_common/sanitizer_platform_interceptors.h @@ -142,6 +142,9 @@ #define SANITIZER_INTERCEPT_MEMMOVE 1 #define SANITIZER_INTERCEPT_MEMCPY 1 #define SANITIZER_INTERCEPT_MEMCMP SI_NOT_FUCHSIA +#define SANITIZER_INTERCEPT_BCMP SANITIZER_INTERCEPT_MEMCMP && ( \ + (SI_POSIX && _GNU_SOURCE) || \ + SI_NETBSD) #define SANITIZER_INTERCEPT_STRNDUP SI_POSIX #define SANITIZER_INTERCEPT___STRNDUP SI_LINUX_NOT_FREEBSD #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ Index: test/asan/TestCases/Posix/bcmp_test.cc =================================================================== --- /dev/null +++ test/asan/TestCases/Posix/bcmp_test.cc @@ -0,0 +1,17 @@ +// 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 + +// REQUIRES: compiler-rt-optimized + +#include +int main(int argc, char **argv) { + char a1[] = {static_cast(argc), 2, 3, 4}; + char a2[] = {1, static_cast(2*argc), 3, 4}; + int res = bcmp(a1, a2, 4 + argc); // BOOM + // CHECK: AddressSanitizer: stack-buffer-overflow + // CHECK: {{#0.*bcmp}} + // CHECK: {{#1.*main}} + return res; +} Index: test/sanitizer_common/TestCases/Posix/weak_hook_test.cc =================================================================== --- test/sanitizer_common/TestCases/Posix/weak_hook_test.cc +++ test/sanitizer_common/TestCases/Posix/weak_hook_test.cc @@ -6,8 +6,11 @@ // XFAIL: lsan // XFAIL: ubsan -#include #include +#include +#ifdef _GNU_SOURCE +#include // for bcmp +#endif bool seen_memcmp, seen_strncmp, seen_strncasecmp, seen_strcmp, seen_strcasecmp, seen_strstr, seen_strcasestr, seen_memmem; @@ -59,6 +62,12 @@ int_sink = memcmp(s1, s2, sizeof(s2)); assert(seen_memcmp); +#if defined(_GNU_SOURCE) || defined(__NetBSD__) + seen_memcmp = false; + int_sink = bcmp(s1, s2, sizeof(s2)); + assert(seen_memcmp); +#endif + int_sink = strncmp(s1, s2, sizeof(s2)); assert(seen_strncmp);