Annotate vfork for hwasan.
Call a hwasan hook in the parent return path for vfork() to let hwasan
update its shadow. See https://github.com/google/sanitizers/issues/925
for more details.
Bug: 112438058
Test: bionic-unit-tests
Change-Id: I9a06800962913e822bd66e072012d0a2c5be453d
diff --git a/libc/arch-arm64/bionic/vfork.S b/libc/arch-arm64/bionic/vfork.S
index 6acd64b..6c01572 100644
--- a/libc/arch-arm64/bionic/vfork.S
+++ b/libc/arch-arm64/bionic/vfork.S
@@ -51,5 +51,26 @@
cneg x0, x0, hi
b.hi __set_errno_internal
+#if __has_feature(hwaddress_sanitizer)
+ cbz x0, .L_exit
+
+ // Clean up stack shadow in the parent process.
+ // https://github.com/google/sanitizers/issues/925
+ stp x0, x30, [sp, #-16]!
+ .cfi_adjust_cfa_offset 16
+ .cfi_rel_offset x0, 0
+ .cfi_rel_offset x30, 8
+
+ add x0, sp, #16
+ bl __hwasan_handle_vfork
+
+ ldp x0, x30, [sp], #16
+ .cfi_adjust_cfa_offset -16
+ .cfi_restore x0
+ .cfi_restore x30
+
+#endif
+
+.L_exit:
ret
END(vfork)
diff --git a/tests/unistd_test.cpp b/tests/unistd_test.cpp
index cb94e45..10c1710 100644
--- a/tests/unistd_test.cpp
+++ b/tests/unistd_test.cpp
@@ -628,6 +628,45 @@
ASSERT_NE(static_cast<uint64_t>(parent_tid), reinterpret_cast<uint64_t>(result));
}
+static void optimization_barrier(void* arg) {
+ asm volatile("" : : "r"(arg) : "memory");
+}
+
+__attribute__((noinline)) static void HwasanVforkTestChild() {
+ // Allocate a tagged region on stack and leave it there.
+ char x[10000];
+ optimization_barrier(x);
+ _exit(0);
+}
+
+__attribute__((noinline)) static void HwasanReadMemory(const char* p, size_t size) {
+ // Read memory byte-by-byte. This will blow up if the pointer tag in p does not match any memory
+ // tag in [p, p+size).
+ volatile char z;
+ for (size_t i = 0; i < size; ++i) {
+ z = p[i];
+ }
+}
+
+__attribute__((noinline, no_sanitize("hwaddress"))) static void HwasanVforkTestParent() {
+ // Allocate a region on stack, but don't tag it (see the function attribute).
+ // This depends on unallocated stack space at current function entry being untagged.
+ char x[10000];
+ optimization_barrier(x);
+ // Verify that contents of x[] are untagged.
+ HwasanReadMemory(x, sizeof(x));
+}
+
+TEST(UNISTD_TEST, hwasan_vfork) {
+ // Test hwasan annotation in vfork. This test is only interesting when built with hwasan, but it
+ // is supposed to work correctly either way.
+ if (vfork()) {
+ HwasanVforkTestParent();
+ } else {
+ HwasanVforkTestChild();
+ }
+}
+
class UNISTD_DEATHTEST : public BionicDeathTest {};
TEST_F(UNISTD_DEATHTEST, abort) {