Headline
CVE-2022-30591: quic-go/mtu_discoverer.go at 84e03e59760ceee37359688871bb0688fcc4e98f · lucas-clemente/quic-go
** DISPUTED ** quic-go through 0.27.0 allows remote attackers to cause a denial of service (CPU consumption) via a Slowloris variant in which incomplete QUIC or HTTP/3 requests are sent. This occurs because mtu_discoverer.go misparses the MTU Discovery service and consequently overflows the probe timer. NOTE: the vendor’s position is that this behavior should not be listed as a vulnerability on the CVE List.
Permalink
Cannot retrieve contributors at this time
package quic
import (
“time”
“github.com/lucas-clemente/quic-go/internal/ackhandler”
“github.com/lucas-clemente/quic-go/internal/protocol”
“github.com/lucas-clemente/quic-go/internal/utils”
“github.com/lucas-clemente/quic-go/internal/wire”
)
type mtuDiscoverer interface {
ShouldSendProbe(now time.Time) bool
NextProbeTime() time.Time
GetPing() (ping ackhandler.Frame, datagramSize protocol.ByteCount)
}
const (
// At some point, we have to stop searching for a higher MTU.
// We’re happy to send a packet that’s 10 bytes smaller than the actual MTU.
maxMTUDiff = 20
// send a probe packet every mtuProbeDelay RTTs
mtuProbeDelay = 5
)
type mtuFinder struct {
lastProbeTime time.Time
probeInFlight bool
mtuIncreased func(protocol.ByteCount)
rttStats *utils.RTTStats
current protocol.ByteCount
max protocol.ByteCount // the maximum value, as advertised by the peer (or our maximum size buffer)
}
var _ mtuDiscoverer = &mtuFinder{}
func newMTUDiscoverer(rttStats *utils.RTTStats, start, max protocol.ByteCount, mtuIncreased func(protocol.ByteCount)) mtuDiscoverer {
return &mtuFinder{
current: start,
rttStats: rttStats,
lastProbeTime: time.Now(), // to make sure the first probe packet is not sent immediately
mtuIncreased: mtuIncreased,
max: max,
}
}
func (f *mtuFinder) done() bool {
return f.max-f.current <= maxMTUDiff+1
}
func (f *mtuFinder) ShouldSendProbe(now time.Time) bool {
if f.probeInFlight || f.done() {
return false
}
return !now.Before(f.NextProbeTime())
}
// NextProbeTime returns the time when the next probe packet should be sent.
// It returns the zero value if no probe packet should be sent.
func (f *mtuFinder) NextProbeTime() time.Time {
if f.probeInFlight || f.done() {
return time.Time{}
}
return f.lastProbeTime.Add(mtuProbeDelay * f.rttStats.SmoothedRTT())
}
func (f *mtuFinder) GetPing() (ackhandler.Frame, protocol.ByteCount) {
size := (f.max + f.current) / 2
f.lastProbeTime = time.Now()
f.probeInFlight = true
return ackhandler.Frame{
Frame: &wire.PingFrame{},
OnLost: func(wire.Frame) {
f.probeInFlight = false
f.max = size
},
OnAcked: func(wire.Frame) {
f.probeInFlight = false
f.current = size
f.mtuIncreased(size)
},
}, size
}