diff --git a/xdr3/decode.go b/xdr3/decode.go index 1863440..11a0ceb 100644 --- a/xdr3/decode.go +++ b/xdr3/decode.go @@ -32,8 +32,12 @@ const maxInt32 = math.MaxInt32 var errMaxSlice = "data exceeds max slice limit" var errIODecode = "%s while decoding %d bytes" -// DecodeDefaultMaxDepth is the default maximum decoding depth -const DecodeDefaultMaxDepth = 250 +// DecodeDefaultMaxDepth is the default maximum decoding depth. +const DecodeDefaultMaxDepth = 1500 + +// DecodeUnlimitedDepth disables the maximum decoding depth limit. Only use it +// for trusted input. +const DecodeUnlimitedDepth = uint(math.MaxUint) // MaxPrealloc is the maximum number of elements pre-allocated when decoding // variable-length arrays. Arrays larger than this are grown incrementally via @@ -45,6 +49,7 @@ type DecodeOptions struct { // MaxDepth is the maximum decoding depth (i.e. maximum nesting of data structures). // It prevents infinite recursions in cyclic datastructures and determines the maximum callstack growth. // If set to 0, DecodeDefaultMaxDepth will be used. + // Set it to DecodeUnlimitedDepth to disable the limit. MaxDepth uint // MaxInputLen sets the maximum input size. It is used by the decoder to sanity-check diff --git a/xdr3/decode_test.go b/xdr3/decode_test.go index 747ebf9..a39f85f 100644 --- a/xdr3/decode_test.go +++ b/xdr3/decode_test.go @@ -1157,6 +1157,41 @@ func TestDecodeMaxDepth(t *testing.T) { assertError(t, "", err, &UnmarshalError{ErrorCode: ErrMaxDecodingDepth}) } +// linkedNode builds arbitrarily deep nesting for depth-limit tests. +type linkedNode struct { + Next *linkedNode +} + +func buildLinkedChain(depth int) *linkedNode { + var head *linkedNode + for i := 0; i < depth; i++ { + head = &linkedNode{Next: head} + } + return head +} + +func TestDecodeUnlimitedDepth(t *testing.T) { + depth := DecodeDefaultMaxDepth + 100 + var buf bytes.Buffer + if _, err := Marshal(&buf, buildLinkedChain(depth)); err != nil { + t.Fatalf("unexpected marshal error: %v", err) + } + + // Default limit rejects the deeply nested input. + bufCopy := buf + var s linkedNode + _, err := NewDecoder(&bufCopy).Decode(&s) + assertError(t, "", err, &UnmarshalError{ErrorCode: ErrMaxDecodingDepth}) + + // DecodeUnlimitedDepth decodes it. + bufCopy = buf + var s2 linkedNode + _, err = NewDecoderWithOptions(&bufCopy, DecodeOptions{MaxDepth: DecodeUnlimitedDepth}).Decode(&s2) + if err != nil { + t.Fatalf("unexpected error decoding with unlimited depth: %v", err) + } +} + func TestDecodeMaxAllocationCheck_ImplicitLenReader(t *testing.T) { var buf bytes.Buffer _, err := Marshal(&buf, "thisstringis23charslong")