From 04c352fad64d2d8268e1f57f6cad7aa8afe5bf59 Mon Sep 17 00:00:00 2001 From: "sashakoshka@tebibyte.media" Date: Fri, 29 Aug 2025 12:03:39 -0400 Subject: [PATCH] tape: Safely cast when dynamically encoding/decoding --- tape/dynamic.go | 8 +++++--- tape/error.go | 3 ++- tape/limits.go | 13 +++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/tape/dynamic.go b/tape/dynamic.go index 5ef9f7c..501ed63 100644 --- a/tape/dynamic.go +++ b/tape/dynamic.go @@ -171,10 +171,12 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i if length > uint64(MaxStructureLength) { return 0, ErrTooLong } + lengthCast, err := Uint64ToIntSafe(length) + if err != nil { return n, err } oneTag, nn, err := decoder.ReadTag() n += nn; if err != nil { return n, err } - if destination.Cap() < int(length) { - destination.Grow(int(length) - destination.Cap()) + if destination.Cap() < lengthCast { + destination.Grow(lengthCast - destination.Cap()) } // skip the rest of the array if the one tag doesn't // match up with the destination @@ -187,7 +189,7 @@ func decodeAnyOrError(decoder *Decoder, destination reflect.Value, tag Tag) (n i break } if err != nil { return n, err } - destination.SetLen(int(length)) + destination.SetLen(lengthCast) for index := range length { nn, err := decodeAny(decoder, destination.Index(int(index)), oneTag) n += nn diff --git a/tape/error.go b/tape/error.go index bf078ff..18a58a2 100644 --- a/tape/error.go +++ b/tape/error.go @@ -2,7 +2,8 @@ package tape // Error enumerates common errors in this package. type Error string; const ( - ErrTooLong Error = "data structure too long" + ErrTooLong Error = "data structure too long" + ErrTooLarge Error = "number too large" ) // Error implements the error interface. diff --git a/tape/limits.go b/tape/limits.go index 21fee47..38c8c1a 100644 --- a/tape/limits.go +++ b/tape/limits.go @@ -11,3 +11,16 @@ package tape // You shouldn't need to change this. If you do, it should only be set once at // the start of the program. var MaxStructureLength = 1024 * 1024 + +// MaxInt is the maximum value an int can hold. This varies depending on the +// system. +const MaxInt int = int(^uint(0) >> 1) + +// Uint64ToIntSafe casts the input to an int if it can be done without overflow, +// or returns an error otherwise. +func Uint64ToIntSafe(input uint64) (int, error) { + if input > uint64(MaxInt) { + return 0, ErrTooLarge + } + return int(input), nil +}