Log errno (or fuchsia equivalent) on map failures (#95391) A feature requested by Android, we should log errno (or the corresponding fuchsia error status) as part of the message when mmap/mprotect/etc. fails. GitOrigin-RevId: 0a94511aec7a41194c0e61d88801312542ff70ce
diff --git a/definitions.h b/definitions.h index bec0290..c6785d4 100644 --- a/definitions.h +++ b/definitions.h
@@ -12,7 +12,8 @@ #define GWP_ASAN_TLS_INITIAL_EXEC \ __thread __attribute__((tls_model("initial-exec"))) -#define GWP_ASAN_UNLIKELY(X) __builtin_expect(!!(X), 0) +#define GWP_ASAN_LIKELY(EXPR) __builtin_expect((bool)(EXPR), true) +#define GWP_ASAN_UNLIKELY(EXPR) __builtin_expect((bool)(EXPR), false) #define GWP_ASAN_ALWAYS_INLINE inline __attribute__((always_inline)) #define GWP_ASAN_WEAK __attribute__((weak))
diff --git a/platform_specific/guarded_pool_allocator_fuchsia.cpp b/platform_specific/guarded_pool_allocator_fuchsia.cpp index 3f39402..5d5c729 100644 --- a/platform_specific/guarded_pool_allocator_fuchsia.cpp +++ b/platform_specific/guarded_pool_allocator_fuchsia.cpp
@@ -24,13 +24,13 @@ assert((Size % State.PageSize) == 0); zx_handle_t Vmo; zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo); - check(Status == ZX_OK, "Failed to create Vmo"); + checkWithErrorCode(Status == ZX_OK, "Failed to create Vmo", Status); _zx_object_set_property(Vmo, ZX_PROP_NAME, Name, strlen(Name)); zx_vaddr_t Addr; Status = _zx_vmar_map(_zx_vmar_root_self(), ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_ALLOW_FAULTS, 0, Vmo, 0, Size, &Addr); - check(Status == ZX_OK, "Vmo mapping failed"); + checkWithErrorCode(Status == ZX_OK, "Vmo mapping failed", Status); _zx_handle_close(Vmo); return reinterpret_cast<void *>(Addr); } @@ -40,7 +40,7 @@ assert((Size % State.PageSize) == 0); zx_status_t Status = _zx_vmar_unmap(_zx_vmar_root_self(), reinterpret_cast<zx_vaddr_t>(Ptr), Size); - check(Status == ZX_OK, "Vmo unmapping failed"); + checkWithErrorCode(Status == ZX_OK, "Vmo unmapping failed", Status); } void *GuardedPoolAllocator::reserveGuardedPool(size_t Size) { @@ -50,7 +50,8 @@ _zx_vmar_root_self(), ZX_VM_CAN_MAP_READ | ZX_VM_CAN_MAP_WRITE | ZX_VM_CAN_MAP_SPECIFIC, 0, Size, &GuardedPagePoolPlatformData.Vmar, &Addr); - check(Status == ZX_OK, "Failed to reserve guarded pool allocator memory"); + checkWithErrorCode(Status == ZX_OK, + "Failed to reserve guarded pool allocator memory", Status); _zx_object_set_property(GuardedPagePoolPlatformData.Vmar, ZX_PROP_NAME, kGwpAsanGuardPageName, strlen(kGwpAsanGuardPageName)); return reinterpret_cast<void *>(Addr); @@ -59,8 +60,10 @@ void GuardedPoolAllocator::unreserveGuardedPool() { const zx_handle_t Vmar = GuardedPagePoolPlatformData.Vmar; assert(Vmar != ZX_HANDLE_INVALID && Vmar != _zx_vmar_root_self()); - check(_zx_vmar_destroy(Vmar) == ZX_OK, "Failed to destroy a vmar"); - check(_zx_handle_close(Vmar) == ZX_OK, "Failed to close a vmar"); + zx_status_t Status = _zx_vmar_destroy(Vmar); + checkWithErrorCode(Status == ZX_OK, "Failed to destroy a vmar", Status); + Status = _zx_handle_close(Vmar); + checkWithErrorCode(Status == ZX_OK, "Failed to close a vmar", Status); GuardedPagePoolPlatformData.Vmar = ZX_HANDLE_INVALID; } @@ -69,7 +72,7 @@ assert((Size % State.PageSize) == 0); zx_handle_t Vmo; zx_status_t Status = _zx_vmo_create(Size, 0, &Vmo); - check(Status == ZX_OK, "Failed to create vmo"); + checkWithErrorCode(Status == ZX_OK, "Failed to create vmo", Status); _zx_object_set_property(Vmo, ZX_PROP_NAME, kGwpAsanAliveSlotName, strlen(kGwpAsanAliveSlotName)); const zx_handle_t Vmar = GuardedPagePoolPlatformData.Vmar; @@ -81,7 +84,7 @@ ZX_VM_PERM_READ | ZX_VM_PERM_WRITE | ZX_VM_ALLOW_FAULTS | ZX_VM_SPECIFIC, Offset, Vmo, 0, Size, &P); - check(Status == ZX_OK, "Vmo mapping failed"); + checkWithErrorCode(Status == ZX_OK, "Vmo mapping failed", Status); _zx_handle_close(Vmo); } @@ -93,7 +96,7 @@ assert(Vmar != ZX_HANDLE_INVALID && Vmar != _zx_vmar_root_self()); const zx_status_t Status = _zx_vmar_unmap(Vmar, reinterpret_cast<zx_vaddr_t>(Ptr), Size); - check(Status == ZX_OK, "Vmar unmapping failed"); + checkWithErrorCode(Status == ZX_OK, "Vmar unmapping failed", Status); } size_t GuardedPoolAllocator::getPlatformPageSize() {
diff --git a/platform_specific/guarded_pool_allocator_posix.cpp b/platform_specific/guarded_pool_allocator_posix.cpp index 549e31a..7b2e199 100644 --- a/platform_specific/guarded_pool_allocator_posix.cpp +++ b/platform_specific/guarded_pool_allocator_posix.cpp
@@ -12,6 +12,7 @@ #include "gwp_asan/utilities.h" #include <assert.h> +#include <errno.h> #include <pthread.h> #include <stdint.h> #include <stdlib.h> @@ -46,7 +47,8 @@ assert((Size % State.PageSize) == 0); void *Ptr = mmap(nullptr, Size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - check(Ptr != MAP_FAILED, "Failed to map guarded pool allocator memory"); + checkWithErrorCode(Ptr != MAP_FAILED, + "Failed to map guarded pool allocator memory", errno); MaybeSetMappingName(Ptr, Size, Name); return Ptr; } @@ -54,15 +56,16 @@ void GuardedPoolAllocator::unmap(void *Ptr, size_t Size) const { assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0); assert((Size % State.PageSize) == 0); - check(munmap(Ptr, Size) == 0, - "Failed to unmap guarded pool allocator memory."); + checkWithErrorCode(munmap(Ptr, Size) == 0, + "Failed to unmap guarded pool allocator memory.", errno); } void *GuardedPoolAllocator::reserveGuardedPool(size_t Size) { assert((Size % State.PageSize) == 0); void *Ptr = mmap(nullptr, Size, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - check(Ptr != MAP_FAILED, "Failed to reserve guarded pool allocator memory"); + checkWithErrorCode(Ptr != MAP_FAILED, + "Failed to reserve guarded pool allocator memory", errno); MaybeSetMappingName(Ptr, Size, kGwpAsanGuardPageName); return Ptr; } @@ -75,8 +78,9 @@ void GuardedPoolAllocator::allocateInGuardedPool(void *Ptr, size_t Size) const { assert((reinterpret_cast<uintptr_t>(Ptr) % State.PageSize) == 0); assert((Size % State.PageSize) == 0); - check(mprotect(Ptr, Size, PROT_READ | PROT_WRITE) == 0, - "Failed to allocate in guarded pool allocator memory"); + checkWithErrorCode(mprotect(Ptr, Size, PROT_READ | PROT_WRITE) == 0, + "Failed to allocate in guarded pool allocator memory", + errno); MaybeSetMappingName(Ptr, Size, kGwpAsanAliveSlotName); } @@ -87,9 +91,10 @@ // mmap() a PROT_NONE page over the address to release it to the system, if // we used mprotect() here the system would count pages in the quarantine // against the RSS. - check(mmap(Ptr, Size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, - 0) != MAP_FAILED, - "Failed to deallocate in guarded pool allocator memory"); + checkWithErrorCode( + mmap(Ptr, Size, PROT_NONE, MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, + 0) != MAP_FAILED, + "Failed to deallocate in guarded pool allocator memory", errno); MaybeSetMappingName(Ptr, Size, kGwpAsanGuardPageName); }
diff --git a/platform_specific/utilities_fuchsia.cpp b/platform_specific/utilities_fuchsia.cpp index bc9d3a4..fecf94b 100644 --- a/platform_specific/utilities_fuchsia.cpp +++ b/platform_specific/utilities_fuchsia.cpp
@@ -8,12 +8,25 @@ #include "gwp_asan/utilities.h" +#include <alloca.h> +#include <stdio.h> #include <string.h> #include <zircon/sanitizer.h> +#include <zircon/status.h> namespace gwp_asan { void die(const char *Message) { __sanitizer_log_write(Message, strlen(Message)); __builtin_trap(); } + +void dieWithErrorCode(const char *Message, int64_t ErrorCode) { + const char *error_str = + _zx_status_get_string(static_cast<zx_status_t>(ErrorCode)); + size_t buffer_size = strlen(Message) + 32 + strlen(error_str); + char *buffer = static_cast<char *>(alloca(buffer_size)); + snprintf(buffer, buffer_size, "%s (Error Code: %s)", Message, error_str); + __sanitizer_log_write(buffer, strlen(buffer)); + __builtin_trap(); +} } // namespace gwp_asan
diff --git a/platform_specific/utilities_posix.cpp b/platform_specific/utilities_posix.cpp index 7357963..7501980 100644 --- a/platform_specific/utilities_posix.cpp +++ b/platform_specific/utilities_posix.cpp
@@ -6,7 +6,11 @@ // //===----------------------------------------------------------------------===// +#include <alloca.h> #include <features.h> // IWYU pragma: keep (for __BIONIC__ macro) +#include <inttypes.h> +#include <stdint.h> +#include <string.h> #ifdef __BIONIC__ #include "gwp_asan/definitions.h" @@ -27,4 +31,21 @@ __builtin_trap(); #endif // __BIONIC__ } + +void dieWithErrorCode(const char *Message, int64_t ErrorCode) { +#ifdef __BIONIC__ + if (&android_set_abort_message == nullptr) + abort(); + + size_t buffer_size = strlen(Message) + 48; + char *buffer = static_cast<char *>(alloca(buffer_size)); + snprintf(buffer, buffer_size, "%s (Error Code: %" PRId64 ")", Message, + ErrorCode); + android_set_abort_message(buffer); + abort(); +#else // __BIONIC__ + fprintf(stderr, "%s (Error Code: %" PRId64 ")", Message, ErrorCode); + __builtin_trap(); +#endif // __BIONIC__ +} } // namespace gwp_asan
diff --git a/tests/utilities.cpp b/tests/utilities.cpp new file mode 100644 index 0000000..09a54e5 --- /dev/null +++ b/tests/utilities.cpp
@@ -0,0 +1,24 @@ +//===-- utilities.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "gwp_asan/utilities.h" +#include "gwp_asan/tests/harness.h" + +using gwp_asan::check; +using gwp_asan::checkWithErrorCode; + +TEST(UtilitiesDeathTest, CheckPrintsAsExpected) { + EXPECT_DEATH({ check(false, "Hello world"); }, "Hello world"); + check(true, "Should not crash"); + EXPECT_DEATH( + { checkWithErrorCode(false, "Hello world", 1337); }, + "Hello world \\(Error Code: 1337\\)"); + EXPECT_DEATH( + { checkWithErrorCode(false, "Hello world", -1337); }, + "Hello world \\(Error Code: -1337\\)"); +}
diff --git a/utilities.h b/utilities.h index 76e5df2..02f450a 100644 --- a/utilities.h +++ b/utilities.h
@@ -12,17 +12,28 @@ #include "gwp_asan/definitions.h" #include <stddef.h> +#include <stdint.h> namespace gwp_asan { // Terminates in a platform-specific way with `Message`. void die(const char *Message); +void dieWithErrorCode(const char *Message, int64_t ErrorCode); // Checks that `Condition` is true, otherwise dies with `Message`. GWP_ASAN_ALWAYS_INLINE void check(bool Condition, const char *Message) { - if (Condition) + if (GWP_ASAN_LIKELY(Condition)) return; die(Message); } + +// Checks that `Condition` is true, otherwise dies with `Message` (including +// errno at the end). +GWP_ASAN_ALWAYS_INLINE void +checkWithErrorCode(bool Condition, const char *Message, int64_t ErrorCode) { + if (GWP_ASAN_LIKELY(Condition)) + return; + dieWithErrorCode(Message, ErrorCode); +} } // namespace gwp_asan #endif // GWP_ASAN_UTILITIES_H_