@@ -116,6 +116,26 @@ type Decoder struct {
116116 currentFieldOpt option
117117
118118 encoding Encoding
119+
120+ // maxSliceLen caps the number of elements a wire-declared slice length
121+ // prefix is allowed to claim before MakeSlice is called. Zero means
122+ // unlimited (backward compatible). Non-zero callers typically set this
123+ // when parsing untrusted input (e.g. RPC/WS payloads) to bound the
124+ // allocation that can result from a malicious length prefix.
125+ //
126+ // The natural bound "l <= Remaining()" is already enforced — but it
127+ // treats every element as taking at least 1 wire byte, so a []BigStruct
128+ // where BigStruct is 1 KiB in memory can still produce a 1 KiB * l
129+ // allocation from only l wire bytes. maxSliceLen lets the caller cap
130+ // the element count directly.
131+ maxSliceLen int
132+
133+ // maxMapLen is the analogous cap for map length prefixes. Unlike
134+ // slices, maps historically had no bound at all: a length of 2^32
135+ // would run 2^32 SetMapIndex iterations. With maxMapLen set, or by
136+ // virtue of the Remaining()/2 lower bound always enforced now, the
137+ // decoder fails fast instead.
138+ maxMapLen int
119139}
120140
121141// Reset resets the decoder to decode a new message.
@@ -152,6 +172,116 @@ func (dec *Decoder) SetEncoding(enc Encoding) {
152172 dec .encoding = enc
153173}
154174
175+ // SetMaxSliceLen sets a hard cap on wire-declared slice lengths. A length
176+ // prefix that claims more than n elements makes Decode fail with an error
177+ // before any MakeSlice is called. Pass 0 to disable (unlimited, the
178+ // default).
179+ //
180+ // Use this when decoding untrusted input. A safe starting value is an
181+ // application-specific bound, e.g. 256 for Solana transaction account
182+ // lists or 1024 for instruction data blobs.
183+ func (dec * Decoder ) SetMaxSliceLen (n int ) * Decoder {
184+ dec .maxSliceLen = n
185+ return dec
186+ }
187+
188+ // SetMaxMapLen sets a hard cap on wire-declared map lengths. See
189+ // SetMaxSliceLen for rationale. Defaults to 0 (unlimited).
190+ func (dec * Decoder ) SetMaxMapLen (n int ) * Decoder {
191+ dec .maxMapLen = n
192+ return dec
193+ }
194+
195+ // MaxSliceLen returns the configured slice-length cap, or 0 for unlimited.
196+ func (dec * Decoder ) MaxSliceLen () int { return dec .maxSliceLen }
197+
198+ // MaxMapLen returns the configured map-length cap, or 0 for unlimited.
199+ func (dec * Decoder ) MaxMapLen () int { return dec .maxMapLen }
200+
201+ // ErrSliceLenTooLarge is returned when a decoded slice length prefix
202+ // exceeds the caller-configured MaxSliceLen cap or is negative. When the
203+ // length merely overruns the wire buffer (i.e. there are not enough
204+ // bytes left to decode l elements), the decoder returns
205+ // io.ErrUnexpectedEOF instead to preserve backward compatibility with
206+ // error-handling code that has long keyed off of it.
207+ var ErrSliceLenTooLarge = errors .New ("decode: slice length exceeds bound" )
208+
209+ // ErrMapLenTooLarge is returned when a decoded map length prefix exceeds
210+ // the MaxMapLen cap or is negative. As with ErrSliceLenTooLarge, the
211+ // "not enough bytes" case returns io.ErrUnexpectedEOF.
212+ var ErrMapLenTooLarge = errors .New ("decode: map length exceeds bound" )
213+
214+ // checkSliceLen validates a wire-declared slice length before it is used
215+ // to allocate a slice. elemMinSize is the minimum number of wire bytes a
216+ // single element must consume — pass 1 for variable-size element types
217+ // (strings, nested slices, general structs), or the exact fixed size for
218+ // PoD elements. Returns ErrSliceLenTooLarge for pathological inputs
219+ // (negative length, cap violation), or io.ErrUnexpectedEOF when the
220+ // claimed payload simply won't fit in Remaining() bytes.
221+ //
222+ // Uses int64 arithmetic so l * elemMinSize cannot wrap on 32-bit hosts.
223+ func (dec * Decoder ) checkSliceLen (l , elemMinSize int ) error {
224+ if l < 0 {
225+ return fmt .Errorf ("%w: negative length %d" , ErrSliceLenTooLarge , l )
226+ }
227+ if dec .maxSliceLen > 0 && l > dec .maxSliceLen {
228+ return fmt .Errorf ("%w: length %d > MaxSliceLen=%d" , ErrSliceLenTooLarge , l , dec .maxSliceLen )
229+ }
230+ if elemMinSize <= 0 {
231+ elemMinSize = 1
232+ }
233+ if int64 (l )* int64 (elemMinSize ) > int64 (dec .Remaining ()) {
234+ return io .ErrUnexpectedEOF
235+ }
236+ return nil
237+ }
238+
239+ // sliceElemMinWireSize returns a conservative lower bound on how many wire
240+ // bytes a single element of the given type must consume. Used by
241+ // checkSliceLen to tighten the "wire length * elem >= allocation" check
242+ // for homogeneous fixed-size element kinds. For variable-size elements
243+ // (structs, strings, nested slices) it returns 1 — the true lower bound
244+ // without knowing the concrete wire layout.
245+ //
246+ // Note: this is *wire* size, not Go memory size. For a type alias like
247+ // `type PublicKey [32]byte` the wire form is 32 bytes regardless of how
248+ // the Go type is declared.
249+ func sliceElemMinWireSize (t reflect.Type ) int {
250+ switch t .Kind () {
251+ case reflect .Uint8 , reflect .Int8 , reflect .Bool :
252+ return 1
253+ case reflect .Uint16 , reflect .Int16 :
254+ return TypeSizeUint16
255+ case reflect .Uint32 , reflect .Int32 , reflect .Float32 :
256+ return TypeSizeUint32
257+ case reflect .Uint64 , reflect .Int64 , reflect .Float64 :
258+ return TypeSizeUint64
259+ case reflect .Array :
260+ // [N]T with T fixed-size becomes N * minWireSize(T). Recurse.
261+ per := sliceElemMinWireSize (t .Elem ())
262+ return t .Len () * per
263+ default :
264+ return 1
265+ }
266+ }
267+
268+ // checkMapLen validates a wire-declared map length before MakeMap /
269+ // SetMapIndex loops run. Each entry consumes at least two wire bytes
270+ // (one for key, one for value) so Remaining()/2 is the natural upper
271+ // bound in addition to the caller's MaxMapLen cap.
272+ func (dec * Decoder ) checkMapLen (l int ) error {
273+ if l < 0 {
274+ return fmt .Errorf ("%w: negative length %d" , ErrMapLenTooLarge , l )
275+ }
276+ if dec .maxMapLen > 0 && l > dec .maxMapLen {
277+ return fmt .Errorf ("%w: length %d > MaxMapLen=%d" , ErrMapLenTooLarge , l , dec .maxMapLen )
278+ }
279+ if int64 (l )* 2 > int64 (dec .Remaining ()) {
280+ return io .ErrUnexpectedEOF
281+ }
282+ return nil
283+ }
284+
155285func NewBinDecoder (data []byte ) * Decoder {
156286 return NewDecoderWithEncoding (data , EncodingBin )
157287}
0 commit comments