File: fuzz.go

package info (click to toggle)
golang-github-lucas-clemente-quic-go 0.54.0-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 4,312 kB
  • sloc: sh: 54; makefile: 7
file content (157 lines) | stat: -rw-r--r-- 4,377 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
package frames

import (
	"fmt"
	"io"

	"github.com/quic-go/quic-go/internal/ackhandler"
	"github.com/quic-go/quic-go/internal/protocol"
	"github.com/quic-go/quic-go/internal/wire"
)

const version = protocol.Version1

// PrefixLen is the number of bytes used for configuration
const PrefixLen = 1

func toEncLevel(v uint8) protocol.EncryptionLevel {
	switch v % 3 {
	default:
		return protocol.EncryptionInitial
	case 1:
		return protocol.EncryptionHandshake
	case 2:
		return protocol.Encryption1RTT
	}
}

// Fuzz fuzzes the QUIC frames.
//
//go:generate go run ./cmd/corpus.go
func Fuzz(data []byte) int {
	if len(data) < PrefixLen {
		return 0
	}
	encLevel := toEncLevel(data[0])
	data = data[PrefixLen:]

	parser := wire.NewFrameParser(true, true)
	parser.SetAckDelayExponent(protocol.DefaultAckDelayExponent)

	var numFrames int
	var b []byte
	for len(data) > 0 {
		initialLen := len(data)
		frameType, l, err := parser.ParseType(data, encLevel)
		if err != nil {
			if err == io.EOF { // the last frame was a PADDING frame
				continue
			}
			break
		}

		data = data[l:]
		numFrames++

		var f wire.Frame
		switch {
		case frameType.IsStreamFrameType():
			f, l, err = parser.ParseStreamFrame(frameType, data, version)
		case frameType == wire.FrameTypeAck || frameType == wire.FrameTypeAckECN:
			f, l, err = parser.ParseAckFrame(frameType, data, encLevel, version)
		case frameType == wire.FrameTypeDatagramNoLength || frameType == wire.FrameTypeDatagramWithLength:
			f, l, err = parser.ParseDatagramFrame(frameType, data, version)
		default:
			f, l, err = parser.ParseLessCommonFrame(frameType, data, version)
		}
		if err != nil {
			break
		}
		data = data[l:]
		wire.IsProbingFrame(f)
		ackhandler.IsFrameAckEliciting(f)
		// We accept empty STREAM frames, but we don't write them.
		if sf, ok := f.(*wire.StreamFrame); ok {
			if sf.DataLen() == 0 {
				sf.PutBack()
				continue
			}
		}
		validateFrame(f)

		startLen := len(b)
		parsedLen := initialLen - len(data)
		b, err = f.Append(b, version)
		if err != nil {
			panic(fmt.Sprintf("error writing frame %#v: %s", f, err))
		}
		frameLen := protocol.ByteCount(len(b) - startLen)
		if f.Length(version) != frameLen {
			panic(fmt.Sprintf("inconsistent frame length for %#v: expected %d, got %d", f, frameLen, f.Length(version)))
		}
		if sf, ok := f.(*wire.StreamFrame); ok {
			sf.PutBack()
		}
		if frameLen > protocol.ByteCount(parsedLen) {
			panic(fmt.Sprintf("serialized length (%d) is longer than parsed length (%d)", len(b), parsedLen))
		}
	}

	if numFrames == 0 {
		return 0
	}
	return 1
}

func validateFrame(frame wire.Frame) {
	switch f := frame.(type) {
	case *wire.StreamFrame:
		if protocol.ByteCount(len(f.Data)) != f.DataLen() {
			panic("STREAM frame: inconsistent data length")
		}
	case *wire.AckFrame:
		if f.DelayTime < 0 {
			panic(fmt.Sprintf("invalid ACK delay_time: %s", f.DelayTime))
		}
		if f.LargestAcked() < f.LowestAcked() {
			panic("ACK: largest acknowledged is smaller than lowest acknowledged")
		}
		for _, r := range f.AckRanges {
			if r.Largest < 0 || r.Smallest < 0 {
				panic("ACK range contains a negative packet number")
			}
		}
		if !f.AcksPacket(f.LargestAcked()) {
			panic("ACK frame claims that largest acknowledged is not acknowledged")
		}
		if !f.AcksPacket(f.LowestAcked()) {
			panic("ACK frame claims that lowest acknowledged is not acknowledged")
		}
		_ = f.AcksPacket(100)
		_ = f.AcksPacket((f.LargestAcked() + f.LowestAcked()) / 2)
	case *wire.NewConnectionIDFrame:
		if f.ConnectionID.Len() < 1 || f.ConnectionID.Len() > 20 {
			panic(fmt.Sprintf("invalid NEW_CONNECTION_ID frame length: %s", f.ConnectionID))
		}
	case *wire.NewTokenFrame:
		if len(f.Token) == 0 {
			panic("NEW_TOKEN frame with an empty token")
		}
	case *wire.MaxStreamsFrame:
		if f.MaxStreamNum > protocol.MaxStreamCount {
			panic("MAX_STREAMS frame with an invalid Maximum Streams value")
		}
	case *wire.StreamsBlockedFrame:
		if f.StreamLimit > protocol.MaxStreamCount {
			panic("STREAMS_BLOCKED frame with an invalid Maximum Streams value")
		}
	case *wire.ConnectionCloseFrame:
		if f.IsApplicationError && f.FrameType != 0 {
			panic("CONNECTION_CLOSE for an application error containing a frame type")
		}
	case *wire.ResetStreamFrame:
		if f.FinalSize < f.ReliableSize {
			panic("RESET_STREAM frame with a FinalSize smaller than the ReliableSize")
		}
	}
}