Skip to main content

sim_lib_midi_smf/
limits.rs

1#![forbid(unsafe_code)]
2
3use crate::{SmfError, SmfLimitKind};
4
5/// Defensive resource bounds applied while parsing an SMF byte slice.
6///
7/// Limits are checked before capacity is reserved or a payload is copied.
8/// [`read_smf`](crate::read_smf) uses [`Default`] limits; hosts accepting
9/// untrusted files can choose tighter limits through
10/// [`read_smf_with_limits`](crate::read_smf_with_limits).
11#[derive(Copy, Clone, Debug, PartialEq, Eq)]
12pub struct SmfReadLimits {
13    /// Maximum complete input size.
14    pub max_file_bytes: usize,
15    /// Maximum declared header chunk body size.
16    pub max_header_bytes: usize,
17    /// Maximum declared number of tracks.
18    pub max_tracks: usize,
19    /// Maximum body size of one track chunk.
20    pub max_track_bytes: usize,
21    /// Maximum total number of events across all tracks.
22    pub max_events: usize,
23    /// Maximum payload size of one meta or system-exclusive event.
24    pub max_event_payload_bytes: usize,
25    /// Maximum aggregate payload bytes copied across all events.
26    pub max_total_payload_bytes: usize,
27}
28
29impl Default for SmfReadLimits {
30    fn default() -> Self {
31        Self {
32            max_file_bytes: 64 * 1024 * 1024,
33            max_header_bytes: 1024,
34            max_tracks: 1024,
35            max_track_bytes: 16 * 1024 * 1024,
36            max_events: 1_000_000,
37            max_event_payload_bytes: 1024 * 1024,
38            max_total_payload_bytes: 16 * 1024 * 1024,
39        }
40    }
41}
42
43#[derive(Debug)]
44pub(crate) struct ReadBudget {
45    limits: SmfReadLimits,
46    events: usize,
47    payload_bytes: usize,
48}
49
50impl ReadBudget {
51    pub(crate) fn new(limits: SmfReadLimits) -> Self {
52        Self {
53            limits,
54            events: 0,
55            payload_bytes: 0,
56        }
57    }
58
59    pub(crate) fn claim_event(&mut self, offset: usize) -> Result<(), SmfError> {
60        let actual = self.events.checked_add(1).ok_or(SmfError::LimitExceeded {
61            offset,
62            kind: SmfLimitKind::EventCount,
63            actual: usize::MAX,
64            maximum: self.limits.max_events,
65        })?;
66        enforce_limit(
67            offset,
68            SmfLimitKind::EventCount,
69            actual,
70            self.limits.max_events,
71        )?;
72        self.events = actual;
73        Ok(())
74    }
75
76    pub(crate) fn claim_payload(&mut self, offset: usize, len: usize) -> Result<(), SmfError> {
77        enforce_limit(
78            offset,
79            SmfLimitKind::EventPayloadBytes,
80            len,
81            self.limits.max_event_payload_bytes,
82        )?;
83        let total = self
84            .payload_bytes
85            .checked_add(len)
86            .ok_or(SmfError::LimitExceeded {
87                offset,
88                kind: SmfLimitKind::TotalPayloadBytes,
89                actual: usize::MAX,
90                maximum: self.limits.max_total_payload_bytes,
91            })?;
92        enforce_limit(
93            offset,
94            SmfLimitKind::TotalPayloadBytes,
95            total,
96            self.limits.max_total_payload_bytes,
97        )?;
98        self.payload_bytes = total;
99        Ok(())
100    }
101}
102
103pub(crate) fn enforce_limit(
104    offset: usize,
105    kind: SmfLimitKind,
106    actual: usize,
107    maximum: usize,
108) -> Result<(), SmfError> {
109    if actual > maximum {
110        return Err(SmfError::LimitExceeded {
111            offset,
112            kind,
113            actual,
114            maximum,
115        });
116    }
117    Ok(())
118}