mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 11:50:44 +00:00
Merge pull request #125423 from benluddy/cbor-nan-inf
KEP-4222: Reject NaN or infinite floating-point values in the CBOR serializer.
This commit is contained in:
commit
4cd4e35061
@ -56,8 +56,6 @@ func TestAppendixA(t *testing.T) {
|
||||
const (
|
||||
reasonArrayFixedLength = "indefinite-length arrays are re-encoded with fixed length"
|
||||
reasonByteString = "strings are encoded as the byte string major type"
|
||||
reasonFloatPacked = "floats are packed into the smallest value-preserving width"
|
||||
reasonNaN = "all NaN values are represented with a single encoding"
|
||||
reasonMapFixedLength = "indefinite-length maps are re-encoded with fixed length"
|
||||
reasonMapSorted = "map entries are sorted"
|
||||
reasonStringFixedLength = "indefinite-length strings are re-encoded with fixed length"
|
||||
@ -202,68 +200,41 @@ func TestAppendixA(t *testing.T) {
|
||||
example: hex("fbc010666666666666"),
|
||||
decoded: -4.1,
|
||||
},
|
||||
// TODO: Should Inf/-Inf/NaN be supported? Current Protobuf will encode this, but
|
||||
// JSON will produce an error. This is less than ideal -- we can't transcode
|
||||
// everything to JSON.
|
||||
{
|
||||
example: hex("f97c00"),
|
||||
decoded: math.Inf(1),
|
||||
reject: "floating-point NaN and infinities are not accepted",
|
||||
},
|
||||
{
|
||||
example: hex("f97e00"),
|
||||
decoded: math.Float64frombits(0x7ff8000000000000),
|
||||
reject: "floating-point NaN and infinities are not accepted",
|
||||
},
|
||||
{
|
||||
example: hex("f9fc00"),
|
||||
decoded: math.Inf(-1),
|
||||
reject: "floating-point NaN and infinities are not accepted",
|
||||
},
|
||||
{
|
||||
example: hex("fa7f800000"),
|
||||
decoded: math.Inf(1),
|
||||
encoded: hex("f97c00"),
|
||||
reasons: []string{
|
||||
reasonFloatPacked,
|
||||
},
|
||||
reject: "floating-point NaN and infinities are not accepted",
|
||||
},
|
||||
{
|
||||
example: hex("fa7fc00000"),
|
||||
decoded: math.NaN(),
|
||||
encoded: hex("f97e00"),
|
||||
reasons: []string{
|
||||
reasonNaN,
|
||||
},
|
||||
reject: "floating-point NaN and infinities are not accepted",
|
||||
},
|
||||
{
|
||||
example: hex("faff800000"),
|
||||
decoded: math.Inf(-1),
|
||||
encoded: hex("f9fc00"),
|
||||
reasons: []string{
|
||||
reasonFloatPacked,
|
||||
},
|
||||
reject: "floating-point NaN and infinities are not accepted",
|
||||
},
|
||||
{
|
||||
example: hex("fb7ff0000000000000"),
|
||||
decoded: math.Inf(1),
|
||||
encoded: hex("f97c00"),
|
||||
reasons: []string{
|
||||
reasonFloatPacked,
|
||||
},
|
||||
reject: "floating-point NaN and infinities are not accepted",
|
||||
},
|
||||
{
|
||||
example: hex("fb7ff8000000000000"),
|
||||
decoded: math.NaN(),
|
||||
encoded: hex("f97e00"),
|
||||
reasons: []string{
|
||||
reasonNaN,
|
||||
},
|
||||
reject: "floating-point NaN and infinities are not accepted",
|
||||
},
|
||||
{
|
||||
example: hex("fbfff0000000000000"),
|
||||
decoded: math.Inf(-1),
|
||||
encoded: hex("f9fc00"),
|
||||
reasons: []string{
|
||||
reasonFloatPacked,
|
||||
},
|
||||
reject: "floating-point NaN and infinities are not accepted",
|
||||
},
|
||||
{
|
||||
example: hex("f4"),
|
||||
|
@ -91,6 +91,11 @@ var Decode cbor.DecMode = func() cbor.DecMode {
|
||||
// For parity with JSON, strings can be decoded into time.Time if they are RFC 3339
|
||||
// timestamps.
|
||||
ByteStringToTime: cbor.ByteStringToTimeAllowed,
|
||||
|
||||
// Reject NaN and infinite floating-point values since they don't have a JSON
|
||||
// representation (RFC 8259 Section 6).
|
||||
NaN: cbor.NaNDecodeForbidden,
|
||||
Inf: cbor.InfDecodeForbidden,
|
||||
}.DecMode()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@ -435,92 +435,83 @@ func TestDecode(t *testing.T) {
|
||||
{
|
||||
name: "half precision infinity",
|
||||
in: hex("f97c00"),
|
||||
assertOnError: func(t *testing.T, e error) {
|
||||
if e == nil {
|
||||
t.Fatal("expected non-nil error")
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
},
|
||||
fixme: "NaN and positive/negative infinities should be rejected",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "single precision infinity",
|
||||
in: hex("fa7f800000"),
|
||||
assertOnError: func(t *testing.T, e error) {
|
||||
if e == nil {
|
||||
t.Fatal("expected non-nil error")
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
},
|
||||
fixme: "NaN and positive/negative infinities should be rejected",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "double precision infinity",
|
||||
in: hex("fb7ff0000000000000"),
|
||||
assertOnError: func(t *testing.T, e error) {
|
||||
if e == nil {
|
||||
t.Fatal("expected non-nil error")
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
},
|
||||
fixme: "NaN and positive/negative infinities should be rejected",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "half precision negative infinity",
|
||||
in: hex("f9fc00"),
|
||||
assertOnError: func(t *testing.T, e error) {
|
||||
if e == nil {
|
||||
t.Fatal("expected non-nil error")
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
},
|
||||
fixme: "NaN and positive/negative infinities should be rejected",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "single precision negative infinity",
|
||||
in: hex("faff800000"),
|
||||
assertOnError: func(t *testing.T, e error) {
|
||||
if e == nil {
|
||||
t.Fatal("expected non-nil error")
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
},
|
||||
fixme: "NaN and positive/negative infinities should be rejected",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "double precision negative infinity",
|
||||
in: hex("fbfff0000000000000"),
|
||||
assertOnError: func(t *testing.T, e error) {
|
||||
if e == nil {
|
||||
t.Fatal("expected non-nil error")
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
},
|
||||
fixme: "NaN and positive/negative infinities should be rejected",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "half precision NaN",
|
||||
in: hex("f97e00"),
|
||||
assertOnError: func(t *testing.T, e error) {
|
||||
if e == nil {
|
||||
t.Fatal("expected non-nil error")
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point NaN"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
},
|
||||
fixme: "NaN and positive/negative infinities should be rejected",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "single precision NaN",
|
||||
in: hex("fa7fc00000"),
|
||||
assertOnError: func(t *testing.T, e error) {
|
||||
if e == nil {
|
||||
t.Fatal("expected non-nil error")
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point NaN"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
},
|
||||
fixme: "NaN and positive/negative infinities should be rejected",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "double precision NaN",
|
||||
in: hex("fb7ff8000000000000"),
|
||||
assertOnError: func(t *testing.T, e error) {
|
||||
if e == nil {
|
||||
t.Fatal("expected non-nil error")
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point NaN"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
},
|
||||
fixme: "NaN and positive/negative infinities should be rejected",
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "smallest nonzero float64",
|
||||
@ -718,25 +709,31 @@ func TestDecode(t *testing.T) {
|
||||
assertOnError: assertNilError,
|
||||
},
|
||||
{
|
||||
name: "tag 1 with a positive infinity",
|
||||
in: hex("c1f97c00"), // 1(Infinity)
|
||||
want: "0001-01-01T00:00:00Z",
|
||||
fixme: "decoding cbor data tagged with 1 produces time.Time instead of RFC3339 timestamp string",
|
||||
assertOnError: assertNilError,
|
||||
name: "tag 1 with a positive infinity",
|
||||
in: hex("c1f97c00"), // 1(Infinity)
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "tag 1 with a negative infinity",
|
||||
in: hex("c1f9fc00"), // 1(-Infinity)
|
||||
want: "0001-01-01T00:00:00Z",
|
||||
fixme: "decoding cbor data tagged with 1 produces time.Time instead of RFC3339 timestamp string",
|
||||
assertOnError: assertNilError,
|
||||
name: "tag 1 with a negative infinity",
|
||||
in: hex("c1f9fc00"), // 1(-Infinity)
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
}),
|
||||
},
|
||||
{
|
||||
name: "tag 1 with NaN",
|
||||
in: hex("c1f9fc00"), // 1(NaN)
|
||||
want: "0001-01-01T00:00:00Z",
|
||||
fixme: "decoding cbor data tagged with 1 produces time.Time instead of RFC3339 timestamp string",
|
||||
assertOnError: assertNilError,
|
||||
name: "tag 1 with NaN",
|
||||
in: hex("c1f97e00"), // 1(NaN)
|
||||
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
|
||||
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point NaN"}, e); diff != "" {
|
||||
t.Errorf("unexpected error diff:\n%s", diff)
|
||||
}
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
|
@ -32,12 +32,10 @@ var Encode cbor.EncMode = func() cbor.EncMode {
|
||||
// encoding. Satisfies one of the "Core Deterministic Encoding Requirements".
|
||||
ShortestFloat: cbor.ShortestFloat16,
|
||||
|
||||
// ShortestFloat doesn't apply to NaN or Inf values. Inf values are losslessly
|
||||
// encoded to float16. RFC 8949 recommends choosing a single representation of NaN
|
||||
// in applications that do not smuggle additional information inside NaN values, we
|
||||
// use 0x7e00.
|
||||
NaNConvert: cbor.NaNConvert7e00,
|
||||
InfConvert: cbor.InfConvertFloat16,
|
||||
// Error on attempt to encode NaN and infinite values. This is what the JSON
|
||||
// serializer does.
|
||||
NaNConvert: cbor.NaNConvertReject,
|
||||
InfConvert: cbor.InfConvertReject,
|
||||
|
||||
// Prefer encoding math/big.Int to one of the 64-bit integer types if it fits. When
|
||||
// later decoded into Unstructured, the set of allowable concrete numeric types is
|
||||
|
Loading…
Reference in New Issue
Block a user