Skip to main content

process_builder/
process_args.rs

1// Copyright 2019 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// The constant and type definitions in this file must all match
6// //zircon/system/public/zircon/processargs.h
7use fuchsia_runtime::HandleInfo;
8
9use std::ffi::CString;
10use std::{fmt, mem, num};
11use thiserror::Error;
12use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
13
14/// Possible errors that can occur during processargs startup message construction
15#[derive(Error, Debug)]
16pub enum ProcessargsError {
17    TryFromInt(num::TryFromIntError),
18    SizeTooLarge(usize),
19    TooManyHandles(usize),
20}
21
22impl ProcessargsError {
23    /// Returns an appropriate zx::Status code for the given error.
24    pub fn as_zx_status(&self) -> zx::Status {
25        match self {
26            ProcessargsError::TryFromInt(_)
27            | ProcessargsError::SizeTooLarge(_)
28            | ProcessargsError::TooManyHandles(_) => zx::Status::INVALID_ARGS,
29        }
30    }
31}
32
33// Can't use macro-based failure Display derive with the _MAX_MSG_BYTES argument below
34impl fmt::Display for ProcessargsError {
35    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
36        match self {
37            ProcessargsError::TryFromInt(e) => {
38                write!(f, "Value > u32 MAX when building processargs message: {}", e)
39            }
40            ProcessargsError::SizeTooLarge(v) => write!(
41                f,
42                "Cannot build processargs message, byte size too large ({} > {})",
43                v,
44                zx::sys::ZX_CHANNEL_MAX_MSG_BYTES
45            ),
46            ProcessargsError::TooManyHandles(v) => write!(
47                f,
48                "Cannot build processargs message, too many handles ({} > {})",
49                v,
50                zx::sys::ZX_CHANNEL_MAX_MSG_HANDLES
51            ),
52        }
53    }
54}
55
56const ZX_PROCARGS_PROTOCOL: u32 = 0x4150585d;
57const ZX_PROCARGS_VERSION: u32 = 0x00001000;
58
59/// Header for bootstrap message following the processargs protocol.
60#[derive(KnownLayout, FromBytes, IntoBytes, Immutable, Default)]
61#[repr(C)]
62pub(crate) struct MessageHeader {
63    // Protocol and version identifiers to allow for different process start message protocols and
64    // versioning of the same.
65    pub protocol: u32,
66    pub version: u32,
67
68    // Offset from start of message to handle info array, which contains one HandleInfo as a u32
69    // per handle passed along with the message.
70    pub handle_info_off: u32,
71
72    // Offset from start of message to arguments and count of arguments. Arguments are provided as
73    // a set of null-terminated UTF-8 strings, one after the other.
74    pub args_off: u32,
75    pub args_num: u32,
76
77    // Offset from start of message to environment strings and count of them.  Environment entries
78    // are provided as a set of null-terminated UTF-8 strings, one after the other. Canonically
79    // each string has the form "NAME=VALUE", but nothing enforces this.
80    pub environ_off: u32,
81    pub environ_num: u32,
82
83    // Offset from start of message to namespace path strings and count of them. These strings are
84    // packed similar to the argument strings, but are referenced by NamespaceDirectory (PA_NS_DIR)
85    // handle table entries and used to set up namespaces.
86    //
87    // Specifically: In a handle table entry with HandleType of NamespaceDirectory (PA_NS_DIR), the
88    // u16 handle info argument is an index into this name table.
89    names_off: u32,
90    names_num: u32,
91}
92
93/// A container for a single startup handle, containing a handle and metadata. Used as an input to
94/// [ProcessBuilder::add_handles()].
95///
96/// [ProcessBuilder::add_handles()]: crate::ProcessBuilder::add_handles()
97pub struct StartupHandle {
98    /// A handle.
99    pub handle: zx::NullableHandle,
100
101    /// Handle metadata. See [fuchsia_runtime::HandleInfo].
102    pub info: HandleInfo,
103}
104
105/// Contents of a bootstrap message, following the processargs protocol. Used as
106/// an input to [Message::build()].
107#[derive(Default)]
108pub struct MessageContents {
109    /// Arguments for a bootstrap message.
110    pub args: Vec<CString>,
111
112    /// Environment variables for a bootstrap message.
113    pub environment_vars: Vec<CString>,
114
115    /// Namespace paths for a bootstrap message.
116    pub namespace_paths: Vec<CString>,
117
118    /// Handles for a bootstrap message.
119    pub handles: Vec<StartupHandle>,
120}
121
122/// A bootstrap message following the processargs protocol.
123///
124/// See [//docs/zircon/program_loading.md#The-processargs-protocol][program_loading.md] or
125/// [//zircon/system/public/zircon/processargs.h][processargs] for more details.
126///
127/// [program_loading.md]: https://fuchsia.dev/fuchsia-src/concepts/booting/program_loading#The-processargs-protocol
128/// [processargs]: https://fuchsia.googlesource.com/fuchsia/+/HEAD/zircon/system/public/zircon/processargs.h
129pub struct Message {
130    bytes: Vec<u8>,
131    handles: Vec<zx::NullableHandle>,
132}
133
134// Return type of fuchsia_runtime::HandleInfo::as_raw(), checked with static assert below.
135type HandleInfoRaw = u32;
136
137impl Message {
138    /// Create a new bootstrap message using the given contents.
139    pub fn build(contents: MessageContents) -> Result<Message, ProcessargsError> {
140        let (header, size) = Self::build_header(&contents)?;
141
142        let mut data = Vec::with_capacity(size);
143        data.extend_from_slice(header.as_bytes());
144
145        // Sanity check length against the offsets in the header as we go. Failures are bugs in
146        // this code and serious enough to panic rather than continue, hence the asserts rather
147        // than returning an Err().
148        assert!(data.len() == header.handle_info_off as usize);
149        let mut handles = Vec::with_capacity(contents.handles.len());
150        for handle in contents.handles {
151            let raw_info = handle.info.as_raw();
152            static_assertions::assert_eq_size_val!(raw_info, 0 as HandleInfoRaw);
153
154            data.extend_from_slice(&raw_info.to_ne_bytes());
155            handles.push(handle.handle);
156        }
157
158        assert!(data.len() == header.args_off as usize);
159        for arg in &contents.args {
160            data.extend_from_slice(arg.as_bytes_with_nul());
161        }
162
163        assert!(data.len() == header.environ_off as usize);
164        for var in &contents.environment_vars {
165            data.extend_from_slice(var.as_bytes_with_nul());
166        }
167
168        assert!(data.len() == header.names_off as usize);
169        for path in &contents.namespace_paths {
170            data.extend_from_slice(path.as_bytes_with_nul());
171        }
172
173        // Sanity check final message size.
174        assert!(data.len() == size);
175        Ok(Message { bytes: data, handles })
176    }
177
178    /// Calculate the size that a bootstrap message will be if created using the given contents.
179    ///
180    /// Note that the size returned is only for the message data and does not include the size of
181    /// the handles themselves, only the handle info in the message.
182    pub fn calculate_size(contents: &MessageContents) -> Result<usize, ProcessargsError> {
183        let (_, size) = Self::build_header(contents)?;
184        Ok(size)
185    }
186
187    /// Builds the processargs message header for the given config, as well as calculates the total
188    /// message size.
189    fn build_header(config: &MessageContents) -> Result<(MessageHeader, usize), ProcessargsError> {
190        let num_handles = config.handles.len();
191        if num_handles > zx::sys::ZX_CHANNEL_MAX_MSG_HANDLES as usize {
192            return Err(ProcessargsError::TooManyHandles(num_handles));
193        }
194
195        let mut header = MessageHeader {
196            protocol: ZX_PROCARGS_PROTOCOL,
197            version: ZX_PROCARGS_VERSION,
198            ..Default::default()
199        };
200
201        let mut size = mem::size_of_val(&header);
202        let mut f = || {
203            header.handle_info_off = u32::try_from(size)?;
204            size += mem::size_of::<HandleInfoRaw>() * num_handles;
205
206            header.args_off = u32::try_from(size)?;
207            header.args_num = u32::try_from(config.args.len())?;
208            for arg in &config.args {
209                size += arg.as_bytes_with_nul().len();
210            }
211
212            header.environ_off = u32::try_from(size)?;
213            header.environ_num = u32::try_from(config.environment_vars.len())?;
214            for var in &config.environment_vars {
215                size += var.as_bytes_with_nul().len();
216            }
217
218            header.names_off = u32::try_from(size)?;
219            header.names_num = u32::try_from(config.namespace_paths.len())?;
220            for path in &config.namespace_paths {
221                size += path.as_bytes_with_nul().len();
222            }
223            Ok(())
224        };
225        f().map_err(|e| ProcessargsError::TryFromInt(e))?;
226
227        if size > zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize {
228            return Err(ProcessargsError::SizeTooLarge(size));
229        }
230        Ok((header, size))
231    }
232
233    /// Write the processargs message to the provided channel.
234    pub fn write(self, channel: &zx::Channel) -> Result<(), zx::Status> {
235        let mut handles = self.handles;
236        channel.write(self.bytes.as_slice(), &mut handles)
237    }
238}
239
240#[cfg(test)]
241mod tests {
242    use super::*;
243    use anyhow::Error;
244    use fuchsia_runtime::HandleType;
245    use std::iter;
246
247    #[test]
248    fn build_and_write_message() -> Result<(), Error> {
249        // We need some dummy handles to use in the message below, since you can't send an invalid
250        // handle in a channel. We just use VMOs since they're easy to create, even though they're
251        // not semantically valid for a processargs handle type like PA_NS_DIR.
252        let (dum0, dum1, dum2) = (zx::Vmo::create(1)?, zx::Vmo::create(1)?, zx::Vmo::create(1)?);
253        let handles = vec![
254            StartupHandle {
255                handle: dum0.into_handle(),
256                info: HandleInfo::new(HandleType::User1, 0x1234),
257            },
258            StartupHandle {
259                handle: dum1.into_handle(),
260                info: HandleInfo::new(HandleType::NamespaceDirectory, 0),
261            },
262            StartupHandle {
263                handle: dum2.into_handle(),
264                info: HandleInfo::new(HandleType::NamespaceDirectory, 1),
265            },
266        ];
267        let handle_koids: Vec<zx::Koid> =
268            handles.iter().map(|h| h.handle.koid()).collect::<Result<_, _>>()?;
269
270        let config = MessageContents {
271            args: vec![CString::new("arg1")?, CString::new("arg2")?, CString::new("arg3")?],
272            environment_vars: vec![CString::new("FOO=BAR")?],
273            namespace_paths: vec![CString::new("/data")?, CString::new("/pkg")?],
274            handles,
275        };
276
277        let calculated_size = Message::calculate_size(&config)?;
278        let message = Message::build(config)?;
279        assert_eq!(calculated_size, message.bytes.len());
280
281        // Write the message into a channel, read it back from the other end.
282        let (chan_wr, chan_rd) = zx::Channel::create();
283        message.write(&chan_wr)?;
284        let mut read_buf = zx::MessageBuf::new();
285        chan_rd.read(&mut read_buf)?;
286        let (read_bytes, read_handles) = read_buf.split();
287
288        // concat! doesn't work for byte literals and there's no concat_bytes! (yet), so we just
289        // build this in a Vec instead since it's a test.
290        let mut correct = Vec::new();
291        correct.extend_from_slice(b"\x5d\x58\x50\x41"); // protocol
292        correct.extend_from_slice(b"\x00\x10\x00\x00"); // version
293        correct.extend_from_slice(b"\x24\x00\x00\x00"); // handle_info_off
294        correct.extend_from_slice(b"\x30\x00\x00\x00"); // args_off
295        correct.extend_from_slice(b"\x03\x00\x00\x00"); // args_num
296        correct.extend_from_slice(b"\x3F\x00\x00\x00"); // environ_off
297        correct.extend_from_slice(b"\x01\x00\x00\x00"); // environ_num
298        correct.extend_from_slice(b"\x47\x00\x00\x00"); // names_off
299        correct.extend_from_slice(b"\x02\x00\x00\x00"); // names_num
300        correct.extend_from_slice(b"\xF1\x00\x34\x12"); // handle info
301        correct.extend_from_slice(b"\x20\x00\x00\x00"); //
302        correct.extend_from_slice(b"\x20\x00\x01\x00"); //
303        correct.extend_from_slice(b"arg1\0"); // args
304        correct.extend_from_slice(b"arg2\0"); //
305        correct.extend_from_slice(b"arg3\0"); //
306        correct.extend_from_slice(b"FOO=BAR\0"); // environ
307        correct.extend_from_slice(b"/data\0"); // namespace paths
308        correct.extend_from_slice(b"/pkg\0");
309
310        assert_eq!(read_bytes.len(), calculated_size);
311        assert_eq!(read_bytes, correct);
312
313        let read_koids: Vec<zx::Koid> =
314            read_handles.iter().map(|h| h.koid()).collect::<Result<_, _>>()?;
315        assert_eq!(read_koids, handle_koids);
316
317        Ok(())
318    }
319
320    #[test]
321    fn byte_limit() -> Result<(), Error> {
322        const LIMIT: usize = zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize;
323        const ARG_LIMIT: usize = LIMIT - 1 - mem::size_of::<MessageHeader>();
324
325        let (chan_wr, chan_rd) = zx::Channel::create();
326        let mut read_buf = zx::MessageBuf::new();
327
328        let make_bytes = iter::repeat_with(|| b'a');
329        let arg: CString = CString::new(make_bytes.take(ARG_LIMIT).collect::<Vec<u8>>())?;
330        let config = MessageContents { args: vec![arg], ..Default::default() };
331
332        // Should succeed at limit.
333        Message::build(config)?.write(&chan_wr)?;
334        chan_rd.read(&mut read_buf)?;
335        assert_eq!(read_buf.bytes().len(), LIMIT);
336
337        // Should fail to build just over limit.
338        let arg2: CString = CString::new(make_bytes.take(ARG_LIMIT + 1).collect::<Vec<u8>>())?;
339        let config2 = MessageContents { args: vec![arg2], ..Default::default() };
340        let result = Message::build(config2);
341        match result {
342            Err(ProcessargsError::SizeTooLarge(_)) => {}
343            Err(err) => {
344                panic!("Unexpected error type: {}", err);
345            }
346            Ok(_) => {
347                panic!("build message unexpectedly succeeded with too large argument");
348            }
349        }
350        Ok(())
351    }
352
353    #[test]
354    fn handle_limit() -> Result<(), Error> {
355        const LIMIT: usize = zx::sys::ZX_CHANNEL_MAX_MSG_HANDLES as usize;
356
357        let make_handles = iter::repeat_with(|| StartupHandle {
358            handle: zx::Vmo::create(1).expect("Failed to create VMO").into_handle(),
359            info: HandleInfo::new(HandleType::User1, 0),
360        });
361        let handles: Vec<StartupHandle> = make_handles.take(LIMIT).collect();
362
363        let config = MessageContents { handles, ..Default::default() };
364
365        // Should succeed at limit.
366        let (chan_wr, chan_rd) = zx::Channel::create();
367        Message::build(config)?.write(&chan_wr)?;
368        let mut read_buf = zx::MessageBuf::new();
369        chan_rd.read(&mut read_buf)?;
370        assert_eq!(read_buf.n_handles(), LIMIT);
371
372        // Should fail to build with one more handle.
373        let handles2: Vec<StartupHandle> = make_handles.take(LIMIT + 1).collect();
374        let config2 = MessageContents { handles: handles2, ..Default::default() };
375        let result = Message::build(config2);
376        match result {
377            Err(ProcessargsError::TooManyHandles(_)) => {}
378            Err(err) => {
379                panic!("Unexpected error type: {}", err);
380            }
381            Ok(_) => {
382                panic!("build message unexpectedly succeeded with too many handles");
383            }
384        }
385        Ok(())
386    }
387}