Skip to main content

zx/
exception.rs

1// Copyright 2017 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//! Type-safe bindings for Zircon event objects.
6
7use crate::{NullableHandle, Process, Status, Thread, ok, sys};
8
9/// An object representing a Zircon
10/// [exception object](https://fuchsia.dev/fuchsia-src/concepts/kernel/exceptions).
11///
12/// As essentially a subtype of `NullableHandle`, it can be freely interconverted.
13#[derive(Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
14#[repr(transparent)]
15pub struct Exception(NullableHandle);
16impl_handle_based!(Exception);
17
18impl Exception {
19    /// Create a handle for the exception's thread
20    ///
21    /// Wraps the
22    /// [zx_exception_get_thread](https://fuchsia.dev/fuchsia-src/reference/syscalls/exception_get_thread)
23    /// syscall.
24    pub fn get_thread(&self) -> Result<Thread, Status> {
25        let mut handle = 0;
26        let status = unsafe { sys::zx_exception_get_thread(self.raw_handle(), &mut handle) };
27        ok(status)?;
28        unsafe { Ok(Thread::from(NullableHandle::from_raw(handle))) }
29    }
30
31    /// Create a handle for the exception's process
32    ///
33    /// Wraps the
34    /// [zx_exception_get_thread](https://fuchsia.dev/fuchsia-src/reference/syscalls/exception_get_thread)
35    /// syscall.
36    pub fn get_process(&self) -> Result<Process, Status> {
37        let mut handle = 0;
38        let status = unsafe { sys::zx_exception_get_process(self.raw_handle(), &mut handle) };
39        ok(status)?;
40        unsafe { Ok(Process::from(NullableHandle::from_raw(handle))) }
41    }
42}
43
44unsafe_handle_properties!(object: Exception,
45    props: [
46        {query_ty: EXCEPTION_STATE, tag: ExceptionStateTag, prop_ty: sys::zx_exception_state_t, get: get_exception_state, set: set_exception_state},
47    ]
48);
49
50/// Information about an exception that occurred in a process.
51#[derive(Debug, Copy, Clone)]
52pub struct ExceptionReport {
53    /// The specific type of the exception.
54    pub ty: ExceptionType,
55    /// Architecture-specific information about the exception.
56    pub arch: ExceptionArchData,
57}
58
59impl ExceptionReport {
60    /// # Safety
61    ///
62    /// The provided exception report must have been written by the kernel with the `context.arch`
63    /// field matching the architecture of the current device.
64    pub unsafe fn from_raw(raw: sys::zx_exception_report_t) -> Self {
65        debug_assert_eq!(
66            raw.header.size as usize,
67            std::mem::size_of::<sys::zx_exception_report_t>()
68        );
69        let ty = ExceptionType::from_raw(
70            raw.header.type_,
71            raw.context.synth_code,
72            raw.context.synth_data,
73        );
74
75        // SAFETY: if the report was written by the kernel, the correct union variant will be
76        // populated for each architecture.
77        #[cfg(target_arch = "x86_64")]
78        let arch = unsafe { ExceptionArchData::from_raw(raw.context.arch.x86_64) };
79        #[cfg(target_arch = "aarch64")]
80        let arch = unsafe { ExceptionArchData::from_raw(raw.context.arch.arm_64) };
81        #[cfg(target_arch = "riscv64")]
82        let arch = unsafe { ExceptionArchData::from_raw(raw.context.arch.riscv_64) };
83
84        Self { ty, arch }
85    }
86}
87
88/// x64-specific exception data.
89#[derive(Debug, Copy, Clone)]
90#[cfg(target_arch = "x86_64")]
91pub struct ExceptionArchData {
92    pub vector: u64,
93    pub err_code: u64,
94    pub cr2: u64,
95}
96
97#[cfg(target_arch = "x86_64")]
98impl ExceptionArchData {
99    fn from_raw(raw: sys::zx_x86_64_exc_data_t) -> Self {
100        Self { vector: raw.vector, err_code: raw.err_code, cr2: raw.cr2 }
101    }
102}
103
104/// arm64-specific exception data.
105#[derive(Debug, Copy, Clone)]
106#[cfg(target_arch = "aarch64")]
107pub struct ExceptionArchData {
108    pub esr: u32,
109    pub far: u64,
110}
111
112#[cfg(target_arch = "aarch64")]
113impl ExceptionArchData {
114    fn from_raw(raw: sys::zx_arm64_exc_data_t) -> Self {
115        Self { esr: raw.esr, far: raw.far }
116    }
117}
118
119/// riscv64-specific exception data.
120#[derive(Debug, Copy, Clone)]
121#[cfg(target_arch = "riscv64")]
122pub struct ExceptionArchData {
123    pub cause: u64,
124    pub tval: u64,
125}
126
127#[cfg(target_arch = "riscv64")]
128impl ExceptionArchData {
129    fn from_raw(raw: sys::zx_riscv64_exc_data_t) -> Self {
130        Self { cause: raw.cause, tval: raw.tval }
131    }
132}
133
134/// The type of an exception observed.
135#[derive(Debug, Copy, Clone)]
136pub enum ExceptionType {
137    /// A general exception occurred.
138    General,
139
140    /// The process generated an unhandled page fault.
141    FatalPageFault {
142        /// Contains the error code returned by the page fault handler.
143        status: Status,
144    },
145
146    /// The process attempted to execute an undefined CPU instruction.
147    UndefinedInstruction,
148
149    /// A software breakpoint was reached by the process.
150    SoftwareBreakpoint,
151
152    /// A hardware breakpoint was reached by the process.
153    HardwareBreakpoint,
154
155    /// The process attempted to perform an unaligned memory access.
156    UnalignedAccess,
157
158    /// A thread is starting.
159    ///
160    /// This exception is sent to debuggers only (ZX_EXCEPTION_CHANNEL_TYPE_DEBUGGER).
161    /// The thread that generates this exception is paused until it the debugger
162    /// handles the exception.
163    ThreadStarting,
164
165    /// A thread is exiting.
166    ///
167    /// This exception is sent to debuggers only (ZX_EXCEPTION_CHANNEL_TYPE_DEBUGGER).
168    ///
169    /// This exception is different from ZX_EXCP_GONE in that a debugger can
170    /// still examine thread state.
171    ///
172    /// The thread that generates this exception is paused until it the debugger
173    /// handles the exception.
174    ThreadExiting,
175
176    /// This exception is generated when a syscall fails with a job policy error (for
177    /// example, an invalid handle argument is passed to the syscall when the
178    /// ZX_POL_BAD_HANDLE policy is enabled) and ZX_POL_ACTION_EXCEPTION is set for
179    /// the policy. The thread that generates this exception is paused until it the
180    /// debugger handles the exception. Additional data about the type of policy
181    /// error can be found in the |synth_code| field of the report and will be a
182    /// ZX_EXCP_POLICY_CODE_* value.
183    PolicyError(PolicyCode),
184
185    /// A process is starting.
186    ///
187    /// This exception is sent to job debuggers only (ZX_EXCEPTION_CHANNEL_TYPE_JOB_DEBUGGER).
188    ///
189    /// The thread that generates this exception is paused until it the debugger
190    /// handles the exception.
191    ProcessStarting,
192
193    /// A user-generated exception indicating to a debugger that the process' name has changed.
194    ProcessNameChanged,
195
196    /// The exception was user-generated but of an unknown type.
197    UnknownUserGenerated { code: u32, data: u32 },
198
199    /// An unknown exception type.
200    Unknown { ty: u32, code: u32, data: u32 },
201}
202
203impl ExceptionType {
204    fn from_raw(raw: sys::zx_excp_type_t, code: u32, data: u32) -> Self {
205        match raw {
206            sys::ZX_EXCP_GENERAL => Self::General,
207            sys::ZX_EXCP_FATAL_PAGE_FAULT => {
208                Self::FatalPageFault { status: Status::from_raw(code as i32) }
209            }
210            sys::ZX_EXCP_UNDEFINED_INSTRUCTION => Self::UndefinedInstruction,
211            sys::ZX_EXCP_SW_BREAKPOINT => Self::SoftwareBreakpoint,
212            sys::ZX_EXCP_HW_BREAKPOINT => Self::HardwareBreakpoint,
213            sys::ZX_EXCP_UNALIGNED_ACCESS => Self::UnalignedAccess,
214            sys::ZX_EXCP_THREAD_STARTING => Self::ThreadStarting,
215            sys::ZX_EXCP_THREAD_EXITING => Self::ThreadExiting,
216            sys::ZX_EXCP_POLICY_ERROR => Self::PolicyError(PolicyCode::from_raw(code, data)),
217            sys::ZX_EXCP_PROCESS_STARTING => Self::ProcessStarting,
218            sys::ZX_EXCP_USER => match code {
219                sys::ZX_EXCP_USER_CODE_PROCESS_NAME_CHANGED => Self::ProcessNameChanged,
220                _ => Self::UnknownUserGenerated { code, data },
221            },
222            other => Self::Unknown { ty: other, code, data },
223        }
224    }
225}
226
227#[derive(Clone, Copy, Debug)]
228pub enum PolicyCode {
229    BadHandle,
230    WrongObject,
231    VmarWriteExecutable,
232    NewAny,
233    NewVmo,
234    NewChannel,
235    NewEvent,
236    NewEventPair,
237    NewPort,
238    NewSocket,
239    NewFifo,
240    NewTimer,
241    NewProcess,
242    NewProfile,
243    NewPager,
244    AmbientMarkVmoExecutable,
245    ChannelFullWrite,
246    PortTooManyPackets,
247    BadSyscall {
248        number: u32,
249    },
250    PortTooManyObservers,
251
252    /// An invalid zx_handle_t* was passed to a syscall, resulting in a handle leak.
253    /// This exception is generated when a thread fails to provide a valid target
254    /// handle buffer for syscalls that return handles. This is temporary until we
255    /// have fully migrated to HandleTableV3.
256    HandleLeak,
257    NewIob,
258
259    /// An unknown policy error.
260    Unknown {
261        code: u32,
262        data: u32,
263    },
264}
265
266impl PolicyCode {
267    fn from_raw(code: u32, data: u32) -> Self {
268        match code {
269            sys::ZX_EXCP_POLICY_CODE_BAD_HANDLE => Self::BadHandle,
270            sys::ZX_EXCP_POLICY_CODE_WRONG_OBJECT => Self::WrongObject,
271            sys::ZX_EXCP_POLICY_CODE_VMAR_WX => Self::VmarWriteExecutable,
272            sys::ZX_EXCP_POLICY_CODE_NEW_ANY => Self::NewAny,
273            sys::ZX_EXCP_POLICY_CODE_NEW_VMO => Self::NewVmo,
274            sys::ZX_EXCP_POLICY_CODE_NEW_CHANNEL => Self::NewChannel,
275            sys::ZX_EXCP_POLICY_CODE_NEW_EVENT => Self::NewEvent,
276            sys::ZX_EXCP_POLICY_CODE_NEW_EVENTPAIR => Self::NewEventPair,
277            sys::ZX_EXCP_POLICY_CODE_NEW_PORT => Self::NewPort,
278            sys::ZX_EXCP_POLICY_CODE_NEW_SOCKET => Self::NewSocket,
279            sys::ZX_EXCP_POLICY_CODE_NEW_FIFO => Self::NewFifo,
280            sys::ZX_EXCP_POLICY_CODE_NEW_TIMER => Self::NewTimer,
281            sys::ZX_EXCP_POLICY_CODE_NEW_PROCESS => Self::NewProcess,
282            sys::ZX_EXCP_POLICY_CODE_NEW_PROFILE => Self::NewProfile,
283            sys::ZX_EXCP_POLICY_CODE_NEW_PAGER => Self::NewPager,
284            sys::ZX_EXCP_POLICY_CODE_AMBIENT_MARK_VMO_EXEC => Self::AmbientMarkVmoExecutable,
285            sys::ZX_EXCP_POLICY_CODE_CHANNEL_FULL_WRITE => Self::ChannelFullWrite,
286            sys::ZX_EXCP_POLICY_CODE_PORT_TOO_MANY_PACKETS => Self::PortTooManyPackets,
287            sys::ZX_EXCP_POLICY_CODE_BAD_SYSCALL => Self::BadSyscall { number: data },
288            sys::ZX_EXCP_POLICY_CODE_PORT_TOO_MANY_OBSERVERS => Self::PortTooManyObservers,
289            sys::ZX_EXCP_POLICY_CODE_HANDLE_LEAK => Self::HandleLeak,
290            sys::ZX_EXCP_POLICY_CODE_NEW_IOB => Self::NewIob,
291            _ => Self::Unknown { code, data },
292        }
293    }
294}