Skip to content

Commit 85bfc0e

Browse files
authored
Encode: add bound check for uint64 > math.Int64 (#785)
As brought up on #782, there is an asymetry between numbers go-toml can encode and decode. Specifically, unsigned numbers larger than `math.Int64` could be encoded but not decoded. We considered two options: allow decoding of those numbers, or prevent those numbers to be decoded. Ultimately we decided to narrow the range of numbers to be encoded. The TOML specification only requires parsers to support at least the 64 bits integer range. Allowing larger numbers would create non-standard TOML documents, which may not be readable (at best) by other implementations. It is a safer default to generate documents valid by default. People who wish to deal with larger numbers can provide a custom type implementing `encoding.TextMarshaler`. Refs #781
1 parent 295a720 commit 85bfc0e

File tree

2 files changed

+25
-1
lines changed

2 files changed

+25
-1
lines changed

marshaler.go

+12-1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,13 @@ func (enc *Encoder) SetIndentTables(indent bool) *Encoder {
107107
// a newline character or a single quote. In that case they are emitted as
108108
// quoted strings.
109109
//
110+
// Unsigned integers larger than math.MaxInt64 cannot be encoded. Doing so
111+
// results in an error. This rule exists because the TOML specification only
112+
// requires parsers to support at least the 64 bits integer range. Allowing
113+
// larger numbers would create non-standard TOML documents, which may not be
114+
// readable (at best) by other implementations. To encode such numbers, a
115+
// solution is a custom type that implements encoding.TextMarshaler.
116+
//
110117
// When encoding structs, fields are encoded in order of definition, with their
111118
// exact name.
112119
//
@@ -303,7 +310,11 @@ func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, e
303310
b = append(b, "false"...)
304311
}
305312
case reflect.Uint64, reflect.Uint32, reflect.Uint16, reflect.Uint8, reflect.Uint:
306-
b = strconv.AppendUint(b, v.Uint(), 10)
313+
x := v.Uint()
314+
if x > uint64(math.MaxInt64) {
315+
return nil, fmt.Errorf("toml: not encoding uint (%d) greater than max int64 (%d)", x, math.MaxInt64)
316+
}
317+
b = strconv.AppendUint(b, x, 10)
307318
case reflect.Int64, reflect.Int32, reflect.Int16, reflect.Int8, reflect.Int:
308319
b = strconv.AppendInt(b, v.Int(), 10)
309320
default:

marshaler_test.go

+13
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,19 @@ func TestLocalTime(t *testing.T) {
11021102
require.Equal(t, expected, string(out))
11031103
}
11041104

1105+
func TestMarshalUint64Overflow(t *testing.T) {
1106+
// The TOML spec only requires implementation to provide support for the
1107+
// int64 range. To avoid generating TOML documents that would not be
1108+
// supported by standard-compliant parsers, uint64 > max int64 cannot be
1109+
// marshaled.
1110+
x := map[string]interface{}{
1111+
"foo": uint64(math.MaxInt64) + 1,
1112+
}
1113+
1114+
_, err := toml.Marshal(x)
1115+
require.Error(t, err)
1116+
}
1117+
11051118
func ExampleMarshal() {
11061119
type MyConfig struct {
11071120
Version int

0 commit comments

Comments
 (0)