Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions xdr3/decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
35 changes: 35 additions & 0 deletions xdr3/decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Loading