dnscrypt-proxy/quic-go.patch

118 lines
3.6 KiB
Diff

From: cunix@mail.de
Date: 2024-04-21 12:00:00
Subject: Memory Exhaustion Attack against QUIC's Connection ID Mechanism
References: https://github.com/quic-go/quic-go/commit/4a99b816ae3ab03ae5449d15aac45147c85ed47a
https://github.com/quic-go/quic-go/security/advisories/GHSA-c33x-xqrf-c478
https://bugzilla.suse.com/show_bug.cgi?id=1222473
This tries to backport commit
https://github.com/quic-go/quic-go/commit/4a99b816ae3ab03ae5449d15aac45147c85ed47a.patch
from Marten Seemann <martenseemann@gmail.com>
to the vendored older version of quic-go.
dnscrypt-proxy upstream already vendors version 0.42 of quic-go with hack
included, but is not released.
Patch should be dropped with next release of dnscrypt-proxy.
---
diff -r -U 5 a/vendor/github.com/quic-go/quic-go/connection.go b/vendor/github.com/quic-go/quic-go/connection.go
--- a/vendor/github.com/quic-go/quic-go/connection.go
+++ b/vendor/github.com/quic-go/quic-go/connection.go
@@ -516,11 +516,14 @@
var sendQueueAvailable <-chan struct{}
runLoop:
for {
- // Close immediately if requested
+ if s.framer.QueuedTooManyControlFrames() {
+ s.closeLocal(&qerr.TransportError{ErrorCode: InternalError})
+ }
+ // Close immediately if requested
select {
case closeErr = <-s.closeChan:
break runLoop
default:
}
diff -r -U 5 a/vendor/github.com/quic-go/quic-go/framer.go b/vendor/github.com/quic-go/quic-go/framer.go
--- a/vendor/github.com/quic-go/quic-go/framer.go
+++ b/vendor/github.com/quic-go/quic-go/framer.go
@@ -19,22 +19,32 @@
AddActiveStream(protocol.StreamID)
AppendStreamFrames([]ackhandler.StreamFrame, protocol.ByteCount, protocol.VersionNumber) ([]ackhandler.StreamFrame, protocol.ByteCount)
Handle0RTTRejection() error
+
+ // QueuedTooManyControlFrames says if the control frame queue exceeded its maximum queue length.
+ // This is a hack.
+ // It is easier to implement than propagating an error return value in QueueControlFrame.
+ // The correct solution would be to queue frames with their respective structs.
+ // See https://github.com/quic-go/quic-go/issues/4271 for the queueing of stream-related control frames.
+ QueuedTooManyControlFrames() bool
}
+const maxControlFrames = 16 << 10
+
type framerI struct {
mutex sync.Mutex
streamGetter streamGetter
activeStreams map[protocol.StreamID]struct{}
streamQueue ringbuffer.RingBuffer[protocol.StreamID]
controlFrameMutex sync.Mutex
controlFrames []wire.Frame
+ queuedTooManyControlFrames bool
}
var _ framer = &framerI{}
func newFramer(streamGetter streamGetter) framer {
@@ -56,11 +66,24 @@
f.controlFrameMutex.Unlock()
return hasData
}
func (f *framerI) QueueControlFrame(frame wire.Frame) {
+ var returnearly bool
f.controlFrameMutex.Lock()
+ // This is a hack.
+ if len(f.controlFrames) >= maxControlFrames {
+ returnearly = true
+ }
+ f.controlFrameMutex.Unlock()
+ if returnearly {
+ f.mutex.Lock()
+ f.queuedTooManyControlFrames = true
+ f.mutex.Unlock()
+ return
+ }
+ f.controlFrameMutex.Lock()
f.controlFrames = append(f.controlFrames, frame)
f.controlFrameMutex.Unlock()
}
func (f *framerI) AppendControlFrames(frames []ackhandler.Frame, maxLen protocol.ByteCount, v protocol.VersionNumber) ([]ackhandler.Frame, protocol.ByteCount) {
@@ -78,10 +101,17 @@
}
f.controlFrameMutex.Unlock()
return frames, length
}
+func (f *framerI) QueuedTooManyControlFrames() bool {
+ f.mutex.Lock()
+ toomany := f.queuedTooManyControlFrames
+ f.mutex.Unlock()
+ return toomany
+}
+
func (f *framerI) AddActiveStream(id protocol.StreamID) {
f.mutex.Lock()
if _, ok := f.activeStreams[id]; !ok {
f.streamQueue.PushBack(id)
f.activeStreams[id] = struct{}{}