File quic-go.patch of Package dnscrypt-proxy

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{}{}
openSUSE Build Service is sponsored by