1use anyhow::{Error, anyhow, bail};
6use elf_parse::{Elf64Headers, SegmentType};
7use fidl_next::{Request, Responder, ServerEnd};
8use fidl_next_fuchsia_ldsvc as fuchsia_ldsvc;
9use fuchsia_bootfs::BootfsParser;
10use fuchsia_ldsvc::loader::{Clone, Config, LoadObject};
11use fuchsia_ldsvc::{Loader, LoaderLoadObjectResponse, LoaderServerHandler};
12use fuchsia_runtime::{HandleInfo, HandleType};
13use process_builder::elf_load::load_elf;
14use process_builder::{Message, MessageContents, StartupHandle, compute_initial_stack_pointer};
15use std::ffi::CString;
16use std::fmt::Write as _;
17use std::str;
18use zx::sys::{ZX_RSRC_SYSTEM_POWER_BASE, ZX_RSRC_SYSTEM_VMEX_BASE};
19use zx::{
20 Channel, DebugLog, Job, Name, NullableHandle, ObjectType, Process, ProcessOptions, Resource,
21 ResourceKind, Rights, Socket, Status, Vmar, VmarFlags, Vmo, VmoChildOptions,
22};
23use zx_libc::sanitizer::Log;
24
25const FDIO_FLAG_USE_FOR_STDIO: u16 = 0x8000;
28
29pub enum SystemLog {
31 DebugLog(DebugLog),
33 Socket(Socket),
35}
36
37impl SystemLog {
38 fn to_handle(&self, rights: Rights) -> Result<NullableHandle, Status> {
39 match self {
40 Self::DebugLog(h) => h.duplicate_handle(rights).map(|h| h.into_handle()),
41 Self::Socket(h) => h.duplicate_handle(rights).map(|h| h.into_handle()),
42 }
43 }
44
45 fn duplicate(&self, rights: Rights) -> Result<Self, Status> {
46 match self {
47 Self::DebugLog(h) => Ok(Self::DebugLog(h.duplicate_handle(rights)?)),
48 Self::Socket(h) => Ok(Self::Socket(h.duplicate_handle(rights)?)),
49 }
50 }
51}
52
53pub struct SystemHandles {
56 pub root_job: Job,
58 pub vmex_resource: Resource,
60 pub power_resource: Option<Resource>,
62 pub vdso_vmo: Vmo,
64 pub zbi_vmo: Vmo,
66 pub log: SystemLog,
68 pub startup_handles: Vec<StartupHandle>,
70}
71
72impl SystemHandles {
73 pub fn from_handles(handles: impl IntoIterator<Item = zx::HandleInfo>) -> Result<Self, Error> {
75 let mut root_job = None;
76 let mut vmex_resource = None;
77 let mut power_resource = None;
78 let mut vdso_vmo = None;
79 let mut zbi_vmo = None;
80 let mut log = None;
81
82 let mut startup_handles = Vec::new();
83 let mut vdso_count = 0u16;
84 let mut kernel_file_count = 0u16;
85
86 for zx::HandleInfo { object_type, handle, .. } in handles {
87 match (object_type, handle.get_name()) {
88 (ObjectType::JOB, Ok(name)) => {
89 if name == "root" {
90 root_job = Some(Job::from(handle));
91 }
92 }
93 (ObjectType::RESOURCE, Ok(name)) => {
94 let htype = if name == "mmio" {
95 HandleType::MmioResource
96 } else if name == "irq" {
97 HandleType::IrqResource
98 } else if name == "io_port" {
99 HandleType::IoportResource
100 } else if name == "smc" {
101 HandleType::SmcResource
102 } else if name == "system" {
103 let resource = handle.as_handle_ref().cast::<Resource>();
104 if vmex_resource.is_none() {
105 vmex_resource = resource
106 .create_child(
107 ResourceKind::SYSTEM,
108 None,
109 ZX_RSRC_SYSTEM_VMEX_BASE,
110 1,
111 b"vmex",
112 )
113 .ok();
114 }
115 if power_resource.is_none() {
116 power_resource = resource
117 .create_child(
118 ResourceKind::SYSTEM,
119 None,
120 ZX_RSRC_SYSTEM_POWER_BASE,
121 1,
122 b"power",
123 )
124 .ok();
125 }
126 HandleType::SystemResource
127 } else if name == "vmex" {
128 vmex_resource = Some(Resource::from(handle));
129 continue;
130 } else if name == "power" {
131 power_resource = Some(Resource::from(handle));
132 continue;
133 } else {
134 continue;
135 };
136 startup_handles.push(StartupHandle { handle, info: HandleInfo::new(htype, 0) });
137 }
138 (ObjectType::VMO, Ok(name)) => {
139 if handle.as_handle_ref().cast::<Vmo>().get_size() == Ok(0) {
141 continue;
142 }
143 if name == "zbi" {
144 zbi_vmo = Some(Vmo::from(handle));
145 } else if name.as_bstr().starts_with(b"vdso/") {
146 if vdso_vmo.is_none() {
147 vdso_vmo =
148 Some(Vmo::from(handle.duplicate_handle(Rights::SAME_RIGHTS)?));
149 }
150 startup_handles.push(StartupHandle {
151 handle,
152 info: HandleInfo::new(HandleType::VdsoVmo, vdso_count),
153 });
154 vdso_count += 1;
155 } else {
156 startup_handles.push(StartupHandle {
157 handle,
158 info: HandleInfo::new(HandleType::KernelFileVmo, kernel_file_count),
159 });
160 kernel_file_count += 1;
161 }
162 }
163 (ObjectType::DEBUGLOG, _) => {
164 log = Some(SystemLog::DebugLog(DebugLog::from(handle)));
165 }
166 (ObjectType::SOCKET, _) => {
167 log = Some(SystemLog::Socket(Socket::from(handle)));
168 }
169 _ => {}
170 }
171 }
172
173 let root_job = root_job.ok_or_else(|| anyhow!("Root job handle not found"))?;
174 let vmex_resource =
175 vmex_resource.ok_or_else(|| anyhow!("VMEX resource handle not found"))?;
176 let vdso_vmo = vdso_vmo.ok_or_else(|| anyhow!("vDSO VMO handle not found"))?;
177 let zbi_vmo = zbi_vmo.ok_or_else(|| anyhow!("ZBI VMO handle not found"))?;
178 let log = log.ok_or_else(|| anyhow!("DebugLog/Socket handle not found"))?;
179
180 Ok(SystemHandles {
181 root_job,
182 vmex_resource,
183 power_resource,
184 vdso_vmo,
185 zbi_vmo,
186 log,
187 startup_handles,
188 })
189 }
190
191 pub fn duplicate(&self) -> Result<Self, Status> {
193 Ok(Self {
194 root_job: self.root_job.duplicate_handle(Rights::SAME_RIGHTS)?,
195 vmex_resource: self.vmex_resource.duplicate_handle(Rights::SAME_RIGHTS)?,
196 power_resource: self
197 .power_resource
198 .as_ref()
199 .map(|p| p.duplicate_handle(Rights::SAME_RIGHTS))
200 .transpose()?,
201 vdso_vmo: self.vdso_vmo.duplicate_handle(Rights::SAME_RIGHTS)?,
202 zbi_vmo: self.zbi_vmo.duplicate_handle(Rights::SAME_RIGHTS)?,
203 log: self.log.duplicate(Rights::SAME_RIGHTS)?,
204 startup_handles: self
205 .startup_handles
206 .iter()
207 .map(|h| {
208 Ok(StartupHandle {
209 handle: h.handle.duplicate_handle(Rights::SAME_RIGHTS)?,
210 info: h.info,
211 })
212 })
213 .collect::<Result<Vec<_>, Status>>()?,
214 })
215 }
216}
217
218fn reserve_low_address_space(root_vmar: &Vmar) -> Result<Vmar, Error> {
221 let info = root_vmar.info()?;
222 let page_size = zx::system_get_page_size() as usize;
223 let reserve_len = (info.len / 2) & !(page_size - 1);
224 let (vmar, addr) = root_vmar.allocate(0, reserve_len, VmarFlags::SPECIFIC)?;
225 if addr != info.base {
226 bail!("zx_vmar_allocate gave wrong address for low address space reservation");
227 }
228 Ok(vmar)
229}
230
231fn create_bootfs_file_vmo(
234 bootfs_vmo: &Vmo,
235 offset: u64,
236 size: u64,
237 vmex_resource: &Resource,
238) -> Result<Vmo, Error> {
239 bootfs_vmo
240 .create_child(VmoChildOptions::SNAPSHOT_AT_LEAST_ON_WRITE, offset, size)?
241 .replace_as_executable(vmex_resource)
242 .map_err(Into::into)
243}
244
245pub async fn launch_program(
252 program_name: &str,
253 target_path: &str,
254 args: impl IntoIterator<Item = &str>,
255 bootfs_vmo: Vmo,
256 handles: SystemHandles,
257 mut bootfs_entries: Option<&mut Vec<(u32, Vmo)>>,
258 log: &mut Log,
259) -> Result<Process, Error> {
260 let SystemHandles {
261 root_job,
262 vmex_resource,
263 power_resource: _,
264 vdso_vmo,
265 zbi_vmo,
266 log: system_log,
267 startup_handles,
268 } = handles;
269
270 let (child_process, child_vmar) =
271 root_job.create_child_process(ProcessOptions::empty(), program_name.as_bytes())?;
272 let reserved_vmar = reserve_low_address_space(&child_vmar)?;
273 let child_thread = child_process.create_thread(program_name.as_bytes())?;
274
275 let bootfs_parser =
276 BootfsParser::create_from_vmo(bootfs_vmo.duplicate_handle(Rights::SAME_RIGHTS)?)?;
277
278 let entry = bootfs_parser
279 .zero_copy_iter()
280 .filter_map(Result::ok)
281 .find(|e| e.name == target_path)
282 .ok_or_else(|| anyhow!("Program '{}' not found in bootfs", target_path))?;
283
284 let program_vmo =
285 create_bootfs_file_vmo(&bootfs_vmo, entry.offset, entry.size, &vmex_resource)?;
286 program_vmo.set_name(&Name::new_lossy(program_name))?;
287
288 if let Some(ref mut bootfs_entries) = bootfs_entries {
289 if let Ok(vmo_dup) = program_vmo.duplicate_handle(Rights::SAME_RIGHTS) {
290 bootfs_entries.push((entry.offset as u32, vmo_dup));
291 }
292 }
293
294 let headers = Elf64Headers::from_vmo(&program_vmo)?;
295
296 let (to_child_client, to_child_server) = Channel::create();
297 let mut loader_server = None;
298
299 let entry_point = if let Some(interp_hdr) =
300 headers.program_header_with_type(SegmentType::Interp)?
301 {
302 let mut interp_bytes = vec![0u8; interp_hdr.filesz as usize];
303 program_vmo.read(&mut interp_bytes, interp_hdr.offset as u64)?;
304 let interp_str = str::from_utf8(&interp_bytes)?.trim_end_matches('\0');
305 let interp_path = if interp_str.starts_with('/') {
306 interp_str.trim_start_matches('/')
307 } else if !interp_str.starts_with("lib/") {
308 &format!("lib/{}", interp_str)
309 } else {
310 interp_str
311 };
312
313 let interp_entry = bootfs_parser
314 .zero_copy_iter()
315 .filter_map(Result::ok)
316 .find(|e| e.name == interp_path || e.name.ends_with(&format!("/{}", interp_path)))
317 .ok_or_else(|| anyhow!("Interpreter '{}' not found in bootfs", interp_path))?;
318
319 let interp_vmo = create_bootfs_file_vmo(
320 &bootfs_vmo,
321 interp_entry.offset,
322 interp_entry.size,
323 &vmex_resource,
324 )?;
325 interp_vmo.set_name(&Name::new_lossy(&interp_path))?;
326
327 if let Some(ref mut bootfs_entries) = bootfs_entries {
328 if let Ok(vmo_dup) = interp_vmo.duplicate_handle(Rights::SAME_RIGHTS) {
329 if !bootfs_entries.iter().any(|(off, _)| *off == interp_entry.offset as u32) {
330 bootfs_entries.push((interp_entry.offset as u32, vmo_dup));
331 }
332 }
333 }
334
335 let interp_headers = Elf64Headers::from_vmo(&interp_vmo)?;
336 let loaded_interp = load_elf(&interp_vmo, &interp_headers, &child_vmar)?;
337
338 let (ldsvc_client, ldsvc_server) = Channel::create();
339 loader_server = Some(ldsvc_server);
340
341 let handles = vec![
342 StartupHandle {
343 handle: program_vmo.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle().into(),
344 info: HandleInfo::new(HandleType::ExecutableVmo, 0),
345 },
346 StartupHandle {
347 handle: system_log.to_handle(Rights::SAME_RIGHTS)?,
348 info: HandleInfo::new(HandleType::FileDescriptor, FDIO_FLAG_USE_FOR_STDIO),
349 },
350 StartupHandle {
351 handle: child_process.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle().into(),
352 info: HandleInfo::new(HandleType::ProcessSelf, 0),
353 },
354 StartupHandle {
355 handle: child_vmar.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle().into(),
356 info: HandleInfo::new(HandleType::RootVmar, 0),
357 },
358 StartupHandle {
359 handle: loaded_interp.vmar.into_handle().into(),
360 info: HandleInfo::new(HandleType::LoadedVmar, 0),
361 },
362 StartupHandle {
363 handle: child_thread.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle().into(),
364 info: HandleInfo::new(HandleType::ThreadSelf, 0),
365 },
366 StartupHandle {
367 handle: ldsvc_client.into_handle().into(),
368 info: HandleInfo::new(HandleType::LdsvcLoader, 0),
369 },
370 ];
371
372 let contents = MessageContents {
373 environment_vars: vec![CString::new("LD_DEBUG=1")?],
376 handles,
377 ..Default::default()
378 };
379
380 let message = Message::build(contents)?;
381 message.write(&to_child_client)?;
382 loaded_interp.entry
383 } else {
384 let loaded_elf = load_elf(&program_vmo, &headers, &child_vmar)?;
385 let mut initial_handles = vec![
386 system_log.to_handle(Rights::SAME_RIGHTS)?,
387 child_process.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle(),
388 child_vmar.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle(),
389 loaded_elf.vmar.into_handle(),
390 child_thread.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle(),
391 ];
392 to_child_client.write(&[], &mut initial_handles)?;
393 loaded_elf.entry
394 };
395
396 let vdso_headers = Elf64Headers::from_vmo(&vdso_vmo)?;
397 let loaded_vdso = load_elf(&vdso_vmo, &vdso_headers, &child_vmar)?;
398 let vdso_base = loaded_vdso.vmar_base;
399
400 let stack_size: usize = 256 * 1024;
401 let stack_vmo = Vmo::create(stack_size as u64)?;
402 stack_vmo.set_name(&Name::new_lossy("userboot-child-initial-stack"))?;
403 let stack_base = child_vmar.map(
404 0,
405 &stack_vmo,
406 0,
407 stack_size,
408 VmarFlags::PERM_READ | VmarFlags::PERM_WRITE,
409 )?;
410 let sp = compute_initial_stack_pointer(stack_base, stack_size);
411
412 let mut handles = vec![
413 StartupHandle {
414 handle: child_process.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle().into(),
415 info: HandleInfo::new(HandleType::ProcessSelf, 0),
416 },
417 StartupHandle {
418 handle: child_vmar.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle().into(),
419 info: HandleInfo::new(HandleType::RootVmar, 0),
420 },
421 StartupHandle {
422 handle: child_thread.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle().into(),
423 info: HandleInfo::new(HandleType::ThreadSelf, 0),
424 },
425 StartupHandle {
426 handle: root_job.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle().into(),
427 info: HandleInfo::new(HandleType::DefaultJob, 0),
428 },
429 StartupHandle {
430 handle: zbi_vmo.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle().into(),
431 info: HandleInfo::new(HandleType::BootdataVmo, 0),
432 },
433 StartupHandle {
434 handle: bootfs_vmo.duplicate_handle(Rights::SAME_RIGHTS)?.into_handle().into(),
435 info: HandleInfo::new(HandleType::BootfsVmo, 0),
436 },
437 StartupHandle {
438 handle: system_log.to_handle(Rights::SAME_RIGHTS)?,
439 info: HandleInfo::new(HandleType::FileDescriptor, FDIO_FLAG_USE_FOR_STDIO),
440 },
441 ];
442 handles.extend(startup_handles);
443
444 let contents = MessageContents {
445 args: args.into_iter().map(CString::new).collect::<Result<Vec<_>, _>>()?,
446 handles,
447 namespace_paths: vec![CString::new("/svc")?],
448 ..Default::default()
449 };
450
451 let message = Message::build(contents)?;
452 message.write(&to_child_client)?;
453
454 unsafe {
461 reserved_vmar.destroy()?;
462 }
463
464 child_process.start(&child_thread, entry_point, sp, to_child_server.into(), vdso_base)?;
465 writeln!(log, "Started child process: {}", program_name)?;
466
467 if let Some(ldsvc_server) = loader_server {
468 writeln!(log, "Serving loader service for child process...")?;
469 let bootfs_parser =
470 BootfsParser::create_from_vmo(bootfs_vmo.duplicate_handle(Rights::SAME_RIGHTS)?)?;
471 let server = UserbootLoaderServer::new(
472 bootfs_parser,
473 bootfs_vmo,
474 vmex_resource,
475 bootfs_entries.is_some(),
476 );
477 if let Ok(Ok(server)) =
478 ServerEnd::<Loader, Channel>::from_untyped(ldsvc_server).spawn(server).await
479 {
480 if let (Some(bootfs_entries), Some(mut server_entries)) =
481 (bootfs_entries, server.entries)
482 {
483 bootfs_entries.append(&mut server_entries);
484 }
485 }
486 }
487
488 Ok(child_process)
489}
490
491struct UserbootLoaderServer {
493 bootfs_parser: BootfsParser,
495 bootfs_vmo: Vmo,
497 vmex_resource: Resource,
499 entries: Option<Vec<(u32, Vmo)>>,
501 subdir: String,
503 exclusive: bool,
505}
506
507impl UserbootLoaderServer {
508 fn new(
509 bootfs_parser: BootfsParser,
510 bootfs_vmo: Vmo,
511 vmex_resource: Resource,
512 collect_entries: bool,
513 ) -> Self {
514 Self {
515 bootfs_parser,
516 bootfs_vmo,
517 vmex_resource,
518 entries: collect_entries.then(Vec::new),
519 subdir: String::new(),
520 exclusive: false,
521 }
522 }
523}
524
525impl LoaderServerHandler<Channel> for UserbootLoaderServer {
526 async fn done(&mut self) {}
528
529 async fn load_object(
531 &mut self,
532 request: Request<LoadObject, Channel>,
533 responder: Responder<LoadObject, Channel>,
534 ) {
535 let name = &request.payload().object_name;
536 let find_file = |path: &str| {
537 self.bootfs_parser
538 .zero_copy_iter()
539 .filter_map(Result::ok)
540 .find(|e| e.name == path || e.name.ends_with(&format!("/{path}")))
541 };
542
543 let mut found = None;
544 if !self.subdir.is_empty() {
545 found = find_file(&format!("lib/{}/{}", self.subdir, name));
546 }
547 if found.is_none() && (!self.exclusive || self.subdir.is_empty()) {
548 found = find_file(&format!("lib/{name}"));
549 }
550
551 let lib_vmo = found.as_ref().and_then(|entry| {
552 create_bootfs_file_vmo(&self.bootfs_vmo, entry.offset, entry.size, &self.vmex_resource)
553 .ok()
554 });
555
556 if let (Some(entries), Some(entry), Some(vmo)) = (&mut self.entries, &found, &lib_vmo) {
557 let offset = entry.offset as u32;
558 if !entries.iter().any(|(off, _)| *off == offset) {
559 if let Ok(vmo_dup) = vmo.duplicate_handle(Rights::SAME_RIGHTS) {
560 entries.push((offset, vmo_dup));
561 }
562 }
563 }
564
565 let (rv, object) = lib_vmo.map_or((Status::NOT_FOUND, None), |v| (Status::OK, Some(v)));
566 let _ = responder.respond(LoaderLoadObjectResponse { rv, object }).await;
567 }
568
569 async fn config(
571 &mut self,
572 request: Request<Config, Channel>,
573 responder: Responder<Config, Channel>,
574 ) {
575 let payload = request.payload();
576 let config_str = payload.config.as_str();
577 self.exclusive = config_str.ends_with('!');
578 self.subdir = config_str.strip_suffix('!').unwrap_or(config_str).to_string();
579 let _ = responder.respond(Status::OK).await;
580 }
581
582 async fn clone(
584 &mut self,
585 _request: Request<Clone, Channel>,
586 responder: Responder<Clone, Channel>,
587 ) {
588 let _ = responder.respond(Status::NOT_SUPPORTED).await;
589 }
590}
591
592#[cfg(test)]
593mod tests {
594 use super::*;
595 use elf_parse::{
596 CURRENT_ARCH, ELF_MAGIC, Elf64FileHeader, Elf64ProgramHeader, ElfClass, ElfIdent, ElfType,
597 ElfVersion, NATIVE_ENCODING, SegmentFlags,
598 };
599 use fidl_fuchsia_kernel::{PowerResourceMarker, VmexResourceMarker};
600 use fuchsia_bootfs::bootfs::ZBI_BOOTFS_MAGIC;
601 use fuchsia_component::client::connect_to_protocol;
602 use fuchsia_runtime::{job_default, take_startup_handle};
603 use std::mem::size_of;
604 use std::sync::LazyLock;
605 use zerocopy::IntoBytes;
606
607 static VDSO_VMO: LazyLock<Vmo> = LazyLock::new(|| {
608 take_startup_handle(HandleInfo::new(HandleType::VdsoVmo, 0))
609 .map(Vmo::from)
610 .expect("expected real vDSO VMO handle from process startup handles")
611 });
612
613 async fn create_test_system_handles() -> SystemHandles {
614 let root_job = job_default().duplicate_handle(Rights::SAME_RIGHTS).unwrap();
615 let vmex_resource = connect_to_protocol::<VmexResourceMarker>()
616 .expect("failed to connect to VmexResource protocol")
617 .get()
618 .await
619 .expect("failed to get VmexResource");
620 let power_resource = connect_to_protocol::<PowerResourceMarker>()
621 .expect("failed to connect to PowerResource protocol")
622 .get()
623 .await
624 .expect("failed to get PowerResource");
625 let vdso_vmo = VDSO_VMO.duplicate_handle(Rights::SAME_RIGHTS).unwrap();
626 let zbi_vmo = Vmo::create(4096).unwrap();
627 let (s1, _s2) = zx::Socket::create_stream();
628 let log = SystemLog::Socket(s1);
629 let startup_handles = vec![];
630
631 SystemHandles {
632 root_job,
633 vmex_resource,
634 power_resource: Some(power_resource),
635 vdso_vmo,
636 zbi_vmo,
637 log,
638 startup_handles,
639 }
640 }
641
642 fn create_bootfs_vmo(files: &[(&str, &[u8])]) -> Vmo {
643 let mut total_dirsize = 0u32;
644 for (name, _) in files {
645 let name_len = (name.len() + 1) as u32;
646 let dirent_size = (12 + name_len + 3) & !3;
647 total_dirsize += dirent_size;
648 }
649
650 let data_start_offset = ((16 + total_dirsize + 4095) & !4095) as u64;
651 let mut vmo_bytes = vec![0u8; data_start_offset as usize];
652
653 vmo_bytes[0..4].copy_from_slice(&ZBI_BOOTFS_MAGIC.to_le_bytes());
655 vmo_bytes[4..8].copy_from_slice(&total_dirsize.to_le_bytes());
656
657 let mut current_dir_off = 16usize;
658 let mut current_data_off = data_start_offset;
659
660 for (name, data) in files {
661 let name_c = format!("{}\0", name);
662 let name_bytes = name_c.as_bytes();
663 let name_len = name_bytes.len() as u32;
664 let data_len = data.len() as u32;
665
666 vmo_bytes[current_dir_off..current_dir_off + 4]
667 .copy_from_slice(&name_len.to_le_bytes());
668 vmo_bytes[current_dir_off + 4..current_dir_off + 8]
669 .copy_from_slice(&data_len.to_le_bytes());
670 vmo_bytes[current_dir_off + 8..current_dir_off + 12]
671 .copy_from_slice(&(current_data_off as u32).to_le_bytes());
672 vmo_bytes[current_dir_off + 12..current_dir_off + 12 + name_bytes.len()]
673 .copy_from_slice(name_bytes);
674
675 let dirent_size = ((12 + name_len + 3) & !3) as usize;
676 current_dir_off += dirent_size;
677
678 if vmo_bytes.len() < (current_data_off as usize + data.len()) {
679 vmo_bytes.resize(current_data_off as usize + data.len(), 0);
680 }
681 vmo_bytes[current_data_off as usize..current_data_off as usize + data.len()]
682 .copy_from_slice(data);
683
684 let page_aligned_data_len = ((data.len() + 4095) & !4095) as u64;
685 current_data_off += page_aligned_data_len.max(4096);
686 }
687
688 let vmo = Vmo::create(vmo_bytes.len() as u64).unwrap();
689 vmo.write(&vmo_bytes, 0).unwrap();
690 vmo
691 }
692
693 fn create_elf_bytes(interp_path: Option<&str>) -> Vec<u8> {
695 let has_interp = interp_path.is_some();
696 let phnum = if has_interp { 2 } else { 1 };
697 let ehsize = size_of::<Elf64FileHeader>() as u16;
698 let phentsize = size_of::<Elf64ProgramHeader>() as u16;
699
700 let interp_str_bytes = interp_path.map(|p| format!("{}\0", p)).unwrap_or_default();
701 let interp_len = interp_str_bytes.len();
702
703 let phoff = ehsize as usize;
704 let interp_offset = phoff + (phnum as usize) * (phentsize as usize);
705
706 let file_header = Elf64FileHeader {
707 ident: ElfIdent {
708 magic: ELF_MAGIC,
709 class: ElfClass::Elf64 as u8,
710 data: NATIVE_ENCODING as u8,
711 version: ElfVersion::Current as u8,
712 osabi: 0,
713 abiversion: 0,
714 pad: [0; 7],
715 },
716 elf_type: ElfType::Executable as u16,
717 machine: CURRENT_ARCH as u16,
718 version: 1,
719 entry: 0x1000,
720 phoff,
721 shoff: 0,
722 flags: 0,
723 ehsize,
724 phentsize,
725 phnum,
726 shentsize: 0,
727 shnum: 0,
728 shstrndx: 0,
729 };
730
731 let mut bytes = Vec::new();
732 bytes.extend_from_slice(file_header.as_bytes());
733
734 let load_phdr = Elf64ProgramHeader {
736 segment_type: SegmentType::Load as u32,
737 flags: (SegmentFlags::READ | SegmentFlags::EXECUTE).bits(),
738 offset: 0,
739 vaddr: 0x1000,
740 paddr: 0x1000,
741 filesz: 4096,
742 memsz: 4096,
743 align: 4096,
744 };
745 bytes.extend_from_slice(load_phdr.as_bytes());
746
747 if let Some(_) = interp_path {
748 let interp_phdr = Elf64ProgramHeader {
749 segment_type: SegmentType::Interp as u32,
750 flags: SegmentFlags::READ.bits(),
751 offset: interp_offset,
752 vaddr: 0,
753 paddr: 0,
754 filesz: interp_len as u64,
755 memsz: interp_len as u64,
756 align: 1,
757 };
758 bytes.extend_from_slice(interp_phdr.as_bytes());
759 bytes.extend_from_slice(interp_str_bytes.as_bytes());
760 }
761
762 if bytes.len() < 4096 {
763 bytes.resize(4096, 0);
764 }
765
766 bytes
767 }
768
769 #[fuchsia::test]
770 async fn test_launch_program_invalid_bootfs() {
771 let handles = create_test_system_handles().await;
772 let invalid_bootfs = Vmo::create(4096).unwrap();
773 let mut log = Log::new();
774
775 let res = launch_program(
776 "test_prog",
777 "bin/test_prog",
778 ["arg1"],
779 invalid_bootfs,
780 handles,
781 None,
782 &mut log,
783 )
784 .await;
785
786 assert!(res.is_err());
787 }
788
789 #[fuchsia::test]
790 async fn test_launch_program_target_not_in_bootfs() {
791 let handles = create_test_system_handles().await;
792 let bootfs = create_bootfs_vmo(&[("bin/other", b"some_data")]);
793 let mut log = Log::new();
794
795 let res =
796 launch_program("test_prog", "bin/test_prog", ["arg1"], bootfs, handles, None, &mut log)
797 .await;
798
799 assert!(res.is_err());
800 let err_msg = res.unwrap_err().to_string();
801 assert!(
802 err_msg.contains("Program 'bin/test_prog' not found in bootfs"),
803 "unexpected err: {err_msg}"
804 );
805 }
806
807 #[fuchsia::test]
808 async fn test_launch_program_corrupt_elf() {
809 let handles = create_test_system_handles().await;
810 let bootfs = create_bootfs_vmo(&[("bin/test_prog", b"NOT_AN_ELF_BINARY")]);
811 let mut log = Log::new();
812
813 let res =
814 launch_program("test_prog", "bin/test_prog", [], bootfs, handles, None, &mut log).await;
815
816 assert!(res.is_err());
817 }
818
819 #[fuchsia::test]
820 async fn test_launch_program_missing_interpreter() {
821 let handles = create_test_system_handles().await;
822 let elf_data = create_elf_bytes(Some("lib/ld.so.1"));
823 let bootfs = create_bootfs_vmo(&[("bin/test_prog", &elf_data)]);
824 let mut log = Log::new();
825
826 let res = launch_program(
827 "test_prog",
828 "bin/test_prog",
829 ["--flag"],
830 bootfs,
831 handles,
832 None,
833 &mut log,
834 )
835 .await;
836
837 assert!(res.is_err());
838 let err_msg = res.unwrap_err().to_string();
839 assert!(
840 err_msg.contains("Interpreter 'lib/ld.so.1' not found in bootfs"),
841 "unexpected err: {err_msg}"
842 );
843 }
844
845 #[fuchsia::test]
846 async fn test_launch_program_interpreter_path_normalization() {
847 let handles = create_test_system_handles().await;
848 let elf_data = create_elf_bytes(Some("/lib/ld.so.1"));
850 let bootfs = create_bootfs_vmo(&[("bin/test_prog", &elf_data)]);
851 let mut log = Log::new();
852
853 let res =
854 launch_program("test_prog", "bin/test_prog", [], bootfs, handles, None, &mut log).await;
855
856 assert!(res.is_err());
857 let err_msg = res.unwrap_err().to_string();
858 assert!(
859 err_msg.contains("Interpreter 'lib/ld.so.1' not found in bootfs"),
860 "unexpected err: {err_msg}"
861 );
862
863 let handles2 = create_test_system_handles().await;
865 let elf_data2 = create_elf_bytes(Some("ld.so.1"));
866 let bootfs2 = create_bootfs_vmo(&[("bin/test_prog", &elf_data2)]);
867 let mut log2 = Log::new();
868
869 let res2 =
870 launch_program("test_prog", "bin/test_prog", [], bootfs2, handles2, None, &mut log2)
871 .await;
872
873 assert!(res2.is_err());
874 let err_msg2 = res2.unwrap_err().to_string();
875 assert!(
876 err_msg2.contains("Interpreter 'lib/ld.so.1' not found in bootfs"),
877 "unexpected err: {err_msg2}"
878 );
879 }
880
881 #[fuchsia::test]
882 async fn test_launch_program_success() {
883 let handles = create_test_system_handles().await;
884 let elf_data = create_elf_bytes(None);
885 let bootfs = create_bootfs_vmo(&[("bin/test_prog", &elf_data)]);
886 let mut log = Log::new();
887
888 let mut bootfs_entries = Vec::new();
889 let res = launch_program(
890 "test_prog",
891 "bin/test_prog",
892 ["arg1", "arg2"],
893 bootfs,
894 handles,
895 Some(&mut bootfs_entries),
896 &mut log,
897 )
898 .await;
899
900 assert!(res.is_ok(), "expected launch_program to succeed, got: {:?}", res);
901 assert!(!bootfs_entries.is_empty());
902 }
903
904 #[fuchsia::test]
905 async fn test_launch_program_arg_with_null_byte() {
906 let handles = create_test_system_handles().await;
907 let elf_data = create_elf_bytes(None);
908 let bootfs = create_bootfs_vmo(&[("bin/test_prog", &elf_data)]);
909 let mut log = Log::new();
910
911 let res = launch_program(
912 "test_prog",
913 "bin/test_prog",
914 ["arg1\0with_null"],
915 bootfs,
916 handles,
917 None,
918 &mut log,
919 )
920 .await;
921
922 assert!(res.is_err());
923 }
924
925 #[fuchsia::test]
926 async fn test_system_handles_success() {
927 let root_job = job_default().duplicate_handle(Rights::SAME_RIGHTS).unwrap();
928 root_job.set_name(&Name::new("root").unwrap()).unwrap();
929 let root_job_info =
930 zx::HandleInfo::new(root_job.into_handle(), ObjectType::JOB, Rights::SAME_RIGHTS);
931
932 let vmex = connect_to_protocol::<VmexResourceMarker>().unwrap().get().await.unwrap();
933 let vmex_info =
934 zx::HandleInfo::new(vmex.into_handle(), ObjectType::RESOURCE, Rights::SAME_RIGHTS);
935
936 let power = connect_to_protocol::<PowerResourceMarker>().unwrap().get().await.unwrap();
937 let power_info =
938 zx::HandleInfo::new(power.into_handle(), ObjectType::RESOURCE, Rights::SAME_RIGHTS);
939
940 let vdso = VDSO_VMO.duplicate_handle(Rights::SAME_RIGHTS).unwrap();
941 vdso.set_name(&Name::new("vdso/stable").unwrap()).unwrap();
942 let vdso_info =
943 zx::HandleInfo::new(vdso.into_handle(), ObjectType::VMO, Rights::SAME_RIGHTS);
944
945 let zbi = Vmo::create(4096).unwrap();
946 zbi.set_name(&Name::new("zbi").unwrap()).unwrap();
947 let zbi_info = zx::HandleInfo::new(zbi.into_handle(), ObjectType::VMO, Rights::SAME_RIGHTS);
948
949 let (s1, _s2) = zx::Socket::create_stream();
950 let log_info =
951 zx::HandleInfo::new(s1.into_handle(), ObjectType::SOCKET, Rights::SAME_RIGHTS);
952
953 let zero_vmo = Vmo::create(0).unwrap();
954 zero_vmo.set_name(&Name::new("zero").unwrap()).unwrap();
955 let zero_info =
956 zx::HandleInfo::new(zero_vmo.into_handle(), ObjectType::VMO, Rights::SAME_RIGHTS);
957
958 let kfile_vmo = Vmo::create(4096).unwrap();
959 kfile_vmo.set_name(&Name::new("kernel_file").unwrap()).unwrap();
960 let kfile_info =
961 zx::HandleInfo::new(kfile_vmo.into_handle(), ObjectType::VMO, Rights::SAME_RIGHTS);
962
963 let res = SystemHandles::from_handles(vec![
964 root_job_info,
965 vmex_info,
966 power_info,
967 vdso_info,
968 zbi_info,
969 log_info,
970 zero_info,
971 kfile_info,
972 ]);
973 let handles = res.expect("expected SystemHandles::from_handles to succeed");
974 assert_eq!(handles.root_job.get_name().unwrap(), "root");
975 assert_eq!(handles.vdso_vmo.get_name().unwrap(), "vdso/stable");
976 assert_eq!(handles.zbi_vmo.get_name().unwrap(), "zbi");
977 assert_eq!(handles.startup_handles.len(), 2);
978 }
979}