new file mode 100644
@@ -0,0 +1,166 @@
+From 490bfb28002fc0253010243ac387c756014c06e5 Mon Sep 17 00:00:00 2001
+From: Alexander Sosedkin <asosedkin@redhat.com>
+Date: Mon, 23 Mar 2026 15:09:43 +0100
+Subject: [PATCH] buffers: switch from end_offset over to frag_length
+
+Instead of maintaining an inclusive [start_offset, end_offset] range
+when reassembling DTLS handshake,
+track start_offset and a relative frag_length instead.
+
+You'd think it'd be a no-op, but it fixes:
+
+* 0-length fragments triggering completion if message was 1 byte long
+* a remotely triggerable underflow and an ensuing heap overrun
+
+Reported-by: Joshua Rogers of AISLE Research Team <joshua@joshua.hu>
+Fixes: #1811
+Fixes: CVE-2026-33845
+Fixes: GNUTLS-SA-2026-04-29-3
+CVSS: 7.5 High CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
+Signed-off-by: Alexander Sosedkin <asosedkin@redhat.com>
+
+CVE: CVE-2026-33845
+Upstream-Status: Backport [https://gitlab.com/gnutls/gnutls/-/commit/e5b72c53c7d789d19d1d1cd10b275e87d0415413]
+Signed-off-by: Adarsh Jagadish Kamini <adarsh.jagadish.kamini@est.tech>
+---
+ lib/buffers.c | 50 +++++++++++++++++++++++++-----------------------
+ lib/gnutls_int.h | 4 ++--
+ 2 files changed, 28 insertions(+), 26 deletions(-)
+
+diff --git a/lib/buffers.c b/lib/buffers.c
+index 09779a8f3..02cc222c4 100644
+--- a/lib/buffers.c
++++ b/lib/buffers.c
+@@ -919,10 +919,7 @@ static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel,
+ }
+ data_size = _mbuffer_get_udata_size(bufel) - handshake_header_size;
+
+- if (frag_size > 0)
+- hsk->end_offset = hsk->start_offset + frag_size - 1;
+- else
+- hsk->end_offset = 0;
++ hsk->frag_length = frag_size;
+
+ _gnutls_handshake_log(
+ "HSK[%p]: %s (%u) was received. Length %d[%d], frag offset %d, frag length: %d, sequence: %d\n",
+@@ -936,9 +933,10 @@ static int parse_handshake_header(gnutls_session_t session, mbuffer_st *bufel,
+
+ if (hsk->length > 0 &&
+ (frag_size > data_size ||
+- (frag_size > 0 && hsk->end_offset >= hsk->length))) {
++ (frag_size > 0 && hsk->start_offset + frag_size > hsk->length))) {
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+- } else if (hsk->length == 0 && hsk->end_offset != 0 &&
++ } else if (hsk->length == 0 &&
++ hsk->start_offset + frag_size != hsk->start_offset &&
+ hsk->start_offset != 0)
+ return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
+
+@@ -1002,11 +1000,10 @@ static int merge_handshake_packet(gnutls_session_t session,
+ hsk->data.length = hsk->length;
+ }
+
+- if (hsk->length > 0 && hsk->end_offset > 0 &&
+- hsk->end_offset - hsk->start_offset + 1 != hsk->length) {
++ if (hsk->length > 0 && hsk->frag_length > 0 &&
++ hsk->frag_length != hsk->length) {
+ memmove(&hsk->data.data[hsk->start_offset],
+- hsk->data.data,
+- hsk->end_offset - hsk->start_offset + 1);
++ hsk->data.data, hsk->frag_length);
+ }
+
+ session->internals.handshake_recv_buffer_size++;
+@@ -1040,20 +1037,27 @@ static int merge_handshake_packet(gnutls_session_t session,
+ }
+
+ if (hsk->start_offset < recv_buf[pos].start_offset &&
+- hsk->end_offset + 1 >= recv_buf[pos].start_offset) {
++ hsk->start_offset + hsk->frag_length >=
++ recv_buf[pos].start_offset) {
+ memcpy(&recv_buf[pos].data.data[hsk->start_offset],
+ hsk->data.data, hsk->data.length);
+ recv_buf[pos].start_offset = hsk->start_offset;
+- recv_buf[pos].end_offset =
+- MIN(hsk->end_offset, recv_buf[pos].end_offset);
+- } else if (hsk->end_offset > recv_buf[pos].end_offset &&
+- hsk->start_offset <= recv_buf[pos].end_offset + 1) {
++ recv_buf[pos].frag_length = MIN(
++ hsk->frag_length, recv_buf[pos].frag_length);
++ } else if (hsk->start_offset + hsk->frag_length >
++ recv_buf[pos].start_offset +
++ recv_buf[pos].frag_length &&
++ hsk->start_offset <=
++ recv_buf[pos].start_offset +
++ recv_buf[pos].frag_length) {
+ memcpy(&recv_buf[pos].data.data[hsk->start_offset],
+ hsk->data.data, hsk->data.length);
+
+- recv_buf[pos].end_offset = hsk->end_offset;
+ recv_buf[pos].start_offset = MIN(
+ hsk->start_offset, recv_buf[pos].start_offset);
++ recv_buf[pos].frag_length = hsk->start_offset +
++ hsk->frag_length -
++ recv_buf[pos].start_offset;
+ }
+ _gnutls_handshake_buffer_clear(hsk);
+ }
+@@ -1113,8 +1117,8 @@ static int get_last_packet(gnutls_session_t session,
+ }
+
+ else if ((recv_buf[LAST_ELEMENT].start_offset == 0 &&
+- recv_buf[LAST_ELEMENT].end_offset ==
+- recv_buf[LAST_ELEMENT].length - 1) ||
++ recv_buf[LAST_ELEMENT].frag_length ==
++ recv_buf[LAST_ELEMENT].length) ||
+ recv_buf[LAST_ELEMENT].length == 0) {
+ session->internals.dtls.hsk_read_seq++;
+ _gnutls_handshake_buffer_move(hsk,
+@@ -1125,8 +1129,9 @@ static int get_last_packet(gnutls_session_t session,
+ /* if we don't have a complete handshake message, but we
+ * have queued data waiting, try again to reconstruct the
+ * handshake packet, using the queued */
+- if (recv_buf[LAST_ELEMENT].end_offset !=
+- recv_buf[LAST_ELEMENT].length - 1 &&
++ if ((recv_buf[LAST_ELEMENT].start_offset +
++ recv_buf[LAST_ELEMENT].frag_length) !=
++ recv_buf[LAST_ELEMENT].length &&
+ record_check_unprocessed(session) > 0)
+ return gnutls_assert_val(
+ GNUTLS_E_INT_CHECK_AGAIN);
+@@ -1313,9 +1318,7 @@ int _gnutls_parse_record_buffered_msgs(gnutls_session_t session)
+ &session->internals.record_buffer,
+ bufel, ret);
+
+- data_size = MIN(tmp.length,
+- tmp.end_offset -
+- tmp.start_offset + 1);
++ data_size = MIN(tmp.length, tmp.frag_length);
+
+ ret = _gnutls_buffer_append_data(
+ &tmp.data,
+@@ -1331,7 +1334,6 @@ int _gnutls_parse_record_buffered_msgs(gnutls_session_t session)
+ ret = merge_handshake_packet(session, &tmp);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+-
+ } while (_mbuffer_get_udata_size(bufel) > 0);
+
+ prev = bufel;
+diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
+index 54d3c9f67..283f25c07 100644
+--- a/lib/gnutls_int.h
++++ b/lib/gnutls_int.h
+@@ -479,10 +479,10 @@ typedef struct {
+ uint16_t sequence;
+
+ /* indicate whether that message is complete.
+- * complete means start_offset == 0 and end_offset == length
++ * complete means start_offset == 0 and frag_length == length
+ */
+ uint32_t start_offset;
+- uint32_t end_offset;
++ uint32_t frag_length; /* used exclusively in DTLS reassembly */
+
+ uint8_t header[MAX_HANDSHAKE_HEADER_SIZE];
+ int header_size;
@@ -37,6 +37,7 @@ SRC_URI = "https://www.gnupg.org/ftp/gcrypt/gnutls/v${SHRT_VER}/gnutls-${PV}.tar
file://CVE-2026-42009_p1.patch \
file://CVE-2026-42009_p2.patch \
file://CVE-2026-3833.patch \
+ file://CVE-2026-33845.patch \
"
SRC_URI[sha256sum] = "a7b341421bfd459acf7a374ca4af3b9e06608dcd7bd792b2bf470bea012b8e51"
Backport patch to fix CVE-2026-33845. References: https://nvd.nist.gov/vuln/detail/CVE-2026-33845 Upstream fix: https://gitlab.com/gnutls/gnutls/-/commit/e5b72c53c7d789d19d1d1cd10b275e87d0415413 Signed-off-by: Adarsh Jagadish Kamini <adarsh.jagadish.kamini@est.tech> --- .../gnutls/gnutls/CVE-2026-33845.patch | 166 ++++++++++++++++++ meta/recipes-support/gnutls/gnutls_3.8.12.bb | 1 + 2 files changed, 167 insertions(+) create mode 100644 meta/recipes-support/gnutls/gnutls/CVE-2026-33845.patch