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::Handle,
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::Handle>,
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    use zx::{AsHandleRef, HandleBased};
247
248    #[test]
249    fn build_and_write_message() -> Result<(), Error> {
250        // We need some dummy handles to use in the message below, since you can't send an invalid
251        // handle in a channel. We just use VMOs since they're easy to create, even though they're
252        // not semantically valid for a processargs handle type like PA_NS_DIR.
253        let (dum0, dum1, dum2) = (zx::Vmo::create(1)?, zx::Vmo::create(1)?, zx::Vmo::create(1)?);
254        let handles = vec![
255            StartupHandle {
256                handle: dum0.into_handle(),
257                info: HandleInfo::new(HandleType::User1, 0x1234),
258            },
259            StartupHandle {
260                handle: dum1.into_handle(),
261                info: HandleInfo::new(HandleType::NamespaceDirectory, 0),
262            },
263            StartupHandle {
264                handle: dum2.into_handle(),
265                info: HandleInfo::new(HandleType::NamespaceDirectory, 1),
266            },
267        ];
268        let handle_koids: Vec<zx::Koid> =
269            handles.iter().map(|h| h.handle.get_koid()).collect::<Result<_, _>>()?;
270
271        let config = MessageContents {
272            args: vec![CString::new("arg1")?, CString::new("arg2")?, CString::new("arg3")?],
273            environment_vars: vec![CString::new("FOO=BAR")?],
274            namespace_paths: vec![CString::new("/data")?, CString::new("/pkg")?],
275            handles,
276        };
277
278        let calculated_size = Message::calculate_size(&config)?;
279        let message = Message::build(config)?;
280        assert_eq!(calculated_size, message.bytes.len());
281
282        // Write the message into a channel, read it back from the other end.
283        let (chan_wr, chan_rd) = zx::Channel::create();
284        message.write(&chan_wr)?;
285        let mut read_buf = zx::MessageBuf::new();
286        chan_rd.read(&mut read_buf)?;
287        let (read_bytes, read_handles) = read_buf.split();
288
289        // concat! doesn't work for byte literals and there's no concat_bytes! (yet), so we just
290        // build this in a Vec instead since it's a test.
291        let mut correct = Vec::new();
292        correct.extend_from_slice(b"\x5d\x58\x50\x41"); // protocol
293        correct.extend_from_slice(b"\x00\x10\x00\x00"); // version
294        correct.extend_from_slice(b"\x24\x00\x00\x00"); // handle_info_off
295        correct.extend_from_slice(b"\x30\x00\x00\x00"); // args_off
296        correct.extend_from_slice(b"\x03\x00\x00\x00"); // args_num
297        correct.extend_from_slice(b"\x3F\x00\x00\x00"); // environ_off
298        correct.extend_from_slice(b"\x01\x00\x00\x00"); // environ_num
299        correct.extend_from_slice(b"\x47\x00\x00\x00"); // names_off
300        correct.extend_from_slice(b"\x02\x00\x00\x00"); // names_num
301        correct.extend_from_slice(b"\xF1\x00\x34\x12"); // handle info
302        correct.extend_from_slice(b"\x20\x00\x00\x00"); //
303        correct.extend_from_slice(b"\x20\x00\x01\x00"); //
304        correct.extend_from_slice(b"arg1\0"); // args
305        correct.extend_from_slice(b"arg2\0"); //
306        correct.extend_from_slice(b"arg3\0"); //
307        correct.extend_from_slice(b"FOO=BAR\0"); // environ
308        correct.extend_from_slice(b"/data\0"); // namespace paths
309        correct.extend_from_slice(b"/pkg\0");
310
311        assert_eq!(read_bytes.len(), calculated_size);
312        assert_eq!(read_bytes, correct);
313
314        let read_koids: Vec<zx::Koid> =
315            read_handles.iter().map(|h| h.get_koid()).collect::<Result<_, _>>()?;
316        assert_eq!(read_koids, handle_koids);
317
318        Ok(())
319    }
320
321    #[test]
322    fn byte_limit() -> Result<(), Error> {
323        const LIMIT: usize = zx::sys::ZX_CHANNEL_MAX_MSG_BYTES as usize;
324        const ARG_LIMIT: usize = LIMIT - 1 - mem::size_of::<MessageHeader>();
325
326        let (chan_wr, chan_rd) = zx::Channel::create();
327        let mut read_buf = zx::MessageBuf::new();
328
329        let make_bytes = iter::repeat_with(|| b'a');
330        let arg: CString = CString::new(make_bytes.take(ARG_LIMIT).collect::<Vec<u8>>())?;
331        let config = MessageContents { args: vec![arg], ..Default::default() };
332
333        // Should succeed at limit.
334        Message::build(config)?.write(&chan_wr)?;
335        chan_rd.read(&mut read_buf)?;
336        assert_eq!(read_buf.bytes().len(), LIMIT);
337
338        // Should fail to build just over limit.
339        let arg2: CString = CString::new(make_bytes.take(ARG_LIMIT + 1).collect::<Vec<u8>>())?;
340        let config2 = MessageContents { args: vec![arg2], ..Default::default() };
341        let result = Message::build(config2);
342        match result {
343            Err(ProcessargsError::SizeTooLarge(_)) => {}
344            Err(err) => {
345                panic!("Unexpected error type: {}", err);
346            }
347            Ok(_) => {
348                panic!("build message unexpectedly succeeded with too large argument");
349            }
350        }
351        Ok(())
352    }
353
354    #[test]
355    fn handle_limit() -> Result<(), Error> {
356        const LIMIT: usize = zx::sys::ZX_CHANNEL_MAX_MSG_HANDLES as usize;
357
358        let make_handles = iter::repeat_with(|| StartupHandle {
359            handle: zx::Vmo::create(1).expect("Failed to create VMO").into_handle(),
360            info: HandleInfo::new(HandleType::User1, 0),
361        });
362        let handles: Vec<StartupHandle> = make_handles.take(LIMIT).collect();
363
364        let config = MessageContents { handles, ..Default::default() };
365
366        // Should succeed at limit.
367        let (chan_wr, chan_rd) = zx::Channel::create();
368        Message::build(config)?.write(&chan_wr)?;
369        let mut read_buf = zx::MessageBuf::new();
370        chan_rd.read(&mut read_buf)?;
371        assert_eq!(read_buf.n_handles(), LIMIT);
372
373        // Should fail to build with one more handle.
374        let handles2: Vec<StartupHandle> = make_handles.take(LIMIT + 1).collect();
375        let config2 = MessageContents { handles: handles2, ..Default::default() };
376        let result = Message::build(config2);
377        match result {
378            Err(ProcessargsError::TooManyHandles(_)) => {}
379            Err(err) => {
380                panic!("Unexpected error type: {}", err);
381            }
382            Ok(_) => {
383                panic!("build message unexpectedly succeeded with too many handles");
384            }
385        }
386        Ok(())
387    }
388}