Skip to content

Bound protocol message sizes before allocating - #108

Open
plaidfinch wants to merge 1 commit into
mainfrom
fix-message-size
Open

Bound protocol message sizes before allocating#108
plaidfinch wants to merge 1 commit into
mainfrom
fix-message-size

Conversation

@plaidfinch

@plaidfinch plaidfinch commented Jul 17, 2026

Copy link
Copy Markdown

CAVEAT LECTOR: This PR was generated by Claude Fable 5.

Problem

recv_msg allocates the full message buffer from the peer-controlled length prefix before reading any of the body, so a TLS-authenticated (but not yet attested) peer can demand a 4 GiB zeroed allocation with a 4-byte prefix, and hold it by leaving the connection open, repeatably across connections, for memory exhaustion.

I posit that authenticated-but-unattested is the adversary class the attestation layer exists to screen out (e.g. a compromised sled with legitimate certs), so the framing layer must not trust it with allocation sizes.

Fix

Bound messages at a named MAX_MSG_SIZE (1 MiB) checked before allocation, rejected as a new Error::MessageTooLarge { len, max }. Compile-time asserts prove the bound admits the largest legitimate messages (the hubpacked measurement Log and Attestation); cert chains and nonces are far smaller. No interoperability change.

Testing

New regression test oversized_message_rejected: an authenticated client sends a u32::MAX length prefix with no body; the server must reject on the prefix alone.

Verified on illumos: cargo test 7/7, CI-style clippy clean.

Relationships

Independent of #107 and #109; the three can land in any order.

recv_msg allocated the full message buffer from the peer-controlled length
prefix before reading any of the body, so a TLS-authenticated (but not yet
attested) peer could demand a 4 GiB zeroed allocation with a 4-byte prefix,
and hold it by leaving the connection open — repeatable across connections
for memory exhaustion. Authenticated-but-unattested is exactly the
adversary class the attestation layer exists to screen out, so the framing
layer must not trust it with allocation sizes.

Bound messages at a named MAX_MSG_SIZE (1 MiB), with compile-time asserts
that the bound admits the largest legitimate messages (the hubpacked
measurement log and attestation). Oversized lengths are rejected as a new
Error::MessageTooLarge before any allocation. No interoperability change:
every legitimate protocol message is far below the bound.

@andrewjstone andrewjstone left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm quite surprised I didn't add max message size code here. That's definitely something I try to always do. Nice catch Claude!

Comment thread tls/src/lib.rs
const MAX_MSG_SIZE: usize = 1024 * 1024;

// The bound must admit every legitimate protocol message.
const _: () = assert!(dice_verifier::Log::MAX_SIZE <= MAX_MSG_SIZE);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Never seen this style of static assertion before. I usually use https://docs.rs/static_assertions/latest/static_assertions/ for this. I like this though!

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

I would have reached for that too :) I think this is even what happens inside of static_assertions? De gustibus, imo.

Comment thread tls/src/lib.rs
/// legitimate protocol message: the largest are the hubpacked measurement
/// log and attestation, whose bounds are asserted below; cert chains and
/// nonces are far smaller.
const MAX_MSG_SIZE: usize = 1024 * 1024;

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

This should be large enough but I really don't want to hit this in production if we have too many reconfigurations before garbage collection is implemented for old secrets.. Maybe 10MiB instead?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

This is a good point! I'd trust your judgement about what to fill in here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants