Skip to content

Commit 011ab37

Browse files
committed
fix: restore SCALE Encode/Decode with size limit, fix formatting
- Restore Encode/Decode impls for VersionedAttestation (needed by CertSigningRequestV2), with a 10 MiB size limit to prevent OOM on untrusted input - Add size check in from_bytes for direct callers - Document that VersionedAttestation must be the last field in SCALE containers due to the consume-all-remaining decode strategy - Fix prek formatting
1 parent 98f03f8 commit 011ab37

2 files changed

Lines changed: 56 additions & 6 deletions

File tree

dstack-attest/src/attestation.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use dstack_types::SysConfig;
2222
use dstack_types::{Platform, VmConfig};
2323
use ez_hash::{sha256, Hasher, Sha384};
2424
use or_panic::ResultOrPanic;
25-
use scale::{Decode, Encode};
25+
use scale::{Decode, Encode, Error as ScaleError, Input, Output};
2626
use serde::{Deserialize, Serialize};
2727
use serde_human_bytes as hex_bytes;
2828
use sha2::Digest as _;
@@ -329,7 +329,16 @@ enum LegacyVersionedAttestation {
329329
V0 { attestation: Attestation },
330330
}
331331

332+
/// Maximum size for encoded attestation bytes (10 MiB).
333+
/// Prevents OOM when decoding untrusted input.
334+
const MAX_ATTESTATION_BYTES: usize = 10 * 1024 * 1024;
335+
332336
/// Represents a versioned attestation.
337+
///
338+
/// **SCALE note**: `VersionedAttestation` implements `Encode`/`Decode` so it can
339+
/// be embedded in SCALE structs (e.g. `CertSigningRequestV2`). The `Decode` impl
340+
/// consumes all remaining input, so it **must** be the last field in any SCALE
341+
/// container.
333342
#[derive(Clone)]
334343
pub enum VersionedAttestation {
335344
/// Legacy SCALE-encoded attestation.
@@ -344,9 +353,54 @@ pub enum VersionedAttestation {
344353
},
345354
}
346355

356+
impl Encode for VersionedAttestation {
357+
fn size_hint(&self) -> usize {
358+
self.to_bytes()
359+
.map(|b| b.len())
360+
.unwrap_or(0)
361+
}
362+
363+
fn encode_to<T: Output + ?Sized>(&self, dest: &mut T) {
364+
let bytes = self
365+
.to_bytes()
366+
.expect("VersionedAttestation should always encode successfully");
367+
dest.write(&bytes);
368+
}
369+
}
370+
371+
impl Decode for VersionedAttestation {
372+
fn decode<I: Input>(input: &mut I) -> Result<Self, ScaleError> {
373+
let Some(remaining_len) = input.remaining_len()? else {
374+
return Err(ScaleError::from(
375+
"VersionedAttestation requires a bounded input to decode",
376+
));
377+
};
378+
if remaining_len > MAX_ATTESTATION_BYTES {
379+
return Err(ScaleError::from(
380+
"attestation bytes exceed maximum allowed size",
381+
));
382+
}
383+
let mut bytes = vec![0u8; remaining_len];
384+
input.read(&mut bytes)?;
385+
Self::from_bytes(&bytes).map_err(|err| {
386+
ScaleError::from(std::io::Error::new(
387+
std::io::ErrorKind::InvalidData,
388+
err.to_string(),
389+
))
390+
})
391+
}
392+
}
393+
347394
impl VersionedAttestation {
348395
/// Decode versioned attestation bytes.
349396
pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
397+
if bytes.len() > MAX_ATTESTATION_BYTES {
398+
bail!(
399+
"attestation bytes too large: {} > {}",
400+
bytes.len(),
401+
MAX_ATTESTATION_BYTES
402+
);
403+
}
350404
let Some(first) = bytes.first().copied() else {
351405
bail!("Empty attestation bytes");
352406
};

guest-agent-simulator/src/main.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,7 @@ impl PlatformBackend for SimulatorPlatform {
8787
}
8888

8989
fn attest_response(&self, report_data: [u8; 64]) -> Result<AttestResponse> {
90-
simulator::simulated_attest_response(
91-
&self.attestation,
92-
report_data,
93-
self.patch_report_data,
94-
)
90+
simulator::simulated_attest_response(&self.attestation, report_data, self.patch_report_data)
9591
}
9692

9793
fn emit_event(&self, event: &str, _payload: &[u8]) -> Result<()> {

0 commit comments

Comments
 (0)