[HWASAN] Improve tag mismatch diagnostics

Reports correct size and tags when either size is not power of two
or offset to bad granule is not zero.

Differential revision: https://reviews.llvm.org/D56603

llvm-svn: 351730
This commit is contained in:
Eugene Leviant 2019-01-21 09:51:10 +00:00
parent f608dc1f57
commit 0d7952ce78
4 changed files with 43 additions and 13 deletions

View File

@ -336,14 +336,14 @@ sptr __hwasan_test_shadow(const void *p, uptr sz) {
if (sz == 0) if (sz == 0)
return -1; return -1;
tag_t ptr_tag = GetTagFromPointer((uptr)p); tag_t ptr_tag = GetTagFromPointer((uptr)p);
if (ptr_tag == 0)
return -1;
uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p)); uptr ptr_raw = UntagAddr(reinterpret_cast<uptr>(p));
uptr shadow_first = MemToShadow(ptr_raw); uptr shadow_first = MemToShadow(ptr_raw);
uptr shadow_last = MemToShadow(ptr_raw + sz - 1); uptr shadow_last = MemToShadow(ptr_raw + sz - 1);
for (uptr s = shadow_first; s <= shadow_last; ++s) for (uptr s = shadow_first; s <= shadow_last; ++s)
if (*(tag_t*)s != ptr_tag) if (*(tag_t *)s != ptr_tag) {
return ShadowToMem(s) - ptr_raw; sptr offset = ShadowToMem(s) - ptr_raw;
return offset < 0 ? 0 : offset;
}
return -1; return -1;
} }

View File

@ -14,6 +14,7 @@
#define HWASAN_CHECKS_H #define HWASAN_CHECKS_H
#include "hwasan_mapping.h" #include "hwasan_mapping.h"
#include "sanitizer_common/sanitizer_common.h"
namespace __hwasan { namespace __hwasan {
template <unsigned X> template <unsigned X>
@ -22,8 +23,8 @@ __attribute__((always_inline)) static void SigTrap(uptr p) {
(void)p; (void)p;
// 0x900 is added to do not interfere with the kernel use of lower values of // 0x900 is added to do not interfere with the kernel use of lower values of
// brk immediate. // brk immediate.
// FIXME: Add a constraint to put the pointer into x0, the same as x86 branch. register uptr x0 asm("x0") = p;
asm("brk %0\n\t" ::"n"(0x900 + X)); asm("brk %1\n\t" ::"r"(x0), "n"(0x900 + X));
#elif defined(__x86_64__) #elif defined(__x86_64__)
// INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes // INT3 + NOP DWORD ptr [EAX + X] to pass X to our signal handler, 5 bytes
// total. The pointer is passed via rdi. // total. The pointer is passed via rdi.
@ -41,6 +42,25 @@ __attribute__((always_inline)) static void SigTrap(uptr p) {
// __builtin_unreachable(); // __builtin_unreachable();
} }
// Version with access size which is not power of 2
template <unsigned X>
__attribute__((always_inline)) static void SigTrap(uptr p, uptr size) {
#if defined(__aarch64__)
register uptr x0 asm("x0") = p;
register uptr x1 asm("x1") = size;
asm("brk %2\n\t" ::"r"(x0), "r"(x1), "n"(0x900 + X));
#elif defined(__x86_64__)
// Size is stored in rsi.
asm volatile(
"int3\n"
"nopl %c0(%%rax)\n" ::"n"(0x40 + X),
"D"(p), "S"(size));
#else
__builtin_trap();
#endif
// __builtin_unreachable();
}
enum class ErrorAction { Abort, Recover }; enum class ErrorAction { Abort, Recover };
enum class AccessType { Load, Store }; enum class AccessType { Load, Store };
@ -69,7 +89,7 @@ __attribute__((always_inline, nodebug)) static void CheckAddressSized(uptr p,
for (tag_t *t = shadow_first; t <= shadow_last; ++t) for (tag_t *t = shadow_first; t <= shadow_last; ++t)
if (UNLIKELY(ptr_tag != *t)) { if (UNLIKELY(ptr_tag != *t)) {
SigTrap<0x20 * (EA == ErrorAction::Recover) + SigTrap<0x20 * (EA == ErrorAction::Recover) +
0x10 * (AT == AccessType::Store) + 0xf>(p); 0x10 * (AT == AccessType::Store) + 0xf>(p, sz);
if (EA == ErrorAction::Abort) if (EA == ErrorAction::Abort)
__builtin_unreachable(); __builtin_unreachable();
} }

View File

@ -399,13 +399,21 @@ void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
Thread *t = GetCurrentThread(); Thread *t = GetCurrentThread();
sptr offset =
__hwasan_test_shadow(reinterpret_cast<void *>(tagged_addr), access_size);
CHECK(offset >= 0 && offset < static_cast<sptr>(access_size));
tag_t ptr_tag = GetTagFromPointer(tagged_addr); tag_t ptr_tag = GetTagFromPointer(tagged_addr);
tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr)); tag_t *tag_ptr =
reinterpret_cast<tag_t *>(MemToShadow(untagged_addr + offset));
tag_t mem_tag = *tag_ptr; tag_t mem_tag = *tag_ptr;
Printf("%s", d.Access()); Printf("%s", d.Access());
Printf("%s of size %zu at %p tags: %02x/%02x (ptr/mem) in thread T%zd\n", Printf("%s of size %zu at %p tags: %02x/%02x (ptr/mem) in thread T%zd\n",
is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag, is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag,
mem_tag, t->unique_id()); mem_tag, t->unique_id());
if (offset != 0)
Printf("Invalid access starting at offset [%zu, %zu)\n", offset,
Min(access_size, static_cast<uptr>(offset) + (1 << kShadowScale)));
Printf("%s", d.Default()); Printf("%s", d.Default());
stack->Print(); stack->Print();

View File

@ -10,8 +10,8 @@
#include <unistd.h> #include <unistd.h>
int main() { int main() {
char Q[16]; char Q[16] __attribute__((aligned(256)));
char P[16]; char P[16] __attribute__((aligned(256)));
#if TEST_NO == 1 #if TEST_NO == 1
memset(Q, 0, 32); memset(Q, 0, 32);
#elif TEST_NO == 2 #elif TEST_NO == 2
@ -21,15 +21,17 @@ int main() {
#endif #endif
write(STDOUT_FILENO, "recovered\n", 10); write(STDOUT_FILENO, "recovered\n", 10);
// WRITE: ERROR: HWAddressSanitizer: tag-mismatch on address // WRITE: ERROR: HWAddressSanitizer: tag-mismatch on address
// WRITE: WRITE {{.*}} tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem) // WRITE: WRITE of size 32 at {{.*}} tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem)
// WRITE: Invalid access starting at offset [16, 32)
// WRITE: Memory tags around the buggy address (one tag corresponds to 16 bytes): // WRITE: Memory tags around the buggy address (one tag corresponds to 16 bytes):
// WRITE: =>{{.*}}[[MEM_TAG]] // WRITE: =>{{.*}}[[PTR_TAG]]{{[[:space:]]\[}}[[MEM_TAG]]
// WRITE-NOT: recovered // WRITE-NOT: recovered
// READ: ERROR: HWAddressSanitizer: tag-mismatch on address // READ: ERROR: HWAddressSanitizer: tag-mismatch on address
// READ-NOT: Invalid access starting at offset
// READ: READ {{.*}} tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem) // READ: READ {{.*}} tags: [[PTR_TAG:..]]/[[MEM_TAG:..]] (ptr/mem)
// READ: Memory tags around the buggy address (one tag corresponds to 16 bytes): // READ: Memory tags around the buggy address (one tag corresponds to 16 bytes):
// READ: =>{{.*}}[[MEM_TAG]] // READ: =>{{.*}}[[PTR_TAG]]{{[[:space:]]\[}}[[MEM_TAG]]
// READ-NOT: recovered // READ-NOT: recovered
// RECOVER: recovered // RECOVER: recovered