Skip to content

tls_codec: 0.4.3 prep#2365

Open
franziskuskiefer wants to merge 3 commits into
masterfrom
franziskus/tls_codec-0.4.3-prep
Open

tls_codec: 0.4.3 prep#2365
franziskuskiefer wants to merge 3 commits into
masterfrom
franziskus/tls_codec-0.4.3-prep

Conversation

@franziskuskiefer

Copy link
Copy Markdown
Contributor

Release prep for the next tls_codec release.
This also fixes a couple more issues (see changelog) and adds some more fuzzers.

@franziskuskiefer franziskuskiefer force-pushed the franziskus/tls_codec-0.4.3-prep branch from 2e91fee to 9349b57 Compare July 3, 2026 09:01
@franziskuskiefer franziskuskiefer force-pushed the franziskus/tls_codec-0.4.3-prep branch from 9349b57 to b31a9e9 Compare July 3, 2026 09:13

@wysiwys wysiwys left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Added some notes, mainly on checks and avoiding overflows. Mainly only relevant on platforms where the serialized struct's size is more likely to reach usize::MAX (but this seems like it would almost never occur in practice)

Comment thread tls_codec/src/quic_vec.rs
let length = ContentLength::from_usize(content_length)?;
let len_len = length.0.bytes_len();

let mut out = Vec::with_capacity(content_length + len_len);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

If the total serialized content length is isize::MAX, this vector allocation could panic. I think this could use a check first that content_length + len_len <= isize::MAX.

Comment thread tls_codec/src/lib.rs
/// Returns [`Error::EndOfStream`] if the reader is exhausted before `len` bytes
/// are read.
#[cfg(feature = "std")]
fn read_bytes_bounded<R: std::io::Read>(reader: &mut R, len: usize) -> Result<Vec<u8>, Error> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

A nit on the parameter name: I think len could be renamed to expected_len or similar, to be more descriptive

Comment thread tls_codec/src/tls_vec.rs
#[cfg(feature = "std")]
#[inline(always)]
fn deserialize_bytes<R: Read>(bytes: &mut R) -> Result<Self, Error> {
let len = <$size>::tls_deserialize(bytes)?.try_into().unwrap();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

It looks like this may panic on 16-bit platforms where $size is u32

Comment thread tls_codec/src/lib.rs

// `read_to_end` reads directly into the vector's spare capacity and retries
// `ErrorKind::Interrupted` internally, unlike a bare `read` loop.
reader.take(len as u64).read_to_end(&mut result)?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since this method is used for types that are internally represented as byte vectors, and the bytes are read here into a byte vector, the size of the serialized data cannot exceed isize::MAX. I think that before reaching this line, an initial check that len does not exceed isize::MAX would be useful in order to rule out invalid lengths early on

Comment thread tls_codec/CHANGELOG.md

### Fixed
- [#2348](https://github.com/RustCrypto/formats/pull/2348) Use `write_all` everywhere instead of write to prevent partial writes from going undetected. The `Error::InvalidWriteLength` variant is deprecated as it is no longer returned.
- [#XXXX](https://github.com/RustCrypto/formats/pull/XXXX) Element-vector deserialization (`Vec<T>`, `TlsVecU*<T>`, `SecretTlsVecU*<T>`), for both the `Deserialize` (`std::io::Read`) and `DeserializeBytes` implementations, now measures actual byte consumption instead of relying on `tls_serialized_len()` and enforces the declared length exactly. This makes the two implementations agree for non-canonical inner encodings (e.g. non-minimal varint lengths) and rejects input whose elements overshoot the declared vector boundary.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
- [#XXXX](https://github.com/RustCrypto/formats/pull/XXXX) Element-vector deserialization (`Vec<T>`, `TlsVecU*<T>`, `SecretTlsVecU*<T>`), for both the `Deserialize` (`std::io::Read`) and `DeserializeBytes` implementations, now measures actual byte consumption instead of relying on `tls_serialized_len()` and enforces the declared length exactly. This makes the two implementations agree for non-canonical inner encodings (e.g. non-minimal varint lengths) and rejects input whose elements overshoot the declared vector boundary.
- [#2365](https://github.com/RustCrypto/formats/pull/2365) Element-vector deserialization (`Vec<T>`, `TlsVecU*<T>`, `SecretTlsVecU*<T>`), for both the `Deserialize` (`std::io::Read`) and `DeserializeBytes` implementations, now measures actual byte consumption instead of relying on `tls_serialized_len()` and enforces the declared length exactly. This makes the two implementations agree for non-canonical inner encodings (e.g. non-minimal varint lengths) and rejects input whose elements overshoot the declared vector boundary.

Comment thread tls_codec/CHANGELOG.md
- [#2348](https://github.com/RustCrypto/formats/pull/2348) Use `write_all` everywhere instead of write to prevent partial writes from going undetected. The `Error::InvalidWriteLength` variant is deprecated as it is no longer returned.
- [#XXXX](https://github.com/RustCrypto/formats/pull/XXXX) Element-vector deserialization (`Vec<T>`, `TlsVecU*<T>`, `SecretTlsVecU*<T>`), for both the `Deserialize` (`std::io::Read`) and `DeserializeBytes` implementations, now measures actual byte consumption instead of relying on `tls_serialized_len()` and enforces the declared length exactly. This makes the two implementations agree for non-canonical inner encodings (e.g. non-minimal varint lengths) and rejects input whose elements overshoot the declared vector boundary.
- Element vectors now reject zero-length elements that would otherwise never advance the read cursor, preventing an infinite loop and unbounded allocation on malicious input.
- Byte-vector deserialization (`TlsByteVecU*`, `VLBytes`, `VLByteVec`) now uses `checked_add` when computing the content range, returning `Error::InvalidVectorLength` instead of overflowing `usize` on targets where the length field is as wide as the pointer width.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

checked_add() is not yet used in the Deserialize impls for VLBytes and VLByteVec, right?

Comment on lines +770 to +773
/// narrower targets the serialized form of a large structure can carry enough
/// length-prefix / discriminant overhead to exceed `usize::MAX`, so we saturate
/// rather than silently wrapping to a small value (a wrapped length would be
/// written to the wire as a truncated, mismatched length prefix).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if it would be better to panic in the derive macro instead, since saturating could cause an incorrectly short length to be written. This would be caught by a debug assertion in impl_serialize(), but otherwise the length does not appear to be checked

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