From 4daa4b59967e588279408aba4c807d198b170e5f Mon Sep 17 00:00:00 2001 From: raphaelroshan Date: Sun, 12 Jul 2026 15:11:40 +0800 Subject: [PATCH] Reject out-of-range and empty integer field values atoi/parseUInt silently overflowed on integer field values that exceed the int range: e.g. "9223372036854775808" wrapped to a negative number and "18446744073709551617" to 1, all returned with a nil error. Because atoi backs FIXInt.Read (MsgSeqNum, BodyLength, resend ranges, etc.), a malformed or oversized field silently corrupted the parsed value instead of being rejected. parseUInt now detects overflow before it happens and returns an error. atoi also length-checks its input before indexing d[0], which previously panicked on an empty integer field. Adds tests for the overflow and empty-input cases. --- fix_int.go | 13 ++++++++++++- fix_int_test.go | 20 ++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/fix_int.go b/fix_int.go index 6bd968003..3a1ed00aa 100644 --- a/fix_int.go +++ b/fix_int.go @@ -17,6 +17,7 @@ package quickfix import ( "errors" + "math" "strconv" ) @@ -31,6 +32,10 @@ const ( // atoi is similar to the function in strconv, but is tuned for ints appearing in FIX field types. func atoi(d []byte) (int, error) { + if len(d) == 0 { + return 0, errors.New("empty bytes") + } + if d[0] == asciiMinus { n, err := parseUInt(d[1:]) return (-1) * n, err @@ -52,7 +57,13 @@ func parseUInt(d []byte) (n int, err error) { return } - n = n*10 + (int(dec) - ascii0) + digit := int(dec) - ascii0 + if n > (math.MaxInt-digit)/10 { + err = errors.New("value out of range") + return + } + + n = n*10 + digit } return diff --git a/fix_int_test.go b/fix_int_test.go index caa9b7840..627b4bf75 100644 --- a/fix_int_test.go +++ b/fix_int_test.go @@ -42,6 +42,26 @@ func TestFIXInt_Int(t *testing.T) { assert.Equal(t, 4, f.Int()) } +func TestFIXInt_ReadOutOfRange(t *testing.T) { + // Out-of-range values must be rejected, not silently wrapped. + for _, tc := range []string{ + "9223372036854775808", // math.MaxInt64 + 1 + "18446744073709551617", // math.MaxUint64 + 1 + "99999999999999999999", + } { + var field FIXInt + err := field.Read([]byte(tc)) + assert.NotNilf(t, err, "expected out-of-range error for %q", tc) + } +} + +func TestFIXInt_ReadEmpty(t *testing.T) { + // An empty value must return an error, not panic on d[0]. + var field FIXInt + err := field.Read([]byte("")) + assert.NotNil(t, err, "expected error for empty bytes") +} + func BenchmarkFIXInt_Read(b *testing.B) { intBytes := []byte("1500") var field FIXInt