1use std::fmt;
15
16use crate::config::Config;
17
18pub const MAX_PACKET_LEN: usize = 4096;
20pub const MAGIC: [u8; 8] = *b"HLIINIT1";
22pub const VERSION: u16 = 1;
24pub const HEADER_LEN: usize = 98;
26
27#[derive(Clone, Copy, Debug, Eq, PartialEq)]
29pub struct Packet<'a> {
30 pub nonce: &'a [u8; 32],
32 pub config_digest: &'a [u8; 32],
34 pub schema_fingerprint: &'a [u8; 16],
36 pub coordinator: &'a [u8],
38 pub detlog_identity: &'a str,
40 pub forward_policy: &'a str,
42}
43
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
46pub enum PacketError {
47 Size,
49 Magic,
51 Version,
53 Length,
55 Empty,
57 Nul,
59 Utf8,
61 Fingerprint,
63}
64impl fmt::Display for PacketError {
65 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
66 f.write_str(match self {
67 Self::Size => "InitialImage packet size mismatch",
68 Self::Magic => "InitialImage packet magic mismatch",
69 Self::Version => "InitialImage packet version mismatch",
70 Self::Length => "InitialImage packet field length mismatch",
71 Self::Empty => "InitialImage packet mandatory field is empty",
72 Self::Nul => "InitialImage packet field contains NUL",
73 Self::Utf8 => "InitialImage packet DETLOG field is not UTF-8",
74 Self::Fingerprint => "InitialImage packet schema fingerprint is not ASCII hex",
75 })
76 }
77}
78impl std::error::Error for PacketError {}
79
80pub fn decode(bytes: &[u8]) -> Result<Packet<'_>, PacketError> {
82 if !(HEADER_LEN..=MAX_PACKET_LEN).contains(&bytes.len()) {
83 return Err(PacketError::Size);
84 }
85 if bytes[..8] != MAGIC {
86 return Err(PacketError::Magic);
87 }
88 if u16::from_be_bytes([bytes[8], bytes[9]]) != VERSION {
89 return Err(PacketError::Version);
90 }
91 if usize::from(u16::from_be_bytes([bytes[10], bytes[11]])) != bytes.len() {
92 return Err(PacketError::Size);
93 }
94 let lengths = [92, 94, 96].map(|i| usize::from(u16::from_be_bytes([bytes[i], bytes[i + 1]])));
95 let path_end = HEADER_LEN + lengths[0];
96 let identity_end = path_end + lengths[1];
97 let policy_end = identity_end + lengths[2];
98 if policy_end != bytes.len() {
99 return Err(PacketError::Length);
100 }
101 let coordinator = &bytes[HEADER_LEN..path_end];
103 let identity = &bytes[path_end..identity_end];
104 let policy = &bytes[identity_end..policy_end];
105 check_text(coordinator)?;
106 check_text(identity)?;
107 check_text(policy)?;
108 let packet = Packet {
109 nonce: bytes[12..44].try_into().map_err(|_| PacketError::Length)?,
110 config_digest: bytes[44..76].try_into().map_err(|_| PacketError::Length)?,
111 schema_fingerprint: bytes[76..92]
112 .try_into()
113 .map_err(|_| PacketError::Fingerprint)?,
114 coordinator,
115 detlog_identity: std::str::from_utf8(identity).map_err(|_| PacketError::Utf8)?,
116 forward_policy: std::str::from_utf8(policy).map_err(|_| PacketError::Utf8)?,
117 };
118 check_fingerprint(packet.schema_fingerprint)?;
119 Ok(packet)
120}
121
122pub fn encode(packet: Packet<'_>) -> Result<Vec<u8>, PacketError> {
124 let fields = [
125 packet.coordinator,
126 packet.detlog_identity.as_bytes(),
127 packet.forward_policy.as_bytes(),
128 ];
129 for field in fields {
130 if field.len() > MAX_PACKET_LEN {
131 return Err(PacketError::Size);
132 }
133 check_text(field)?;
134 }
135 check_fingerprint(packet.schema_fingerprint)?;
136 let total = HEADER_LEN
137 .checked_add(fields.iter().map(|field| field.len()).sum::<usize>())
138 .filter(|total| *total <= MAX_PACKET_LEN)
139 .ok_or(PacketError::Size)?;
140 let mut bytes = Vec::with_capacity(total);
141 bytes.extend_from_slice(&MAGIC);
142 bytes.extend_from_slice(&VERSION.to_be_bytes());
143 bytes.extend_from_slice(&(total as u16).to_be_bytes());
144 bytes.extend_from_slice(packet.nonce);
145 bytes.extend_from_slice(packet.config_digest);
146 bytes.extend_from_slice(packet.schema_fingerprint);
147 for field in fields {
148 bytes.extend_from_slice(&(field.len() as u16).to_be_bytes());
149 }
150 for field in fields {
151 bytes.extend_from_slice(field);
152 }
153 Ok(bytes)
154}
155
156pub const OBSERVER_VERSION: u16 = 2;
159pub fn decode_with_observer(bytes: &[u8]) -> Result<(Packet<'_>, Option<&str>), PacketError> {
162 if bytes.len() < 10 || u16::from_be_bytes([bytes[8], bytes[9]]) != OBSERVER_VERSION {
163 return decode(bytes).map(|packet| (packet, None));
164 }
165 if !(HEADER_LEN + 2..=MAX_PACKET_LEN).contains(&bytes.len()) {
166 return Err(PacketError::Size);
167 }
168 if bytes[..8] != MAGIC {
169 return Err(PacketError::Magic);
170 }
171 if usize::from(u16::from_be_bytes([bytes[10], bytes[11]])) != bytes.len() {
172 return Err(PacketError::Size);
173 }
174 let lengths =
175 [92, 94, 96, 98].map(|i| usize::from(u16::from_be_bytes([bytes[i], bytes[i + 1]])));
176 let mut end = HEADER_LEN + 2;
177 let mut fields: [&[u8]; 4] = [&[]; 4];
178 for (field, length) in fields.iter_mut().zip(lengths) {
179 let next = end.checked_add(length).ok_or(PacketError::Length)?;
180 *field = bytes.get(end..next).ok_or(PacketError::Length)?;
181 check_text(field)?;
182 end = next;
183 }
184 if end != bytes.len() {
185 return Err(PacketError::Length);
186 }
187 let packet = Packet {
188 nonce: bytes[12..44].try_into().map_err(|_| PacketError::Length)?,
189 config_digest: bytes[44..76].try_into().map_err(|_| PacketError::Length)?,
190 schema_fingerprint: bytes[76..92]
191 .try_into()
192 .map_err(|_| PacketError::Fingerprint)?,
193 coordinator: fields[0],
194 detlog_identity: std::str::from_utf8(fields[1]).map_err(|_| PacketError::Utf8)?,
195 forward_policy: std::str::from_utf8(fields[2]).map_err(|_| PacketError::Utf8)?,
196 };
197 check_fingerprint(packet.schema_fingerprint)?;
198 let observer = std::str::from_utf8(fields[3]).map_err(|_| PacketError::Utf8)?;
199 if observer == packet.detlog_identity {
200 return Err(PacketError::Length);
201 }
202 Ok((packet, Some(observer)))
203}
204
205pub fn encode_with_observer(packet: Packet<'_>, observer: &str) -> Result<Vec<u8>, PacketError> {
207 check_text(observer.as_bytes())?;
208 if observer == packet.detlog_identity {
209 return Err(PacketError::Length);
210 }
211 let original = encode(packet)?;
212 let total = original
213 .len()
214 .checked_add(2)
215 .and_then(|n| n.checked_add(observer.len()))
216 .filter(|n| *n <= MAX_PACKET_LEN)
217 .ok_or(PacketError::Size)?;
218 let mut bytes = Vec::with_capacity(total);
219 bytes.extend_from_slice(&original[..HEADER_LEN]);
220 bytes[8..10].copy_from_slice(&OBSERVER_VERSION.to_be_bytes());
221 bytes[10..12].copy_from_slice(&(total as u16).to_be_bytes());
222 bytes.extend_from_slice(&(observer.len() as u16).to_be_bytes());
223 bytes.extend_from_slice(&original[HEADER_LEN..]);
224 bytes.extend_from_slice(observer.as_bytes());
225 decode_with_observer(&bytes)?;
226 Ok(bytes)
227}
228
229fn check_text(bytes: &[u8]) -> Result<(), PacketError> {
230 if bytes.is_empty() {
231 return Err(PacketError::Empty);
232 }
233 if bytes.contains(&0) {
234 return Err(PacketError::Nul);
235 }
236 Ok(())
237}
238fn check_fingerprint(bytes: &[u8; 16]) -> Result<(), PacketError> {
239 if bytes.iter().all(u8::is_ascii_hexdigit) {
240 Ok(())
241 } else {
242 Err(PacketError::Fingerprint)
243 }
244}
245
246pub fn config_violation(config: &Config) -> Option<&'static str> {
254 if config.use_rcb_time() || config.max_timeslice.is_some() || config.target_timeslice.is_some()
255 {
256 return Some("InitialImage does not support RCB time or preemption timeslices");
257 }
258 if config.imprecise_timers
259 || config.chaos
260 || config.chaos_target_races
261 || config.chaos_per_thread_slowdown
262 {
263 return Some("InitialImage does not support chaos or imprecise timers");
264 }
265 if config.record_preemptions
266 || config.record_preemptions_to.is_some()
267 || config.replay_preemptions_from.is_some()
268 || config.replay_schedule_from.is_some()
269 || config.preemption_stacktrace
270 || config.preemption_stacktrace_log_file.is_some()
271 || !config.stacktrace_event.is_empty()
272 || config.stacktrace_signal.is_some()
273 {
274 return Some(
275 "InitialImage does not support preemption/schedule recording, replay or stack traces",
276 );
277 }
278 if config.recordreplay_modes || config.replaying || config.replay_data.is_some() {
279 return Some("InitialImage does not support record/replay execution");
280 }
281 None
282}
283
284#[cfg(test)]
285mod tests {
286 use super::*;
287
288 fn packet<'a>() -> Packet<'a> {
289 Packet {
290 nonce: &[1; 32],
291 config_digest: &[2; 32],
292 schema_fingerprint: b"0123456789abcdef",
293 coordinator: b"/tmp/coordinator-\xff.sock",
294 detlog_identity: "19:7:1234",
295 forward_policy: "INFO",
296 }
297 }
298
299 #[test]
300 fn exact_wire_shape_and_borrowed_roundtrip_accept_raw_path() {
301 let packet = packet();
302 let encoded = encode(packet).unwrap();
303 assert_eq!(
304 &encoded[..12],
305 &[b'H', b'L', b'I', b'I', b'N', b'I', b'T', b'1', 0, 1, 0, 134]
306 );
307 assert_eq!(HEADER_LEN, 98);
308 let decoded = decode(&encoded).unwrap();
309 assert_eq!(decoded, packet);
310 assert_eq!(decoded.coordinator.as_ptr(), encoded[HEADER_LEN..].as_ptr());
311 assert_eq!(decoded.nonce.as_ptr(), encoded[12..].as_ptr());
312 }
313
314 #[test]
315 fn malformed_shapes_and_every_truncated_prefix_refuse() {
316 let valid = encode(packet()).unwrap();
317 for length in 0..valid.len() {
318 assert!(decode(&valid[..length]).is_err(), "prefix {length}");
319 }
320 for (index, byte, expected) in [
321 (0, 0, PacketError::Magic),
322 (9, 2, PacketError::Version),
323 (11, 0, PacketError::Size),
324 (76, b'g', PacketError::Fingerprint),
325 (93, 0, PacketError::Length),
326 (98, 0, PacketError::Nul),
327 ] {
328 let mut bytes = valid.clone();
329 bytes[index] = byte;
330 assert_eq!(decode(&bytes), Err(expected), "offset {index}");
331 }
332 for field in 0..3 {
333 let mut bytes = valid.clone();
334 let index = 92 + field * 2;
335 let length = u16::from_be_bytes([bytes[index], bytes[index + 1]]) as usize;
336 let start = 98
337 + [
338 packet().coordinator.len(),
339 packet().detlog_identity.len(),
340 packet().forward_policy.len(),
341 ][..field]
342 .iter()
343 .sum::<usize>();
344 bytes.drain(start..start + length);
345 bytes[index..index + 2].copy_from_slice(&0_u16.to_be_bytes());
346 let total = bytes.len() as u16;
347 bytes[10..12].copy_from_slice(&total.to_be_bytes());
348 assert_eq!(
349 decode(&bytes),
350 Err(PacketError::Empty),
351 "empty field {field}"
352 );
353 }
354 let mut trailing = valid.clone();
355 trailing.push(1);
356 assert_eq!(decode(&trailing), Err(PacketError::Size));
357 let total = trailing.len() as u16;
358 trailing[10..12].copy_from_slice(&total.to_be_bytes());
359 assert_eq!(decode(&trailing), Err(PacketError::Length));
360 let identity_start = 98 + packet().coordinator.len();
361 for (byte, error) in [(0, PacketError::Nul), (0xff, PacketError::Utf8)] {
362 let mut bytes = valid.clone();
363 bytes[identity_start] = byte;
364 assert_eq!(decode(&bytes), Err(error));
365 }
366 let policy_start = identity_start + packet().detlog_identity.len();
367 let mut bytes = valid;
368 bytes[policy_start] = 0xff;
369 assert_eq!(decode(&bytes), Err(PacketError::Utf8));
370 }
371
372 #[test]
373 fn packet_bound_and_host_validation_have_no_fallback() {
374 let mut packet = packet();
375 let path = vec![
376 b'x';
377 MAX_PACKET_LEN
378 - HEADER_LEN
379 - packet.detlog_identity.len()
380 - packet.forward_policy.len()
381 ];
382 packet.coordinator = &path;
383 let bytes = encode(packet).unwrap();
384 assert_eq!(bytes.len(), 4096);
385 assert!(decode(&bytes).is_ok());
386 let oversized = vec![b'x'; path.len() + 1];
387 packet.coordinator = &oversized;
388 assert_eq!(encode(packet), Err(PacketError::Size));
389 packet.coordinator = b"";
390 assert_eq!(encode(packet), Err(PacketError::Empty));
391 packet.coordinator = b"x\0";
392 assert_eq!(encode(packet), Err(PacketError::Nul));
393 packet.coordinator = b"x";
394 packet.detlog_identity = "";
395 assert_eq!(encode(packet), Err(PacketError::Empty));
396 packet.detlog_identity = "1";
397 packet.forward_policy = "\0";
398 assert_eq!(encode(packet), Err(PacketError::Nul));
399 assert_eq!(decode(&vec![1; 4097]), Err(PacketError::Size));
400 }
401
402 fn bounded_config() -> Config {
403 Config {
404 max_timeslice: None,
405 target_timeslice: None,
406 no_rcb_time: true,
407 ..Config::default()
408 }
409 }
410 #[test]
411 fn actual_config_options_refuse_without_mutation_and_verify_capture_remains() {
412 let mut allowed = bounded_config();
413 allowed.record_host_inputs = true;
414 assert_eq!(config_violation(&allowed), None);
415 let mut rcb = bounded_config();
416 rcb.no_rcb_time = false;
417 rcb.max_timeslice = Some(std::num::NonZeroU64::new(1).unwrap());
418 assert!(rcb.use_rcb_time());
419 assert!(config_violation(&rcb).is_some());
420 let mutations: &[fn(&mut Config)] = &[
421 |c| c.max_timeslice = Some(std::num::NonZeroU64::new(1).unwrap()),
422 |c| c.target_timeslice = Some(std::num::NonZeroU64::new(1).unwrap()),
423 |c| c.imprecise_timers = true,
424 |c| c.chaos = true,
425 |c| c.chaos_target_races = true,
426 |c| c.chaos_per_thread_slowdown = true,
427 |c| c.record_preemptions = true,
428 |c| c.record_preemptions_to = Some("trace".into()),
429 |c| c.replay_preemptions_from = Some("trace".into()),
430 |c| c.replay_schedule_from = Some("trace".into()),
431 |c| c.preemption_stacktrace = true,
432 |c| c.preemption_stacktrace_log_file = Some("trace".into()),
433 |c| c.stacktrace_event.push((1, None)),
434 |c| c.stacktrace_signal = Some(crate::schedule::SigWrapper(libc::SIGINT)),
435 |c| c.recordreplay_modes = true,
436 |c| c.replaying = true,
437 |c| c.replay_data = Some("record".into()),
438 ];
439 for (index, mutate) in mutations.iter().enumerate() {
440 let mut config = bounded_config();
441 mutate(&mut config);
442 let before = bincode::serde::encode_to_vec(&config, bincode::config::legacy()).unwrap();
443 assert!(config_violation(&config).is_some(), "case {index}");
444 assert_eq!(
445 bincode::serde::encode_to_vec(&config, bincode::config::legacy()).unwrap(),
446 before
447 );
448 }
449 }
450}
451
452#[cfg(test)]
453mod observer_tests {
454 use super::*;
455 fn packet() -> Packet<'static> {
456 Packet {
457 nonce: &[1; 32],
458 config_digest: &[2; 32],
459 schema_fingerprint: b"0123456789abcdef",
460 coordinator: b"/rpc",
461 detlog_identity: "10:0000000000000001:0000000000000002",
462 forward_policy: "INFO",
463 }
464 }
465 #[test]
466 fn legacy_bytes_and_decoder_stay_separate() {
467 let old = encode(packet()).unwrap();
468 assert_eq!(decode_with_observer(&old), Ok((packet(), None)));
469 let new = encode_with_observer(packet(), "11:0000000000000001:0000000000000003").unwrap();
470 assert_eq!(decode(&new), Err(PacketError::Version));
471 assert_eq!(
472 decode_with_observer(&new),
473 Ok((packet(), Some("11:0000000000000001:0000000000000003")))
474 );
475 }
476 #[test]
477 fn observer_missing_malformed_and_trailing_refuse() {
478 assert_eq!(encode_with_observer(packet(), ""), Err(PacketError::Empty));
479 assert_eq!(
480 encode_with_observer(packet(), packet().detlog_identity),
481 Err(PacketError::Length)
482 );
483 let bytes = encode_with_observer(packet(), "11:1:3").unwrap();
484 let mut missing = bytes.clone();
485 missing[98..100].copy_from_slice(&0u16.to_be_bytes());
486 assert_eq!(decode_with_observer(&missing), Err(PacketError::Empty));
487 let mut extra = bytes.clone();
488 extra.push(1);
489 let n = extra.len() as u16;
490 extra[10..12].copy_from_slice(&n.to_be_bytes());
491 assert_eq!(decode_with_observer(&extra), Err(PacketError::Length));
492 assert_eq!(
493 decode_with_observer(&bytes[..bytes.len() - 1]),
494 Err(PacketError::Size)
495 );
496 let mut nul = bytes;
497 *nul.last_mut().unwrap() = 0;
498 assert_eq!(decode_with_observer(&nul), Err(PacketError::Nul));
499 }
500}