Skip to content

Commit 2a31354

Browse files
committed
Reject NaN or infinite floating-point values in the CBOR serializer.
1 parent 6346b9d commit 2a31354

File tree

4 files changed

+75
-104
lines changed

4 files changed

+75
-104
lines changed

staging/src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/appendixa_test.go

Lines changed: 9 additions & 38 deletions
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"),

staging/src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/decode.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,11 @@ var Decode cbor.DecMode = func() cbor.DecMode {
8888
// For parity with JSON, strings can be decoded into time.Time if they are RFC 3339
8989
// timestamps.
9090
ByteStringToTime: cbor.ByteStringToTimeAllowed,
91+
92+
// Reject NaN and infinite floating-point values since they don't have a JSON
93+
// representation (RFC 8259 Section 6).
94+
NaN: cbor.NaNDecodeForbidden,
95+
Inf: cbor.InfDecodeForbidden,
9196
}.DecMode()
9297
if err != nil {
9398
panic(err)

staging/src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/decode_test.go

Lines changed: 57 additions & 60 deletions
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",
@@ -728,25 +719,31 @@ func TestDecode(t *testing.T) {
728719
assertOnError: assertNilError,
729720
},
730721
{
731-
name: "tag 1 with a positive infinity",
732-
in: hex("c1f97c00"), // 1(Infinity)
733-
want: "0001-01-01T00:00:00Z",
734-
fixme: "decoding cbor data tagged with 1 produces time.Time instead of RFC3339 timestamp string",
735-
assertOnError: assertNilError,
722+
name: "tag 1 with a positive infinity",
723+
in: hex("c1f97c00"), // 1(Infinity)
724+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
725+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
726+
t.Errorf("unexpected error diff:\n%s", diff)
727+
}
728+
}),
736729
},
737730
{
738-
name: "tag 1 with a negative infinity",
739-
in: hex("c1f9fc00"), // 1(-Infinity)
740-
want: "0001-01-01T00:00:00Z",
741-
fixme: "decoding cbor data tagged with 1 produces time.Time instead of RFC3339 timestamp string",
742-
assertOnError: assertNilError,
731+
name: "tag 1 with a negative infinity",
732+
in: hex("c1f9fc00"), // 1(-Infinity)
733+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
734+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point infinity"}, e); diff != "" {
735+
t.Errorf("unexpected error diff:\n%s", diff)
736+
}
737+
}),
743738
},
744739
{
745-
name: "tag 1 with NaN",
746-
in: hex("c1f9fc00"), // 1(NaN)
747-
want: "0001-01-01T00:00:00Z",
748-
fixme: "decoding cbor data tagged with 1 produces time.Time instead of RFC3339 timestamp string",
749-
assertOnError: assertNilError,
740+
name: "tag 1 with NaN",
741+
in: hex("c1f97e00"), // 1(NaN)
742+
assertOnError: assertOnConcreteError(func(t *testing.T, e *cbor.UnacceptableDataItemError) {
743+
if diff := cmp.Diff(&cbor.UnacceptableDataItemError{CBORType: "primitives", Message: "floating-point NaN"}, e); diff != "" {
744+
t.Errorf("unexpected error diff:\n%s", diff)
745+
}
746+
}),
750747
},
751748
})
752749

staging/src/k8s.io/apimachinery/pkg/runtime/serializer/cbor/internal/modes/encode.go

Lines changed: 4 additions & 6 deletions
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)