pub trait BoundedReader {
// Required method
fn remaining(&self) -> usize;
// Provided methods
fn protocol_limits(&self) -> ProtocolLimits { ... }
fn alloc_count_checked(
&self,
count: usize,
min_bytes_per_elem: usize,
) -> Result<usize> { ... }
fn with_capacity_bounded<T>(
&self,
count: usize,
min_bytes_per_elem: usize,
) -> Vec<T> { ... }
fn with_capacity_limited<T, F>(
&self,
count: usize,
min_bytes_per_elem: usize,
check: F,
) -> Result<Vec<T>>
where F: FnOnce(&ProtocolLimits, usize) -> Result<()> { ... }
}Expand description
The structural OOM-from-length invariant for every wire decoder.
A length/count field read from the wire can never drive an allocation
larger than the bytes actually remaining in the current message buffer: you
cannot have N elements if fewer than N * min_bytes_per_elem bytes remain.
Every reader over an untrusted buffer (TtcReader, the OSON / DbObject /
notification cursors, the VECTOR reader) implements this trait, and every
count-driven Vec::with_capacity / reserve in the decoders routes through
one of its two methods instead of trusting a raw u16/u32/u64 count.
This closes the OOM-from-length bug class by construction: a new decoder
physically cannot pre-allocate from a wire count without going through a
bound, because the raw Vec::with_capacity(count) shape is the thing we
audit against (see docs/FUZZING.md).
Two flavors, both anchored on remaining:
alloc_count_checked— fail closed early: returns anErrif the declared count cannot possibly fit, before any allocation. Use where an oversized count is unambiguously malformed.with_capacity_bounded— cap the pre-allocation at what the buffer could hold while still returning a normal growableVec. Use where the loop body itself fails closed on the first truncated element read; legitimate large payloads keep working because the cap equals the honest count whenever the bytes are really there.
Required Methods§
Provided Methods§
Sourcefn protocol_limits(&self) -> ProtocolLimits
fn protocol_limits(&self) -> ProtocolLimits
Resource policy attached to this decoder. Readers that have not yet grown a configurable policy surface use the validated defaults.
Sourcefn alloc_count_checked(
&self,
count: usize,
min_bytes_per_elem: usize,
) -> Result<usize>
fn alloc_count_checked( &self, count: usize, min_bytes_per_elem: usize, ) -> Result<usize>
Validate a server-declared element count against the buffer: a run of
count elements must carry at least count * min_bytes_per_elem bytes,
so a count whose minimum byte footprint exceeds Self::remaining is a lie.
Returns the (unchanged) count when it fits, or a fail-closed
ProtocolError::TtcDecode otherwise — never a panic, never an OOM.
min_bytes_per_elem is the minimum on-wire size of one element (e.g.
4 for a u32 index, 8 for an f64, 1 for a length-prefixed field whose
shortest legal form is a single length byte). A zero is treated as 1.
Sourcefn with_capacity_bounded<T>(
&self,
count: usize,
min_bytes_per_elem: usize,
) -> Vec<T>
fn with_capacity_bounded<T>( &self, count: usize, min_bytes_per_elem: usize, ) -> Vec<T>
Pre-size a Vec for count elements without trusting count: the
reserved capacity is capped at remaining() / min_bytes_per_elem, the
largest number of elements the buffer could actually hold. The returned
Vec is a normal growable Vec, so a legitimately large payload (where
count really fits) is pre-sized to the honest count, and a streamed /
chunked field that grows past the initial buffer still appends correctly
— the cap only governs the speculative up-front reservation.
Sourcefn with_capacity_limited<T, F>(
&self,
count: usize,
min_bytes_per_elem: usize,
check: F,
) -> Result<Vec<T>>
fn with_capacity_limited<T, F>( &self, count: usize, min_bytes_per_elem: usize, check: F, ) -> Result<Vec<T>>
Policy-aware form of with_capacity_bounded:
the caller supplies the resource family check, then the speculative
allocation is still capped by the remaining buffer.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".