1use fuchsia_runtime::HandleInfo;
8
9use std::ffi::CString;
10use std::{fmt, mem, num};
11use thiserror::Error;
12use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout};
13
14#[derive(Error, Debug)]
16pub enum ProcessargsError {
17 TryFromInt(num::TryFromIntError),
18 SizeTooLarge(usize),
19 TooManyHandles(usize),
20}
21
22impl ProcessargsError {
23 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
33impl 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#[derive(KnownLayout, FromBytes, IntoBytes, Immutable, Default)]
61#[repr(C)]
62pub(crate) struct MessageHeader {
63 pub protocol: u32,
66 pub version: u32,
67
68 pub handle_info_off: u32,
71
72 pub args_off: u32,
75 pub args_num: u32,
76
77 pub environ_off: u32,
81 pub environ_num: u32,
82
83 names_off: u32,
90 names_num: u32,
91}
92
93pub struct StartupHandle {
98 pub handle: zx::Handle,
100
101 pub info: HandleInfo,
103}
104
105#[derive(Default)]
108pub struct MessageContents {
109 pub args: Vec<CString>,
111
112 pub environment_vars: Vec<CString>,
114
115 pub namespace_paths: Vec<CString>,
117
118 pub handles: Vec<StartupHandle>,
120}
121
122pub struct Message {
130 bytes: Vec<u8>,
131 handles: Vec<zx::Handle>,
132}
133
134type HandleInfoRaw = u32;
136
137impl Message {
138 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 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 assert!(data.len() == size);
175 Ok(Message { bytes: data, handles })
176 }
177
178 pub fn calculate_size(contents: &MessageContents) -> Result<usize, ProcessargsError> {
183 let (_, size) = Self::build_header(contents)?;
184 Ok(size)
185 }
186
187 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 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 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 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 let mut correct = Vec::new();
292 correct.extend_from_slice(b"\x5d\x58\x50\x41"); correct.extend_from_slice(b"\x00\x10\x00\x00"); correct.extend_from_slice(b"\x24\x00\x00\x00"); correct.extend_from_slice(b"\x30\x00\x00\x00"); correct.extend_from_slice(b"\x03\x00\x00\x00"); correct.extend_from_slice(b"\x3F\x00\x00\x00"); correct.extend_from_slice(b"\x01\x00\x00\x00"); correct.extend_from_slice(b"\x47\x00\x00\x00"); correct.extend_from_slice(b"\x02\x00\x00\x00"); correct.extend_from_slice(b"\xF1\x00\x34\x12"); correct.extend_from_slice(b"\x20\x00\x00\x00"); correct.extend_from_slice(b"\x20\x00\x01\x00"); correct.extend_from_slice(b"arg1\0"); correct.extend_from_slice(b"arg2\0"); correct.extend_from_slice(b"arg3\0"); correct.extend_from_slice(b"FOO=BAR\0"); correct.extend_from_slice(b"/data\0"); 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 Message::build(config)?.write(&chan_wr)?;
335 chan_rd.read(&mut read_buf)?;
336 assert_eq!(read_buf.bytes().len(), LIMIT);
337
338 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 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 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}