Skip to main content

fidl/
error.rs

1// Copyright 2016 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//! Error (common to all fidl operations)
6
7use crate::handle::{ObjectType, Rights};
8use std::sync::Arc;
9use zx_status::Status;
10
11/// A specialized `Result` type for FIDL operations.
12pub type Result<T, E = Error> = std::result::Result<T, E>;
13
14#[derive(Debug, Clone, thiserror::Error)]
15#[allow(missing_docs)]
16pub enum TransportError {
17    #[error(transparent)]
18    Status(#[from] Status),
19    #[error(transparent)]
20    Other(Arc<dyn std::error::Error + Send + Sync>),
21}
22
23// This prevents some binary size bloat.
24impl Drop for TransportError {
25    #[inline(never)]
26    fn drop(&mut self) {}
27}
28
29/// Represents the closure condition of a FIDL channel, either via an explicit epitaph or a direct
30/// peer closure.
31#[derive(Copy, Clone, Debug, Eq, PartialEq)]
32pub enum Epitaph {
33    /// The channel was closed with an explicit epitaph status (`Ok(())` for `ZX_OK` success, or
34    /// `Err(Status)` for an error).
35    Explicit(std::result::Result<(), zx_status::Status>),
36    /// The channel was closed without an epitaph message (`PEER_CLOSED`).
37    PeerClosed,
38}
39
40impl std::fmt::Display for Epitaph {
41    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
42        match self {
43            Epitaph::Explicit(Ok(())) => write!(f, "ZX_OK (0)"),
44            Epitaph::Explicit(Err(s)) => write!(f, "{}", s),
45            Epitaph::PeerClosed => write!(f, "{}", zx_status::Status::PEER_CLOSED),
46        }
47    }
48}
49
50impl std::error::Error for Epitaph {
51    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
52        match self {
53            Epitaph::Explicit(Ok(())) | Epitaph::PeerClosed => None,
54            Epitaph::Explicit(Err(s)) => Some(s),
55        }
56    }
57}
58
59impl Epitaph {
60    /// Returns `true` if an explicit epitaph was received or set (`Explicit`).
61    pub fn is_explicit(&self) -> bool {
62        matches!(self, Epitaph::Explicit(_))
63    }
64
65    /// Returns `true` if the channel was closed without an epitaph (`PEER_CLOSED`)
66    /// or with an explicit `PEER_CLOSED` epitaph.
67    pub fn is_peer_closed(&self) -> bool {
68        matches!(self, Epitaph::PeerClosed | Epitaph::Explicit(Err(zx_status::Status::PEER_CLOSED)))
69    }
70
71    /// Returns `true` if an explicit `ZX_OK` (`Ok(())`) epitaph was received.
72    pub fn is_ok(&self) -> bool {
73        matches!(self, Epitaph::Explicit(Ok(())))
74    }
75}
76
77impl PartialEq<zx_status::Status> for Epitaph {
78    fn eq(&self, other: &zx_status::Status) -> bool {
79        match self {
80            Epitaph::Explicit(Ok(())) => *other == zx_status::Status::OK,
81            Epitaph::Explicit(Err(s)) => s == other,
82            Epitaph::PeerClosed => *other == zx_status::Status::PEER_CLOSED,
83        }
84    }
85}
86
87impl PartialEq<Epitaph> for zx_status::Status {
88    fn eq(&self, other: &Epitaph) -> bool {
89        other.eq(self)
90    }
91}
92
93impl PartialEq<std::result::Result<(), zx_status::Status>> for Epitaph {
94    fn eq(&self, other: &std::result::Result<(), zx_status::Status>) -> bool {
95        let res: std::result::Result<(), zx_status::Status> = (*self).into();
96        &res == other
97    }
98}
99
100impl PartialEq<Epitaph> for std::result::Result<(), zx_status::Status> {
101    fn eq(&self, other: &Epitaph) -> bool {
102        other.eq(self)
103    }
104}
105
106impl From<Epitaph> for std::result::Result<(), zx_status::Status> {
107    fn from(epitaph: Epitaph) -> Self {
108        match epitaph {
109            Epitaph::Explicit(res) => res,
110            Epitaph::PeerClosed => Err(zx_status::Status::PEER_CLOSED),
111        }
112    }
113}
114
115impl From<&Epitaph> for std::result::Result<(), zx_status::Status> {
116    fn from(epitaph: &Epitaph) -> Self {
117        (*epitaph).into()
118    }
119}
120
121impl From<zx_status::Status> for Epitaph {
122    fn from(status: zx_status::Status) -> Self {
123        if status == zx_status::Status::OK {
124            Epitaph::Explicit(Ok(()))
125        } else {
126            Epitaph::Explicit(Err(status))
127        }
128    }
129}
130
131impl From<std::result::Result<(), zx_status::Status>> for Epitaph {
132    fn from(res: std::result::Result<(), zx_status::Status>) -> Self {
133        Epitaph::Explicit(res)
134    }
135}
136
137/// The error type used by FIDL operations.
138#[derive(Debug, Clone, thiserror::Error)]
139#[non_exhaustive]
140#[allow(missing_docs)]
141pub enum Error {
142    #[error("Unexpected response to synchronous FIDL query.")]
143    UnexpectedSyncResponse,
144
145    #[error("Invalid FIDL boolean.")]
146    InvalidBoolean,
147
148    #[error("Invalid header for a FIDL buffer.")]
149    InvalidHeader,
150
151    #[error("Incompatible wire format magic number: {0}.")]
152    IncompatibleMagicNumber(u8),
153
154    #[error("Unsupported wire format version")]
155    UnsupportedWireFormatVersion,
156
157    #[error("Invalid FIDL buffer.")]
158    Invalid,
159
160    #[error(
161        "The FIDL object of size {expected} could not fit within the provided \
162        buffer range of size {actual}."
163    )]
164    OutOfRange { expected: usize, actual: usize },
165
166    #[error(
167        "The FIDL object requested more handles during decoding than were \
168        provided with the message."
169    )]
170    OutOfHandles,
171
172    #[error("Decoding the FIDL object did not use all of the bytes provided.")]
173    ExtraBytes,
174
175    #[error("Decoding the FIDL object did not use all of the handles provided.")]
176    ExtraHandles,
177
178    #[error(
179        "Decoding the FIDL object observed non-zero value in the padding region \
180        starting at byte {padding_start}."
181    )]
182    NonZeroPadding {
183        /// Index of the first byte of the padding, relative to the beginning of the message.
184        padding_start: usize,
185    },
186
187    #[error("The FIDL object had too many layers of out-of-line recursion.")]
188    MaxRecursionDepth,
189
190    #[error(
191        "There was an attempt to read or write a null-valued object as a non-nullable FIDL type."
192    )]
193    NotNullable,
194
195    #[error("A FIDL object reference with nonzero byte length had a null data pointer.")]
196    UnexpectedNullRef,
197
198    #[error("A FIDL message contained incorrectly encoded UTF8.")]
199    Utf8Error,
200
201    #[error("Vector was too long. Expected at most {max_length} elements, got {actual_length}.")]
202    VectorTooLong {
203        /// Maximum length, i.e. the `N` in `vector<T>:N`.
204        max_length: usize,
205        /// Actual length of the vector (number of elements).
206        actual_length: usize,
207    },
208
209    #[error("String was too long. Expected at most {max_bytes} bytes, got {actual_bytes}.")]
210    StringTooLong {
211        /// Maximum length in bytes, i.e. the `N` in `string:N`.
212        max_bytes: usize,
213        /// Actual length of the string in bytes.
214        actual_bytes: usize,
215    },
216
217    #[error(
218        "A message was received for ordinal value {ordinal} that the FIDL \
219        protocol {protocol_name} does not understand."
220    )]
221    UnknownOrdinal { ordinal: u64, protocol_name: &'static str },
222
223    #[error("Server for the FIDL protocol {protocol_name} did not recognize method {method_name}.")]
224    UnsupportedMethod { method_name: &'static str, protocol_name: &'static str },
225
226    #[error("Invalid bits value for a strict bits type.")]
227    InvalidBitsValue,
228
229    #[error("Invalid enum value for a strict enum type.")]
230    InvalidEnumValue,
231
232    #[error("Unrecognized descriminant for a FIDL union type.")]
233    UnknownUnionTag,
234
235    #[error("A FIDL future was polled after it had already completed.")]
236    PollAfterCompletion,
237
238    #[error(
239        "Received request with zero txid for two-way method ordinal, \
240        or nonzero txid for one-way method ordinal."
241    )]
242    InvalidRequestTxid,
243
244    #[error("Received response with unknown txid.")]
245    InvalidResponseTxid,
246
247    #[error("Received response with unexpected ordinal.")]
248    InvalidResponseOrdinal,
249
250    #[error("Invalid presence indicator.")]
251    InvalidPresenceIndicator,
252
253    #[error("Invalid inline bit in envelope.")]
254    InvalidInlineBitInEnvelope,
255
256    #[error("Invalid inline marker in envelope.")]
257    InvalidInlineMarkerInEnvelope,
258
259    #[error("Invalid number of bytes in FIDL envelope.")]
260    InvalidNumBytesInEnvelope,
261
262    #[error("Invalid number of handles in FIDL envelope.")]
263    InvalidNumHandlesInEnvelope,
264
265    #[error("Invalid FIDL handle used on the host.")]
266    InvalidHostHandle,
267
268    #[error("Incorrect handle subtype. Expected {}, but received {}", .expected.into_raw(), .received.into_raw())]
269    IncorrectHandleSubtype { expected: ObjectType, received: ObjectType },
270
271    #[error("Some expected handle rights are missing: {}", .missing_rights.bits())]
272    MissingExpectedHandleRights { missing_rights: Rights },
273
274    #[error("An error was encountered during handle replace()")]
275    HandleReplace(#[source] Status),
276
277    #[error("A server encountered an IO error writing a FIDL response to a channel: {0}")]
278    ServerResponseWrite(#[source] TransportError),
279
280    #[error(
281        "A FIDL server encountered an IO error reading incoming FIDL requests from a channel: {0}"
282    )]
283    ServerRequestRead(#[source] TransportError),
284
285    #[error("A FIDL server encountered an IO error writing an epitaph into a channel: {0}")]
286    ServerEpitaphWrite(#[source] TransportError),
287
288    #[error("A FIDL client encountered an IO error reading a response from a channel: {0}")]
289    ClientRead(#[source] TransportError),
290
291    #[error("A FIDL client encountered an IO error writing a request into a channel: {0}")]
292    ClientWrite(#[source] TransportError),
293
294    #[error("A FIDL client encountered an IO error issuing a channel call: {0}")]
295    ClientCall(#[source] Status),
296
297    #[error("A FIDL client encountered an IO error issuing a channel call: {0}")]
298    ClientEvent(#[source] Status),
299
300    #[cfg(not(target_os = "fuchsia"))]
301    #[error(
302        "A FIDL client's channel to the protocol {protocol_name} was closed: {epitaph}, reason: {}",
303        .reason.as_ref().map(String::as_str).unwrap_or("not given")
304    )]
305    ClientChannelClosed {
306        /// The epitaph or `Epitaph::PeerClosed`.
307        #[source]
308        epitaph: Epitaph,
309        /// The name of the protocol at the other end of the channel.
310        protocol_name: &'static str,
311        /// Further details on why exactly the channel closed.
312        reason: Option<String>,
313    },
314
315    #[cfg(target_os = "fuchsia")]
316    #[error("A FIDL client's channel to the protocol {protocol_name} was closed: {epitaph}")]
317    ClientChannelClosed {
318        /// The epitaph or `Epitaph::PeerClosed`.
319        #[source]
320        epitaph: Epitaph,
321        /// The name of the protocol at the other end of the channel.
322        protocol_name: &'static str,
323    },
324
325    #[error("There was an error attaching a FIDL channel to the async executor: {0}")]
326    AsyncChannel(#[source] Status),
327
328    #[cfg(target_os = "fuchsia")]
329    #[cfg(test)]
330    #[error("Test Status: {0}")]
331    TestIo(#[source] Status),
332}
333
334impl Error {
335    /// Returns `true` if the error was sourced by a closed channel.
336    pub fn is_closed(&self) -> bool {
337        matches!(self, Error::ClientChannelClosed { .. })
338    }
339}