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_