Make codec shape authority compile-time and fixed

This commit is contained in:
ms2k 2026-07-19 04:33:43 +09:00
commit 54cc167f81
5 changed files with 322 additions and 52 deletions

View file

@ -65,6 +65,10 @@ pub struct NetworkProbeRequest {
pub dns: Vec<DnsProbeTarget>,
}
impl lazaret_core::records::SchemaType for NetworkProbeRequest {
const NAME: &'static str = "NetworkProbeRequest";
}
#[derive(Clone, Copy, Debug)]
pub struct NetworkAttestationContext {
pub domain_id: DomainId,
@ -663,9 +667,42 @@ mod tests {
DerivedNetworkPolicyFields, PolicyAction, PrefixRule, Resolver,
};
use lazaret_linux::filesystem::sealed_memfd;
use sha2::{Digest, Sha256};
use super::*;
#[test]
fn network_probe_request_vector_is_stable() {
let request = NetworkProbeRequest {
domain_id: DomainId::new([1; 32]),
generation_id: GenerationId::new(2),
enforcement_namespace: NamespaceIdentity {
device: lazaret_core::identity::DeviceId::new(3),
inode: lazaret_core::identity::InodeId::new(4),
},
denied: vec![TcpProbeTarget {
address: IpAddress::V4([10, 0, 0, 1]),
port: 9,
}],
permitted: vec![TcpProbeTarget {
address: IpAddress::V4([1, 1, 1, 1]),
port: 443,
}],
dns: vec![DnsProbeTarget {
resolver: IpAddress::V4([8, 8, 8, 8]),
query_name: "example.invalid".into(),
}],
};
let encoded = lazaret_core::codec::encode(&request).unwrap();
assert_eq!(
Sha256::digest(encoded).as_slice(),
[
190, 221, 207, 18, 23, 85, 102, 200, 111, 129, 14, 45, 225, 55, 146, 147, 223, 162,
27, 106, 204, 136, 214, 57, 134, 235, 241, 78, 215, 193, 84, 233,
]
);
}
#[test]
fn sealed_policy_requires_exact_canonical_digest() {
let policy = policy();

View file

@ -56,6 +56,10 @@ pub enum AttachedRunscSignal {
Kill,
}
impl lazaret_core::records::SchemaType for AttachedRunscSignal {
const NAME: &'static str = "AttachedRunscSignal";
}
impl AttachedRunscSignal {
const fn signal(self) -> Signal {
match self {
@ -112,6 +116,10 @@ pub enum SupervisorEvent {
Refused(String),
}
impl lazaret_core::records::SchemaType for SupervisorEvent {
const NAME: &'static str = "SupervisorEvent";
}
#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Serialize)]
enum CoordinatorMessage {
MountStateAnchor,
@ -138,6 +146,10 @@ enum CoordinatorMessage {
Stop,
}
impl lazaret_core::records::SchemaType for CoordinatorMessage {
const NAME: &'static str = "CoordinatorMessage";
}
pub struct CoordinatorChannel {
descriptor: OwnedFd,
}

View file

@ -68,6 +68,10 @@ enum RuntimeSupervisorHandoff {
V1(RuntimeSupervisorHandoffV1),
}
impl lazaret_core::records::SchemaType for RuntimeSupervisorHandoff {
const NAME: &'static str = "RuntimeSupervisorHandoff";
}
fn descriptor(
number: i32,
kind: RuntimeDescriptorKindV1,

View file

@ -543,7 +543,10 @@ fn lifetime_lost() -> io::Result<bool> {
.intersects(PollFlags::IN | PollFlags::HUP | PollFlags::ERR))
}
fn send_message<T: Serialize>(descriptor: impl AsFd, message: &T) -> io::Result<()> {
fn send_message<T: lazaret_core::records::SchemaType + Serialize>(
descriptor: impl AsFd,
message: &T,
) -> io::Result<()> {
let encoded = encode(message).map_err(|error| io::Error::other(error.to_string()))?;
if encoded.len() > MAX_CONTROL_MESSAGE {
return Err(io::Error::other("runtime control message is oversized"));
@ -557,7 +560,7 @@ fn send_message<T: Serialize>(descriptor: impl AsFd, message: &T) -> io::Result<
fn receive_message<T>(descriptor: impl AsFd) -> io::Result<T>
where
T: serde::de::DeserializeOwned + Serialize,
T: lazaret_core::records::SchemaType + serde::de::DeserializeOwned + Serialize,
{
let mut length = [0_u8; 4];
receive_exact(descriptor.as_fd(), &mut length)?;

View file

@ -7,9 +7,51 @@ mod tests {
use lazaret_linux::ipc::private_seqpacket_pair;
use lazaret_linux::process::ChildStandardIoPolicy;
use rustix::pipe::{PipeFlags, pipe_with};
use sha2::{Digest, Sha256};
use super::*;
#[test]
fn private_control_record_vectors_are_stable() {
let namespace = NamespaceIdentity {
device: lazaret_core::identity::DeviceId::new(1),
inode: lazaret_core::identity::InodeId::new(2),
};
let mount = MountIdentity {
mount_id: lazaret_core::identity::MountId::new(3),
device: lazaret_core::identity::DeviceId::new(4),
inode: lazaret_core::identity::InodeId::new(5),
};
let records = [
encode(&AttachedRunscSignal::Continue).unwrap(),
encode(&SupervisorEvent::StateAnchorAbsenceObserved {
user_namespace: namespace,
mount_namespace: namespace,
mount,
observed: mount,
})
.unwrap(),
encode(&CoordinatorMessage::ObserveStateAnchorAbsence {
user_namespace: namespace,
mount_namespace: namespace,
mount,
})
.unwrap(),
];
let mut corpus = Sha256::new();
for record in records {
corpus.update(record.len().to_be_bytes());
corpus.update(record);
}
assert_eq!(
corpus.finalize().as_slice(),
[
59, 166, 131, 49, 39, 47, 193, 4, 70, 106, 107, 188, 204, 94, 145, 7, 250,
164, 101, 22, 121, 174, 24, 13, 138, 108, 62, 97, 17, 162, 129, 89,
]
);
}
#[test]
fn private_stream_framing_round_trips_typed_events() {
let (coordinator, supervisor) = private_seqpacket_pair().unwrap();