1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <lib/fzl/memory-probe.h>
6
7 #include <limits.h>
8 #include <stdio.h>
9 #include <zircon/syscalls/exception.h>
10 #include <zircon/syscalls/port.h>
11 #include <lib/zx/port.h>
12 #include <lib/zx/process.h>
13 #include <lib/zx/thread.h>
14
15 namespace {
16
17 enum class ProbeOperation {
18 kRead,
19 kWrite
20 };
21
22 #if __has_feature(address_sanitizer)
23 [[clang::no_sanitize("address")]]
24 #endif
except_thread_func(uintptr_t op,uintptr_t address)25 void except_thread_func(uintptr_t op, uintptr_t address) {
26 volatile char* ch_address = reinterpret_cast<char*>(address);
27
28 char ch = *ch_address;
29 if (static_cast<ProbeOperation>(op) == ProbeOperation::kWrite)
30 *ch_address = ch;
31
32 zx_thread_exit();
33 }
34
do_probe(ProbeOperation op,const void * addr)35 bool do_probe(ProbeOperation op, const void* addr) {
36 // This function starts a new thread to perform the read/write test, and catches any exceptions
37 // in this thread to see if it failed or not.
38 zx::thread thread;
39 zx_status_t status = zx::thread::create(*zx::process::self(), "memory_probe", 12u, 0u, &thread);
40 if (status != ZX_OK)
41 return false;
42
43 alignas(16) static uint8_t thread_stack[128];
44 void* stack = thread_stack + sizeof(thread_stack);
45
46 zx::port port;
47 status = zx::port::create(0, &port);
48 if (status != ZX_OK)
49 return false;
50
51 // Cause the port to be signaled with kThreadKey when the background thread crashes or teminates without crashing.
52 constexpr uint64_t kThreadKey = 0x42;
53 if (thread.wait_async(port, kThreadKey, ZX_THREAD_TERMINATED, ZX_WAIT_ASYNC_ONCE) != ZX_OK) {
54 return false;
55 }
56 if (zx_task_bind_exception_port(thread.get(), port.get(), kThreadKey, 0) != ZX_OK) {
57 return false;
58 }
59
60 thread.start(&except_thread_func, stack, static_cast<uintptr_t>(op), reinterpret_cast<uintptr_t>(addr));
61
62 // Wait for crash or thread completion.
63 zx_port_packet_t packet;
64 if (port.wait(zx::time::infinite(), &packet) == ZX_OK) {
65 if (ZX_PKT_IS_EXCEPTION(packet.type)) {
66 // Thread crashed so the operation failed. The thread is now in a suspended state and
67 // needs to be explicitly terminated.
68 thread.kill();
69 return false;
70 }
71 if (ZX_PKT_IS_SIGNAL_ONE(packet.type)) {
72 if (packet.key == kThreadKey && (packet.signal.observed & ZX_THREAD_TERMINATED)) {
73 // Thread terminated normally so the memory is readable/writable.
74 return true;
75 }
76 }
77 }
78
79 return false;
80 }
81
82 } // namespace
83
probe_for_read(const void * addr)84 bool probe_for_read(const void* addr) {
85 return do_probe(ProbeOperation::kRead, addr);
86 }
87
probe_for_write(void * addr)88 bool probe_for_write(void* addr) {
89 return do_probe(ProbeOperation::kWrite, addr);
90 }
91