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, 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 break } 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") } case *wire.AckFrequencyFrame: if f.RequestMaxAckDelay < 0 { panic("ACK_FREQUENCY frame with a negative RequestMaxAckDelay") } } }