Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/migtd/src/driver/serial.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ pub fn virtio_serial_device_init() {
pci::init_mmio();

// Enumerate the virtio device
let (_b, dev, _f) = pci::find_device(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID).unwrap();
let (_b, dev, _f) = pci::find_device(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID)
.expect("Failed to find virtio-serial PCI device");

let pci_device = pci::PciDevice::new(0, dev, 0);

Expand Down
9 changes: 9 additions & 0 deletions src/migtd/src/driver/timer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,15 @@ pub fn init_timer() {
pub fn schedule_timeout(timeout: u32) -> Option<u64> {
reset_timer();
let cpuid = unsafe { core::arch::x86_64::__cpuid_count(0x15, 0) };
if cpuid.eax == 0 || cpuid.ebx == 0 || cpuid.ecx == 0 {
log::error!(
"schedule_timeout: CPUID.15H returned invalid values (eax={}, ebx={}, ecx={})\n",
cpuid.eax,
cpuid.ebx,
cpuid.ecx
);
return None;
}
let tsc_frequency = cpuid.ecx * (cpuid.ebx / cpuid.eax);
let deadline = (tsc_frequency / 1000) as u64 * timeout as u64;

Expand Down
3 changes: 2 additions & 1 deletion src/migtd/src/driver/vsock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ pub fn virtio_vsock_device_init() {
pci::init_mmio();

// Enumerate the virtio device
let (_b, dev, _f) = pci::find_device(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID).unwrap();
let (_b, dev, _f) = pci::find_device(VIRTIO_PCI_VENDOR_ID, VIRTIO_PCI_DEVICE_ID)
.expect("Failed to find virtio-vsock PCI device");

let pci_device = pci::PciDevice::new(0, dev, 0);

Expand Down
33 changes: 18 additions & 15 deletions src/migtd/src/event_log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,20 @@
}

pub fn get_event_log_mut() -> Option<&'static mut [u8]> {
get_ccel().map(event_log_slice)
get_ccel().and_then(event_log_slice)
}

pub fn get_event_log() -> Option<&'static [u8]> {
let raw = get_ccel().map(event_log_slice)?;
let raw = get_ccel().and_then(event_log_slice)?;
// The `+1` is required: `cc_measurement::log::CcEvents::next()` only
// yields an event when `end_of_event < bytes.len()` (strict inequality).
// If we sliced to exactly `size`, the buffer would end at the last
// event boundary and that final event would be silently dropped by the
// iterator, breaking `parse_events()` (e.g. losing the policy tag) and
// any downstream RTMR replay. The runtime layout has trailing zeros in
// the CCEL area, so including one extra byte is safe.
event_log_size(raw).map(|size| &raw[..size + 1])
// If we sliced to exactly `size`, the buffer would end at the last event
// boundary and that final event (the MigTdPolicy measurement) would be
// silently dropped by the iterator, breaking `parse_events()` and causing
// `check_policy_integrity()` to fail with `PolicyHashMismatch`. The runtime
// layout has trailing zeros in the CCEL area, so the extra byte is safe;
// clamp to the raw length as a defensive guard.
event_log_size(raw).map(|size| &raw[..core::cmp::min(size + 1, raw.len())])
}

fn event_log_size(event_log: &[u8]) -> Option<usize> {
Expand All @@ -90,16 +91,19 @@
Some(size)
}

fn event_log_slice(ccel: &Ccel) -> &'static mut [u8] {
unsafe { core::slice::from_raw_parts_mut(ccel.lasa as *mut u8, ccel.laml as usize) }
fn event_log_slice(ccel: &Ccel) -> Option<&'static mut [u8]> {
// Validate that lasa and laml are non-zero and laml is reasonable
if ccel.lasa == 0 || ccel.laml == 0 || ccel.laml as usize > 0x10_0000 {
return None;
}
Some(unsafe { core::slice::from_raw_parts_mut(ccel.lasa as *mut u8, ccel.laml as usize) })
}

fn get_ccel() -> Option<&'static Ccel> {
if !CCEL.is_completed() {
// Parse out ACPI tables handoff from firmware and find the event log location
let &ccel = get_acpi_tables()
.and_then(|tables| tables.iter().find(|&&t| t[..4] == *b"CCEL"))
.expect("Failed to find CCEL");
.and_then(|tables| tables.iter().find(|&&t| t.get(..4) == Some(b"CCEL")))?;

if ccel.len() < size_of::<Ccel>() {
return None;
Expand Down Expand Up @@ -173,7 +177,7 @@

tdx::tdcall_extend_rtmr(&digest, rtmr_index).map_err(|e| anyhow!("Extend RTMR: {:?}", e))
}

Check warning

Code scanning / clippy

accessing first element with event_data.get(0) Warning

accessing first element with event_data.get(0)
pub(crate) fn parse_events(event_log: &[u8]) -> Option<BTreeMap<EventName, CcEvent>> {
let mut map: BTreeMap<EventName, CcEvent> = BTreeMap::new();
let reader = CcEventLogReader::new(event_log)?;
Expand All @@ -181,9 +185,8 @@
for (event_header, event_data) in reader.cc_events {
match event_header.event_type {
EV_EFI_PLATFORM_FIRMWARE_BLOB2 => {
let desc_size = event_data[0] as usize;
let desc = event_data.get(1..1 + desc_size)?;
if desc == PLATFORM_FIRMWARE_BLOB2_PAYLOAD {
let desc_size = *event_data.get(0)? as usize;
if event_data.get(1..1 + desc_size)? == PLATFORM_FIRMWARE_BLOB2_PAYLOAD {
map.insert(EventName::MigTdCore, CcEvent::new(event_header, None));
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/migtd/src/migration/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ impl<'a> VmcallServiceResponse<'a> {
if length < RESPONSE_HEADER_LENGTH || length > data.len() {
return None;
}
Some(Self { data })
Some(Self {
data: &data[..length],
})
}

pub fn new(response: &'a mut [u8], guid: Guid) -> Option<Self> {
Expand Down
20 changes: 16 additions & 4 deletions src/migtd/src/migration/logging.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,12 +374,24 @@ pub fn entrylog(msg: &Vec<u8>, loglevel: Level, request_id: u64) {
Some(if v == u64::MAX { 1 } else { v + 1 })
})
.unwrap();
let start_offset: u64 =
u64::from_le_bytes(data_buffer[24..32].try_into().unwrap());
let end_offset: u64 =
u64::from_le_bytes(data_buffer[32..40].try_into().unwrap());
// Copy offsets from shared memory via volatile read to prevent TOCTOU
let start_offset: u64 = unsafe {
core::ptr::read_volatile(data_buffer[24..32].as_ptr() as *const u64)
};
let end_offset: u64 = unsafe {
core::ptr::read_volatile(data_buffer[32..40].as_ptr() as *const u64)
};
let mut currentstartoffset: usize = start_offset as usize;
let mut currentendoffset: usize = end_offset as usize;
// Clamp offsets to valid range to prevent VMM-controlled panic
if currentstartoffset < LOGAREABUFFERHEADERSIZE
|| currentstartoffset >= PAGE_SIZE
{
currentstartoffset = LOGAREABUFFERHEADERSIZE;
}
if currentendoffset < LOGAREABUFFERHEADERSIZE || currentendoffset >= PAGE_SIZE {
currentendoffset = LOGAREABUFFERHEADERSIZE;
}
if currentendoffset + LOGENTRYHEADERSIZE + msg.len() > PAGE_SIZE
|| currentendoffset < currentstartoffset
{
Expand Down
13 changes: 13 additions & 0 deletions src/migtd/src/migration/pre_session_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ pub(super) async fn receive_pre_session_data<T: AsyncRead + AsyncWrite + Unpin>(
log::error!("receive_pre_session_data: Network error: {:?}\n", e);
MigrationResult::NetworkError
})?;
if n == 0 {
log::error!("receive_pre_session_data: EOF (peer closed connection)\n");
return Err(MigrationResult::NetworkError);
}
recvd += n;
}
Ok(())
Expand Down Expand Up @@ -194,6 +198,15 @@ pub(super) async fn receive_pre_session_data_packet<T: AsyncRead + AsyncWrite +
}

let pre_session_data_payload_size = header.length as usize;
const MAX_PRE_SESSION_PAYLOAD: usize = 64 * 1024; // 64 KiB

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this limit is too low and it breaks rebinding

if pre_session_data_payload_size > MAX_PRE_SESSION_PAYLOAD {
log::error!(
"receive_pre_session_data_packet: payload size {} exceeds max {}\n",
pre_session_data_payload_size,
MAX_PRE_SESSION_PAYLOAD
);
return Err(MigrationResult::InvalidParameter);
}
let mut pre_session_data_payload = vec![0u8; pre_session_data_payload_size];
receive_pre_session_data(transport, &mut pre_session_data_payload)
.await
Expand Down
3 changes: 3 additions & 0 deletions src/migtd/src/migration/rebinding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -560,6 +560,9 @@ async fn tls_session_read_exact(
.read(&mut data[recvd..])
.await
.map_err(|_| MigrationResult::NetworkError)?;
if n == 0 {
return Err(MigrationResult::NetworkError);
}
recvd += n;
}
Ok(())
Expand Down
22 changes: 21 additions & 1 deletion src/migtd/src/ratls/server_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,16 @@ mod verify {
cert: &[u8],
quote_local: &[u8],
) -> core::result::Result<(), CryptoError> {
// Reject oversized certificates to prevent heap exhaustion from DER parsing
const MAX_CERT_SIZE: usize = 8192;
if cert.len() > MAX_CERT_SIZE {
log::error!(
"Certificate too large: {} bytes (max {})\n",
cert.len(),
MAX_CERT_SIZE
);
return Err(CryptoError::ParseCertificate);
}
let verified_report_local = attestation::verify_quote(quote_local).map_err(|e| {
log::error!("Mutual attestation error {:?}.\n", e);
CryptoError::TlsVerifyPeerCert(MUTUAL_ATTESTATION_ERROR.to_string())
Expand Down Expand Up @@ -1123,7 +1133,17 @@ mod verify {
}
const PUBLIC_KEY_HASH_SIZE: usize = 48;

let report_data = &verified_report[520..520 + PUBLIC_KEY_HASH_SIZE];
let report_data = verified_report
.get(520..520 + PUBLIC_KEY_HASH_SIZE)
.ok_or_else(|| {
log::error!(
"verify_public_key: verified_report too short (len={})\n",
verified_report.len()
);
CryptoError::TlsVerifyPeerCert(
"verified_report too short for public key hash".to_string(),
)
})?;
let digest = digest_sha384(public_key).map_err(|e| {
log::error!("Failed to compute SHA384 digest: {:?}\n", e);
e
Expand Down
Loading