From ecd9ac71bcc8862b1e3c8037978a4fad10aebe0f Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Sat, 21 Feb 2026 14:47:17 -0500 Subject: [PATCH 1/2] Strip ARM64 TBI tag byte from addresses before pread on /proc//mem Android's scudo heap allocator uses ARM64 Top-Byte Ignore (TBI) to tag heap pointers with a non-zero top byte (e.g., 0xB4). While the CPU ignores this byte during memory access, pread on /proc//mem treats the offset as a file position where TBI does not apply, causing EINVAL. Strip the top byte before pread in PAL_ReadProcessMemory and createdump's ReadProcessMemory. This is a no-op on non-Android ARM64 Linux today, but guards against future TBI/MTE adoption on other distributions. See https://www.kernel.org/doc/html/latest/arch/arm64/tagged-address-abi.html --- src/coreclr/debug/createdump/crashinfounix.cpp | 11 +++++++++++ src/coreclr/pal/src/debug/debug.cpp | 9 +++++++++ 2 files changed, 20 insertions(+) diff --git a/src/coreclr/debug/createdump/crashinfounix.cpp b/src/coreclr/debug/createdump/crashinfounix.cpp index e4ac6bcd55905e..32ca785e8b6bcc 100644 --- a/src/coreclr/debug/createdump/crashinfounix.cpp +++ b/src/coreclr/debug/createdump/crashinfounix.cpp @@ -531,6 +531,17 @@ CrashInfo::ReadProcessMemory(uint64_t address, void* buffer, size_t size, size_t // performance optimization. m_canUseProcVmReadSyscall = false; assert(m_fdMem != -1); +#ifdef TARGET_ARM64 + // Android's heap allocator (scudo) uses ARM64 Top-Byte Ignore (TBI) for memory tagging. + // pread on /proc//mem treats the offset as a file position, not a virtual address, + // so the kernel does not apply TBI — tagged pointers cause EINVAL. + // See https://www.kernel.org/doc/html/latest/arch/arm64/tagged-address-abi.html + // + // Currently only Android allocators set a non-zero top byte, so on other ARM64 Linux + // configurations this is a no-op. However, any future use of TBI tagging (e.g., ARM MTE) + // on other Linux distros would hit the same issue. + address &= 0x00FFFFFFFFFFFFFFULL; +#endif *read = pread(m_fdMem, buffer, size, (off_t)address); } diff --git a/src/coreclr/pal/src/debug/debug.cpp b/src/coreclr/pal/src/debug/debug.cpp index 27f01acd64842b..e3452c2c97cae6 100644 --- a/src/coreclr/pal/src/debug/debug.cpp +++ b/src/coreclr/pal/src/debug/debug.cpp @@ -723,6 +723,15 @@ PAL_ReadProcessMemory( free(data); } #else + // Android's heap allocator (scudo) uses ARM64 Top-Byte Ignore (TBI) for memory tagging. + // pread on /proc//mem treats the offset as a file position, not a virtual address, + // so the kernel does not apply TBI — tagged pointers cause EINVAL. + // See https://www.kernel.org/doc/html/latest/arch/arm64/tagged-address-abi.html + // + // Currently only Android allocators set a non-zero top byte, so on other ARM64 Linux + // configurations this is a no-op. However, any future use of TBI tagging (e.g., ARM MTE) + // on other Linux distros would hit the same issue, so the strip is applied unconditionally. + address &= 0x00FFFFFFFFFFFFFFULL; read = pread(handle, buffer, size, address); if (read == (size_t)-1) { From aa385df8468884f445db968276c1fc00b85899fa Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Sat, 21 Feb 2026 16:42:15 -0500 Subject: [PATCH 2/2] TARGET_ARM64 guard in debug.cpp --- src/coreclr/pal/src/debug/debug.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/coreclr/pal/src/debug/debug.cpp b/src/coreclr/pal/src/debug/debug.cpp index e3452c2c97cae6..f52709c3f0ae06 100644 --- a/src/coreclr/pal/src/debug/debug.cpp +++ b/src/coreclr/pal/src/debug/debug.cpp @@ -730,8 +730,10 @@ PAL_ReadProcessMemory( // // Currently only Android allocators set a non-zero top byte, so on other ARM64 Linux // configurations this is a no-op. However, any future use of TBI tagging (e.g., ARM MTE) - // on other Linux distros would hit the same issue, so the strip is applied unconditionally. + // on other Linux distros would hit the same issue. +#ifdef TARGET_ARM64 address &= 0x00FFFFFFFFFFFFFFULL; +#endif read = pread(handle, buffer, size, address); if (read == (size_t)-1) {