Skip to content

Commit cf52dcb

Browse files
benluddyk8s-publishing-bot
authored andcommitted
Reject NaN or infinite floating-point values in the CBOR serializer.
Kubernetes-commit: 2a31354e266e829d6d63a776b933947b4118436e
1 parent 04fe518 commit cf52dcb

File tree

4 files changed

+75
-104
lines changed

4 files changed

+75
-104
lines changed

pkg/runtime/serializer/cbor/internal/modes/appendixa_test.go

+9-38
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,6 @@ func TestAppendixA(t *testing.T) {
5656
const (
5757
reasonArrayFixedLength = "indefinite-length arrays are re-encoded with fixed length"
5858
reasonByteString = "strings are encoded as the byte string major type"
59-
reasonFloatPacked = "floats are packed into the smallest value-preserving width"
60-
reasonNaN = "all NaN values are represented with a single encoding"
6159
reasonMapFixedLength = "indefinite-length maps are re-encoded with fixed length"
6260
reasonMapSorted = "map entries are sorted"
6361
reasonStringFixedLength = "indefinite-length strings are re-encoded with fixed length"
@@ -202,68 +200,41 @@ func TestAppendixA(t *testing.T) {
202200
example: hex("fbc010666666666666"),
203201
decoded: -4.1,
204202
},
205-
// TODO: Should Inf/-Inf/NaN be supported? Current Protobuf will encode this, but
206-
// JSON will produce an error. This is less than ideal -- we can't transcode
207-
// everything to JSON.
208203
{
209204
example: hex("f97c00"),
210-
decoded: math.Inf(1),
205+
reject: "floating-point NaN and infinities are not accepted",
211206
},
212207
{
213208
example: hex("f97e00"),
214-
decoded: math.Float64frombits(0x7ff8000000000000),
209+
reject: "floating-point NaN and infinities are not accepted",
215210
},
216211
{
217212
example: hex("f9fc00"),
218-
decoded: math.Inf(-1),
213+
reject: "floating-point NaN and infinities are not accepted",
219214
},
220215
{
221216
example: hex("fa7f800000"),
222-
decoded: math.Inf(1),
223-
encoded: hex("f97c00"),
224-
reasons: []string{
225-
reasonFloatPacked,
226-
},
217+
reject: "floating-point NaN and infinities are not accepted",
227218
},
228219
{
229220
example: hex("fa7fc00000"),
230-
decoded: math.NaN(),
231-
encoded: hex("f97e00"),
232-
reasons: []string{
233-
reasonNaN,
234-
},
221+
reject: "floating-point NaN and infinities are not accepted",
235222
},
236223
{
237224
example: hex("faff800000"),
238-
decoded: math.Inf(-1),
239-
encoded: hex("f9fc00"),
240-
reasons: []string{
241-
reasonFloatPacked,
242-
},
225+
reject: "floating-point NaN and infinities are not accepted",
243226
},
244227
{
245228
example: hex("fb7ff0000000000000"),
246-
decoded: math.Inf(1),
247-
encoded: hex("f97c00"),
248-
reasons: []string{
249-
reasonFloatPacked,
250-
},
229+
reject: "floating-point NaN and infinities are not accepted",
251230
},
252231
{
253232
example: hex("fb7ff8000000000000"),
254-
decoded: math.NaN(),
255-
encoded: hex("f97e00"),
256-
reasons: []string{
257-
reasonNaN,
258-
},
233+
reject: "floating-point NaN and infinities are not accepted",
259234
},
260235
{
261236
example: hex("fbfff0000000000000"),
262-
decoded: math.Inf(-1),
263-
encoded: hex("f9fc00"),
264-
reasons: []string{
265-
reasonFloatPacked,
266-
},
237+
reject: "floating-point NaN and infinities are not accepted",
267238
},
268239
{
269240
example: hex("f4"),

pkg/runtime/serializer/cbor/internal/modes/decode.go

+5
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@ var Decode cbor.DecMode = func() cbor.DecMode {
9191
// For parity with JSON, strings can be decoded into time.Time if they are RFC 3339
9292
// timestamps.
9393
ByteStringToTime: cbor.ByteStringToTimeAllowed,
94+
95+
// Reject NaN and infinite floating-point values since they don't have a JSON
96+
// representation (RFC 8259 Section 6).
97+
NaN: cbor.NaNDecodeForbidden,
98+
Inf: cbor.InfDecodeForbidden,
9499
}.DecMode()
95100
if err != nil {
96101
panic(err)

pkg/runtime/serializer/cbor/internal/modes/decode_test.go

+57-60
Original file line numberDiff line numberDiff line change
@@ -435,92 +435,83 @@ func TestDecode(t *testing.T) {
435435
{
436436
name: "half precision infinity",
437437
in: hex("f97c00"),
438-
assertOnError: func(t *testing.T, e error) {
439-
if e == nil {
440-
t.Fatal("expected non-nil error")
438+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
439+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
440+
t.Errorf("unexpected error diff:\n%s", diff)
441441
}
442-
},
443-
fixme: "NaN and positive/negative infinities should be rejected",
442+
}),
444443
},
445444
{
446445
name: "single precision infinity",
447446
in: hex("fa7f800000"),
448-
assertOnError: func(t *testing.T, e error) {
449-
if e == nil {
450-
t.Fatal("expected non-nil error")
447+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
448+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
449+
t.Errorf("unexpected error diff:\n%s", diff)
451450
}
452-
},
453-
fixme: "NaN and positive/negative infinities should be rejected",
451+
}),
454452
},
455453
{
456454
name: "double precision infinity",
457455
in: hex("fb7ff0000000000000"),
458-
assertOnError: func(t *testing.T, e error) {
459-
if e == nil {
460-
t.Fatal("expected non-nil error")
456+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
457+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
458+
t.Errorf("unexpected error diff:\n%s", diff)
461459
}
462-
},
463-
fixme: "NaN and positive/negative infinities should be rejected",
460+
}),
464461
},
465462
{
466463
name: "half precision negative infinity",
467464
in: hex("f9fc00"),
468-
assertOnError: func(t *testing.T, e error) {
469-
if e == nil {
470-
t.Fatal("expected non-nil error")
465+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
466+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
467+
t.Errorf("unexpected error diff:\n%s", diff)
471468
}
472-
},
473-
fixme: "NaN and positive/negative infinities should be rejected",
469+
}),
474470
},
475471
{
476472
name: "single precision negative infinity",
477473
in: hex("faff800000"),
478-
assertOnError: func(t *testing.T, e error) {
479-
if e == nil {
480-
t.Fatal("expected non-nil error")
474+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
475+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
476+
t.Errorf("unexpected error diff:\n%s", diff)
481477
}
482-
},
483-
fixme: "NaN and positive/negative infinities should be rejected",
478+
}),
484479
},
485480
{
486481
name: "double precision negative infinity",
487482
in: hex("fbfff0000000000000"),
488-
assertOnError: func(t *testing.T, e error) {
489-
if e == nil {
490-
t.Fatal("expected non-nil error")
483+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
484+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
485+
t.Errorf("unexpected error diff:\n%s", diff)
491486
}
492-
},
493-
fixme: "NaN and positive/negative infinities should be rejected",
487+
}),
494488
},
495489
{
496490
name: "half precision NaN",
497491
in: hex("f97e00"),
498-
assertOnError: func(t *testing.T, e error) {
499-
if e == nil {
500-
t.Fatal("expected non-nil error")
492+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
493+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point NaN"}, e); diff != "" {
494+
t.Errorf("unexpected error diff:\n%s", diff)
501495
}
502-
},
503-
fixme: "NaN and positive/negative infinities should be rejected",
496+
}),
504497
},
505498
{
506499
name: "single precision NaN",
507500
in: hex("fa7fc00000"),
508-
assertOnError: func(t *testing.T, e error) {
509-
if e == nil {
510-
t.Fatal("expected non-nil error")
501+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
502+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point NaN"}, e); diff != "" {
503+
t.Errorf("unexpected error diff:\n%s", diff)
511504
}
512-
},
513-
fixme: "NaN and positive/negative infinities should be rejected",
505+
}),
514506
},
515507
{
516508
name: "double precision NaN",
517509
in: hex("fb7ff8000000000000"),
518-
assertOnError: func(t *testing.T, e error) {
519-
if e == nil {
520-
t.Fatal("expected non-nil error")
510+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
511+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point NaN"}, e); diff != "" {
512+
t.Errorf("unexpected error diff:\n%s", diff)
521513
}
522-
},
523-
fixme: "NaN and positive/negative infinities should be rejected",
514+
}),
524515
},
525516
{
526517
name: "smallest nonzero float64",
@@ -718,25 +709,31 @@ func TestDecode(t *testing.T) {
718709
assertOnError: assertNilError,
719710
},
720711
{
721-
name: "tag 1 with a positive infinity",
722-
in: hex("c1f97c00"), // 1(Infinity)
723-
want: "0001-01-01T00:00:00Z",
724-
fixme: "decoding cbor data tagged with 1 produces time.Time instead of RFC3339 timestamp string",
725-
assertOnError: assertNilError,
712+
name: "tag 1 with a positive infinity",
713+
in: hex("c1f97c00"), // 1(Infinity)
714+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
715+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
716+
t.Errorf("unexpected error diff:\n%s", diff)
717+
}
718+
}),
726719
},
727720
{
728-
name: "tag 1 with a negative infinity",
729-
in: hex("c1f9fc00"), // 1(-Infinity)
730-
want: "0001-01-01T00:00:00Z",
731-
fixme: "decoding cbor data tagged with 1 produces time.Time instead of RFC3339 timestamp string",
732-
assertOnError: assertNilError,
721+
name: "tag 1 with a negative infinity",
722+
in: hex("c1f9fc00"), // 1(-Infinity)
723+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
724+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
725+
t.Errorf("unexpected error diff:\n%s", diff)
726+
}
727+
}),
733728
},
734729
{
735-
name: "tag 1 with NaN",
736-
in: hex("c1f9fc00"), // 1(NaN)
737-
want: "0001-01-01T00:00:00Z",
738-
fixme: "decoding cbor data tagged with 1 produces time.Time instead of RFC3339 timestamp string",
739-
assertOnError: assertNilError,
730+
name: "tag 1 with NaN",
731+
in: hex("c1f97e00"), // 1(NaN)
732+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
733+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point NaN"}, e); diff != "" {
734+
t.Errorf("unexpected error diff:\n%s", diff)
735+
}
736+
}),
740737
},
741738
})
742739

pkg/runtime/serializer/cbor/internal/modes/encode.go

+4-6
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,10 @@ var Encode cbor.EncMode = func() cbor.EncMode {
3232
// encoding. Satisfies one of the "Core Deterministic Encoding Requirements".
3333
ShortestFloat: cbor.ShortestFloat16,
3434

35-
// ShortestFloat doesn't apply to NaN or Inf values. Inf values are losslessly
36-
// encoded to float16. RFC 8949 recommends choosing a single representation of NaN
37-
// in applications that do not smuggle additional information inside NaN values, we
38-
// use 0x7e00.
39-
NaNConvert: cbor.NaNConvert7e00,
40-
InfConvert: cbor.InfConvertFloat16,
35+
// Error on attempt to encode NaN and infinite values. This is what the JSON
36+
// serializer does.
37+
NaNConvert: cbor.NaNConvertReject,
38+
InfConvert: cbor.InfConvertReject,
4139

4240
// Prefer encoding math/big.Int to one of the 64-bit integer types if it fits. When
4341
// later decoded into Unstructured, the set of allowable concrete numeric types is

0 commit comments

Comments
 (0)