microvms_domain/
provision.rs1use crate::error::{Error, ErrorKind};
9
10pub const REQUIRED_ELF_MACHINE: u16 = 0xB7;
12
13pub fn elf_machine(bytes: &[u8]) -> Option<u16> {
20 let header = bytes.get(..20)?;
21 if &header[..4] != b"\x7fELF" {
22 return None;
23 }
24 let field = [header[18], header[19]];
25 Some(if header[5] == 1 {
26 u16::from_le_bytes(field)
27 } else {
28 u16::from_be_bytes(field)
29 })
30}
31
32pub fn not_aarch64(bytes: &[u8]) -> Option<String> {
34 match elf_machine(bytes) {
35 Some(REQUIRED_ELF_MACHINE) => None,
36 Some(machine) => Some(format!(
37 "ELF machine 0x{machine:x}, not aarch64 (0x{REQUIRED_ELF_MACHINE:x})"
38 )),
39 None => Some("not an ELF binary at all".to_string()),
40 }
41}
42
43pub fn require_aarch64(bytes: &[u8]) -> Result<(), Error> {
49 match not_aarch64(bytes) {
50 None => Ok(()),
51 Some(why) => Err(Error::new(
52 ErrorKind::Precondition,
53 format!(
54 "the daemon binary is {why}. MicroVMs are ARM64-only, and a wrong-architecture \
55 daemon fails as a run-hook timeout 45 minutes into a build.\n - build one: \
56 cargo build --release -p agentd --target aarch64-unknown-linux-musl"
57 ),
58 )),
59 }
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 fn elf_header(machine: u16) -> Vec<u8> {
67 let mut header = vec![0u8; 20];
68 header[..4].copy_from_slice(b"\x7fELF");
69 header[5] = 1;
70 header[18..20].copy_from_slice(&machine.to_le_bytes());
71 header
72 }
73
74 #[test]
76 fn the_elf_header_is_read_in_its_own_byte_order() {
77 assert_eq!(elf_machine(&elf_header(0xB7)), Some(0xB7));
78 let mut big = elf_header(0);
79 big[5] = 2;
80 big[18..20].copy_from_slice(&0xB7u16.to_be_bytes());
81 assert_eq!(elf_machine(&big), Some(0xB7));
82 assert_eq!(elf_machine(b"#!/bin/sh"), None);
83 assert_eq!(elf_machine(&elf_header(0xB7)[..19]), None);
84 assert_eq!(not_aarch64(&elf_header(0xB7)), None);
85 }
86
87 #[test]
88 fn a_non_aarch64_binary_is_refused_with_its_machine() {
89 assert!(not_aarch64(&elf_header(0x3E)).is_some_and(|why| why.contains("0x3e")));
90 assert!(not_aarch64(b"#!/bin/sh").is_some_and(|why| why.contains("not an ELF")));
91 }
92
93 #[test]
96 fn an_images_daemon_must_be_an_aarch64_elf() {
97 assert!(require_aarch64(&elf_header(0xB7)).is_ok());
98 for (bytes, why) in [
99 (elf_header(0x3E), "ELF machine 0x3e, not aarch64 (0xb7)"),
100 (b"#!/bin/sh".to_vec(), "not an ELF binary at all"),
101 (Vec::new(), "not an ELF binary at all"),
102 ] {
103 let error = require_aarch64(&bytes).expect_err(why);
104 assert_eq!(error.kind(), ErrorKind::Precondition, "{error}");
105 let message = error.to_string();
106 assert!(message.contains(why), "{message}");
107 assert!(message.contains("ARM64-only"), "{message}");
108 assert!(
109 message.contains("--target aarch64-unknown-linux-musl"),
110 "{message}"
111 );
112 }
113 }
114}