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