Index: lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- lib/tsan/rtl/tsan_interceptors.cc +++ lib/tsan/rtl/tsan_interceptors.cc @@ -576,7 +576,7 @@ void *p = 0; { SCOPED_INTERCEPTOR_RAW(malloc, size); - p = user_alloc(thr, pc, size); + p = tsan_malloc(thr, pc, size); } invoke_malloc_hook(p, size); return p; @@ -584,7 +584,7 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { SCOPED_TSAN_INTERCEPTOR(__libc_memalign, align, sz); - return user_alloc(thr, pc, sz, align); + return tsan_memalign(thr, pc, align, sz); } TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { @@ -593,7 +593,7 @@ void *p = 0; { SCOPED_INTERCEPTOR_RAW(calloc, size, n); - p = user_calloc(thr, pc, size, n); + p = tsan_calloc(thr, pc, size, n); } invoke_malloc_hook(p, n * size); return p; @@ -606,7 +606,7 @@ invoke_free_hook(p); { SCOPED_INTERCEPTOR_RAW(realloc, p, size); - p = user_realloc(thr, pc, p, size); + p = tsan_realloc(thr, pc, p, size); } invoke_malloc_hook(p, size); return p; @@ -619,7 +619,7 @@ return InternalFree(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(free, p); - user_free(thr, pc, p); + tsan_free(thr, pc, p); } TSAN_INTERCEPTOR(void, cfree, void *p) { @@ -629,12 +629,12 @@ return InternalFree(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(cfree, p); - user_free(thr, pc, p); + tsan_free(thr, pc, p); } TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p); - return user_alloc_usable_size(p); + return tsan_alloc_usable_size(p); } #endif @@ -730,7 +730,7 @@ #if SANITIZER_LINUX TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) { SCOPED_INTERCEPTOR_RAW(memalign, align, sz); - return user_alloc(thr, pc, sz, align); + return tsan_memalign(thr, pc, align, sz); } #define TSAN_MAYBE_INTERCEPT_MEMALIGN TSAN_INTERCEPT(memalign) #else @@ -739,21 +739,20 @@ #if !SANITIZER_MAC TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) { - SCOPED_INTERCEPTOR_RAW(memalign, align, sz); - return user_alloc(thr, pc, sz, align); + SCOPED_INTERCEPTOR_RAW(aligned_alloc, align, sz); + return tsan_aligned_alloc(thr, pc, align, sz); } TSAN_INTERCEPTOR(void*, valloc, uptr sz) { SCOPED_INTERCEPTOR_RAW(valloc, sz); - return user_alloc(thr, pc, sz, GetPageSizeCached()); + return tsan_valloc(thr, pc, sz); } #endif #if SANITIZER_LINUX TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) { SCOPED_INTERCEPTOR_RAW(pvalloc, sz); - sz = RoundUp(sz, GetPageSizeCached()); - return user_alloc(thr, pc, sz, GetPageSizeCached()); + return tsan_pvalloc(thr, pc, sz); } #define TSAN_MAYBE_INTERCEPT_PVALLOC TSAN_INTERCEPT(pvalloc) #else @@ -763,8 +762,7 @@ #if !SANITIZER_MAC TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) { SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz); - *memptr = user_alloc(thr, pc, sz, align); - return 0; + return tsan_posix_memalign(thr, pc, memptr, align, sz); } #endif Index: lib/tsan/rtl/tsan_malloc_mac.cc =================================================================== --- lib/tsan/rtl/tsan_malloc_mac.cc +++ lib/tsan/rtl/tsan_malloc_mac.cc @@ -26,29 +26,29 @@ #define COMMON_MALLOC_FORCE_UNLOCK() #define COMMON_MALLOC_MEMALIGN(alignment, size) \ void *p = \ - user_alloc(cur_thread(), StackTrace::GetCurrentPc(), size, alignment) + tsan_memalign(cur_thread(), StackTrace::GetCurrentPc(), alignment, size) #define COMMON_MALLOC_MALLOC(size) \ if (cur_thread()->in_symbolizer) return InternalAlloc(size); \ SCOPED_INTERCEPTOR_RAW(malloc, size); \ - void *p = user_alloc(thr, pc, size) + void *p = tsan_malloc(thr, pc, size) #define COMMON_MALLOC_REALLOC(ptr, size) \ if (cur_thread()->in_symbolizer) return InternalRealloc(ptr, size); \ SCOPED_INTERCEPTOR_RAW(realloc, ptr, size); \ - void *p = user_realloc(thr, pc, ptr, size) + void *p = tsan_realloc(thr, pc, ptr, size) #define COMMON_MALLOC_CALLOC(count, size) \ if (cur_thread()->in_symbolizer) return InternalCalloc(count, size); \ SCOPED_INTERCEPTOR_RAW(calloc, size, count); \ - void *p = user_calloc(thr, pc, size, count) + void *p = tsan_calloc(thr, pc, size, count) #define COMMON_MALLOC_VALLOC(size) \ if (cur_thread()->in_symbolizer) \ return InternalAlloc(size, nullptr, GetPageSizeCached()); \ SCOPED_INTERCEPTOR_RAW(valloc, size); \ - void *p = user_alloc(thr, pc, size, GetPageSizeCached()) + void *p = tsan_valloc(thr, pc, size) #define COMMON_MALLOC_FREE(ptr) \ if (cur_thread()->in_symbolizer) return InternalFree(ptr); \ SCOPED_INTERCEPTOR_RAW(free, ptr); \ - user_free(thr, pc, ptr) -#define COMMON_MALLOC_SIZE(ptr) uptr size = user_alloc_usable_size(ptr); + tsan_free(thr, pc, ptr) +#define COMMON_MALLOC_SIZE(ptr) uptr size = tsan_alloc_usable_size(ptr); #define COMMON_MALLOC_FILL_STATS(zone, stats) #define COMMON_MALLOC_REPORT_UNKNOWN_REALLOC(ptr, zone_ptr, zone_name) \ (void)zone_name; \ Index: lib/tsan/rtl/tsan_mman.h =================================================================== --- lib/tsan/rtl/tsan_mman.h +++ lib/tsan/rtl/tsan_mman.h @@ -29,12 +29,22 @@ // For user allocations. void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align = kDefaultAlignment, bool signal = true); -void *user_calloc(ThreadState *thr, uptr pc, uptr sz, uptr n); // Does not accept NULL. void user_free(ThreadState *thr, uptr pc, void *p, bool signal = true); -void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz); -void *user_alloc_aligned(ThreadState *thr, uptr pc, uptr sz, uptr align); -uptr user_alloc_usable_size(const void *p); +// Interceptor implementations. +void *tsan_malloc(ThreadState *thr, uptr pc, uptr sz); +inline void tsan_free(ThreadState *thr, uptr pc, void *p) { + user_free(thr, pc, p); +} +void *tsan_calloc(ThreadState *thr, uptr pc, uptr sz, uptr n); +void *tsan_realloc(ThreadState *thr, uptr pc, void *p, uptr sz); +void *tsan_memalign(ThreadState *thr, uptr pc, uptr align, uptr sz); +int tsan_posix_memalign(ThreadState *thr, uptr pc, void **memptr, uptr align, + uptr sz); +void *tsan_aligned_alloc(ThreadState *thr, uptr pc, uptr align, uptr sz); +void *tsan_valloc(ThreadState *thr, uptr pc, uptr sz); +void *tsan_pvalloc(ThreadState *thr, uptr pc, uptr sz); +uptr tsan_alloc_usable_size(const void *p); // Invoking malloc/free hooks that may be installed by the user. void invoke_malloc_hook(void *ptr, uptr size); Index: lib/tsan/rtl/tsan_mman.cc =================================================================== --- lib/tsan/rtl/tsan_mman.cc +++ lib/tsan/rtl/tsan_mman.cc @@ -153,7 +153,7 @@ if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) return Allocator::FailureHandler::OnBadRequest(); void *p = allocator()->Allocate(&thr->proc()->alloc_cache, sz, align); - if (p == 0) + if (UNLIKELY(p == 0)) return 0; if (ctx && ctx->initialized) OnUserAlloc(thr, pc, (uptr)p, sz, true); @@ -162,15 +162,6 @@ return p; } -void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) { - if (CheckForCallocOverflow(size, n)) - return Allocator::FailureHandler::OnBadRequest(); - void *p = user_alloc(thr, pc, n * size); - if (p) - internal_memset(p, 0, n * size); - return p; -} - void user_free(ThreadState *thr, uptr pc, void *p, bool signal) { ScopedGlobalProcessor sgp; if (ctx && ctx->initialized) @@ -180,6 +171,19 @@ SignalUnsafeCall(thr, pc); } +void *tsan_malloc(ThreadState *thr, uptr pc, uptr sz) { + return SetErrnoOnNull(user_alloc(thr, pc, sz, kDefaultAlignment)); +} + +void *tsan_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) { + if (UNLIKELY(CheckForCallocOverflow(size, n))) + return SetErrnoOnNull(Allocator::FailureHandler::OnBadRequest()); + void *p = user_alloc(thr, pc, n * size); + if (p) + internal_memset(p, 0, n * size); + return SetErrnoOnNull(p); +} + void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) { DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p); ctx->metamap.AllocBlock(thr, pc, p, sz); @@ -197,21 +201,66 @@ MemoryRangeFreed(thr, pc, (uptr)p, sz); } -void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) { +void *tsan_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) { // FIXME: Handle "shrinking" more efficiently, // it seems that some software actually does this. - void *p2 = user_alloc(thr, pc, sz); - if (p2 == 0) - return 0; - if (p) { - uptr oldsz = user_alloc_usable_size(p); - internal_memcpy(p2, p, min(oldsz, sz)); + if (!p) + return SetErrnoOnNull(user_alloc(thr, pc, sz)); + if (!sz) { user_free(thr, pc, p); + return nullptr; } - return p2; + void *new_p = user_alloc(thr, pc, sz); + if (new_p) { + uptr old_sz = tsan_alloc_usable_size(p); + internal_memcpy(new_p, p, min(old_sz, sz)); + user_free(thr, pc, p); + } + return SetErrnoOnNull(new_p); +} + +void *tsan_memalign(ThreadState *thr, uptr pc, uptr align, uptr sz) { + if (UNLIKELY(!IsPowerOfTwo(align))) { + errno = errno_EINVAL; + return Allocator::FailureHandler::OnBadRequest(); + } + return SetErrnoOnNull(user_alloc(thr, pc, sz, align)); +} + +int tsan_posix_memalign(ThreadState *thr, uptr pc, void **memptr, uptr align, + uptr sz) { + if (UNLIKELY(!CheckPosixMemalignAlignment(align))) { + Allocator::FailureHandler::OnBadRequest(); + return errno_EINVAL; + } + void *ptr = user_alloc(thr, pc, sz, align); + if (UNLIKELY(!ptr)) + return errno_ENOMEM; + CHECK(IsAligned((uptr)ptr, align)); + *memptr = ptr; + return 0; +} + +void *tsan_aligned_alloc(ThreadState *thr, uptr pc, uptr align, uptr sz) { + if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(align, sz))) { + errno = errno_EINVAL; + return Allocator::FailureHandler::OnBadRequest(); + } + return SetErrnoOnNull(user_alloc(thr, pc, sz, align)); +} + +void *tsan_valloc(ThreadState *thr, uptr pc, uptr sz) { + return SetErrnoOnNull(user_alloc(thr, pc, sz, GetPageSizeCached())); +} + +void *tsan_pvalloc(ThreadState *thr, uptr pc, uptr sz) { + uptr PageSize = GetPageSizeCached(); + // pvalloc(0) should allocate one page. + sz = sz ? RoundUpTo(sz, PageSize) : PageSize; + return SetErrnoOnNull(user_alloc(thr, pc, sz, PageSize)); } -uptr user_alloc_usable_size(const void *p) { +uptr tsan_alloc_usable_size(const void *p) { if (p == 0) return 0; MBlock *b = ctx->metamap.GetBlock((uptr)p); @@ -290,7 +339,7 @@ } uptr __sanitizer_get_allocated_size(const void *p) { - return user_alloc_usable_size(p); + return tsan_alloc_usable_size(p); } void __tsan_on_thread_idle() { Index: lib/tsan/rtl/tsan_new_delete.cc =================================================================== --- lib/tsan/rtl/tsan_new_delete.cc +++ lib/tsan/rtl/tsan_new_delete.cc @@ -32,7 +32,7 @@ void *p = 0; \ { \ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \ - p = user_alloc(thr, pc, size); \ + p = tsan_malloc(thr, pc, size); \ if (!nothrow && UNLIKELY(!p)) DieOnFailure::OnOOM(); \ } \ invoke_malloc_hook(p, size); \ @@ -68,7 +68,7 @@ return InternalFree(ptr); \ invoke_free_hook(ptr); \ SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ - user_free(thr, pc, ptr); + tsan_free(thr, pc, ptr); SANITIZER_INTERFACE_ATTRIBUTE void operator delete(void *ptr) NOEXCEPT; Index: lib/tsan/tests/unit/tsan_mman_test.cc =================================================================== --- lib/tsan/tests/unit/tsan_mman_test.cc +++ lib/tsan/tests/unit/tsan_mman_test.cc @@ -37,73 +37,74 @@ TEST(Mman, User) { ThreadState *thr = cur_thread(); uptr pc = 0; - char *p = (char*)user_alloc(thr, pc, 10); + char *p = (char*)tsan_malloc(thr, pc, 10); EXPECT_NE(p, (char*)0); - char *p2 = (char*)user_alloc(thr, pc, 20); + char *p2 = (char*)tsan_malloc(thr, pc, 20); EXPECT_NE(p2, (char*)0); EXPECT_NE(p2, p); - EXPECT_EQ(10U, user_alloc_usable_size(p)); - EXPECT_EQ(20U, user_alloc_usable_size(p2)); - user_free(thr, pc, p); - user_free(thr, pc, p2); + EXPECT_EQ(10U, tsan_alloc_usable_size(p)); + EXPECT_EQ(20U, tsan_alloc_usable_size(p2)); + tsan_free(thr, pc, p); + tsan_free(thr, pc, p2); } TEST(Mman, UserRealloc) { ThreadState *thr = cur_thread(); uptr pc = 0; { - void *p = user_realloc(thr, pc, 0, 0); + void *p = tsan_realloc(thr, pc, 0, 0); // Realloc(NULL, N) is equivalent to malloc(N), thus must return // non-NULL pointer. EXPECT_NE(p, (void*)0); } { - void *p = user_realloc(thr, pc, 0, 100); + void *p = tsan_realloc(thr, pc, 0, 100); EXPECT_NE(p, (void*)0); memset(p, 0xde, 100); - user_free(thr, pc, p); + tsan_free(thr, pc, p); } { - void *p = user_alloc(thr, pc, 100); + void *p = tsan_malloc(thr, pc, 100); EXPECT_NE(p, (void*)0); memset(p, 0xde, 100); - void *p2 = user_realloc(thr, pc, p, 0); - EXPECT_NE(p2, (void*)0); + // Realloc(P, 0) is equivalent to free(N) and returns NULL. + void *p2 = tsan_realloc(thr, pc, p, 0); + EXPECT_EQ(p2, (void*)0); } { - void *p = user_realloc(thr, pc, 0, 100); + void *p = tsan_realloc(thr, pc, 0, 100); EXPECT_NE(p, (void*)0); memset(p, 0xde, 100); - void *p2 = user_realloc(thr, pc, p, 10000); + void *p2 = tsan_realloc(thr, pc, p, 10000); EXPECT_NE(p2, (void*)0); for (int i = 0; i < 100; i++) EXPECT_EQ(((char*)p2)[i], (char)0xde); memset(p2, 0xde, 10000); - user_free(thr, pc, p2); + tsan_free(thr, pc, p2); } { - void *p = user_realloc(thr, pc, 0, 10000); + void *p = tsan_realloc(thr, pc, 0, 10000); EXPECT_NE(p, (void*)0); memset(p, 0xde, 10000); - void *p2 = user_realloc(thr, pc, p, 10); + void *p2 = tsan_realloc(thr, pc, p, 10); EXPECT_NE(p2, (void*)0); for (int i = 0; i < 10; i++) EXPECT_EQ(((char*)p2)[i], (char)0xde); - user_free(thr, pc, p2); + tsan_free(thr, pc, p2); } } TEST(Mman, UsableSize) { ThreadState *thr = cur_thread(); uptr pc = 0; - char *p = (char*)user_alloc(thr, pc, 10); - char *p2 = (char*)user_alloc(thr, pc, 20); - EXPECT_EQ(0U, user_alloc_usable_size(NULL)); - EXPECT_EQ(10U, user_alloc_usable_size(p)); - EXPECT_EQ(20U, user_alloc_usable_size(p2)); - user_free(thr, pc, p); - user_free(thr, pc, p2); - EXPECT_EQ(0U, user_alloc_usable_size((void*)0x4123)); + char *p = (char*)tsan_malloc(thr, pc, 10); + char *p2 = (char*)tsan_malloc(thr, pc, 20); + EXPECT_EQ(0U, tsan_alloc_usable_size(NULL)); + EXPECT_EQ(10U, tsan_alloc_usable_size(p)); + EXPECT_EQ(20U, tsan_alloc_usable_size(p2)); + tsan_free(thr, pc, p); + tsan_free(thr, pc, p2); + EXPECT_EQ(0U, tsan_alloc_usable_size((void*)0x4123)); } TEST(Mman, Stats) { @@ -118,7 +119,7 @@ EXPECT_EQ(20U, __sanitizer_get_estimated_allocated_size(20)); EXPECT_EQ(100U, __sanitizer_get_estimated_allocated_size(100)); - char *p = (char*)user_alloc(thr, 0, 10); + char *p = (char*)tsan_malloc(thr, 0, 10); EXPECT_TRUE(__sanitizer_get_ownership(p)); EXPECT_EQ(10U, __sanitizer_get_allocated_size(p)); @@ -127,7 +128,7 @@ EXPECT_EQ(free0, __sanitizer_get_free_bytes()); EXPECT_EQ(unmapped0, __sanitizer_get_unmapped_bytes()); - user_free(thr, 0, p); + tsan_free(thr, 0, p); EXPECT_EQ(alloc0, __sanitizer_get_current_allocated_bytes()); EXPECT_GE(__sanitizer_get_heap_size(), heap0); @@ -135,21 +136,90 @@ EXPECT_EQ(unmapped0, __sanitizer_get_unmapped_bytes()); } +TEST(Mman, Valloc) { + ThreadState *thr = cur_thread(); + + void *p = tsan_valloc(thr, 0, 100); + EXPECT_NE(p, (void*)0); + tsan_free(thr, 0, p); + + p = tsan_pvalloc(thr, 0, 100); + EXPECT_NE(p, (void*)0); + tsan_free(thr, 0, p); + + p = tsan_pvalloc(thr, 0, 0); + EXPECT_NE(p, (void*)0); + EXPECT_EQ(GetPageSizeCached(), __sanitizer_get_allocated_size(p)); + tsan_free(thr, 0, p); +} + +#if !SANITIZER_DEBUG +// EXPECT_DEATH clones a thread with 4K stack, +// which is overflown by tsan memory accesses functions in debug mode. + TEST(Mman, CallocOverflow) { -#if SANITIZER_DEBUG - // EXPECT_DEATH clones a thread with 4K stack, - // which is overflown by tsan memory accesses functions in debug mode. - return; -#endif ThreadState *thr = cur_thread(); uptr pc = 0; size_t kArraySize = 4096; volatile size_t kMaxSizeT = std::numeric_limits::max(); volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; volatile void *p = NULL; - EXPECT_DEATH(p = user_calloc(thr, pc, kArraySize, kArraySize2), + EXPECT_DEATH(p = tsan_calloc(thr, pc, kArraySize, kArraySize2), + "allocator is terminating the process instead of returning 0"); + EXPECT_EQ(0L, p); +} + +TEST(Mman, Memalign) { + ThreadState *thr = cur_thread(); + + void *p = tsan_memalign(thr, 0, 8, 100); + EXPECT_NE(p, (void*)0); + tsan_free(thr, 0, p); + + p = NULL; + EXPECT_DEATH(p = tsan_memalign(thr, 0, 7, 100), + "allocator is terminating the process instead of returning 0"); + EXPECT_EQ(0L, p); +} + +TEST(Mman, PosixMemalign) { + ThreadState *thr = cur_thread(); + + void *p = NULL; + int res = tsan_posix_memalign(thr, 0, &p, 8, 100); + EXPECT_NE(p, (void*)0); + EXPECT_EQ(res, 0); + tsan_free(thr, 0, p); + + p = NULL; + // Alignment is not a power of two, although is a multiple of sizeof(void*). + EXPECT_DEATH(res = tsan_posix_memalign(thr, 0, &p, 3 * sizeof(p), 100), + "allocator is terminating the process instead of returning 0"); + EXPECT_EQ(0L, p); + // Alignment is not a multiple of sizeof(void*), although is a power of 2. + EXPECT_DEATH(res = tsan_posix_memalign(thr, 0, &p, 2, 100), + "allocator is terminating the process instead of returning 0"); + EXPECT_EQ(0L, p); +} + +TEST(Mman, AlignedAlloc) { + ThreadState *thr = cur_thread(); + + void *p = tsan_aligned_alloc(thr, 0, 8, 64); + EXPECT_NE(p, (void*)0); + tsan_free(thr, 0, p); + + p = NULL; + // Alignement is not a power of 2. + EXPECT_DEATH(p = tsan_aligned_alloc(thr, 0, 7, 100), + "allocator is terminating the process instead of returning 0"); + EXPECT_EQ(0L, p); + // Size is not a multiple of alignment. + EXPECT_DEATH(p = tsan_aligned_alloc(thr, 0, 8, 100), "allocator is terminating the process instead of returning 0"); EXPECT_EQ(0L, p); } +#endif + } // namespace __tsan Index: test/tsan/allocator_returns_null.cc =================================================================== --- test/tsan/allocator_returns_null.cc +++ test/tsan/allocator_returns_null.cc @@ -36,10 +36,14 @@ // RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 \ // RUN: | FileCheck %s --check-prefix=CHECK-nnNULL +// Until we figure out why errno is not properly set on windows +// UNSUPPORTED: win32 + #include -#include +#include #include #include +#include #include #include @@ -51,6 +55,7 @@ const char *action = argv[1]; fprintf(stderr, "%s:\n", action); + // The limit enforced in tsan_mman.cc, user_alloc function. static const size_t kMaxAllowedMallocSizePlusOne = (1ULL << 40) + 1; void *x = 0; @@ -78,10 +83,13 @@ assert(0); } + fprintf(stderr, "errno: %d\n", errno); + // The NULL pointer is printed differently on different systems, while (long)0 // is always the same. fprintf(stderr, "x: %lx\n", (long)x); free(x); + return x != 0; } @@ -101,14 +109,19 @@ // CHECK-nnCRASH: ThreadSanitizer's allocator is terminating the process // CHECK-mNULL: malloc: +// CHECK-mNULL: errno: 12 // CHECK-mNULL: x: 0 // CHECK-cNULL: calloc: +// CHECK-cNULL: errno: 12 // CHECK-cNULL: x: 0 // CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: errno: 12 // CHECK-coNULL: x: 0 // CHECK-rNULL: realloc: +// CHECK-rNULL: errno: 12 // CHECK-rNULL: x: 0 // CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: errno: 12 // CHECK-mrNULL: x: 0 // CHECK-nnNULL: new-nothrow: // CHECK-nnNULL: x: 0