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