From 6cf5e7111b7dcd5928021d5104abd01b75ec6b97 Mon Sep 17 00:00:00 2001
From: Daniel Markstedt <daniel@mindani.net>
Date: Thu, 25 Jun 2026 22:08:07 +0200
Subject: [PATCH] CVE-2026-62319,CVE-2026-62320: libatalk: harden DSI frame
 parsing

Reported-by: Alejandro Ramos <aramosf@gmail.com>
Signed-off-by: Daniel Markstedt <daniel@mindani.net>
Reviewed-by: Andy Lemin (andylemin)
---
 libatalk/dsi/dsi_stream.c | 33 +++++++++++++++++++++++++--------
 libatalk/dsi/dsi_tcp.c    | 13 +++++++++++--
 2 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/libatalk/dsi/dsi_stream.c b/libatalk/dsi/dsi_stream.c
index 677186b28..a882af9a5 100644
--- a/libatalk/dsi/dsi_stream.c
+++ b/libatalk/dsi/dsi_stream.c
@@ -662,6 +662,8 @@ int dsi_stream_send(DSI *dsi, void *buf, size_t length)
 int dsi_stream_receive(DSI *dsi)
 {
     char block[DSI_BLOCKSIZ];
+    uint32_t dsi_len;
+    uint32_t dsi_doff;
     LOG(log_maxdebug, logtype_dsi, "dsi_stream_receive: START");
 
     if (dsi->flags & DSI_DISCONNECTED) {
@@ -689,22 +691,37 @@ int dsi_stream_receive(DSI *dsi)
     memcpy(&dsi->header.dsi_len, block + 8, sizeof(dsi->header.dsi_len));
     memcpy(&dsi->header.dsi_reserved, block + 12, sizeof(dsi->header.dsi_reserved));
     dsi->clientID = ntohs(dsi->header.dsi_requestID);
-    /* make sure we don't over-write our buffers. */
-    dsi->cmdlen = MIN(ntohl(dsi->header.dsi_len), dsi->server_quantum);
-    dsi->header.dsi_data.dsi_doff = MIN(dsi->header.dsi_data.dsi_doff,
-                                        dsi->server_quantum);
+    dsi_len = ntohl(dsi->header.dsi_len);
+    dsi_doff = dsi->header.dsi_data.dsi_doff;
+
+    if (dsi_len > dsi->server_quantum) {
+        LOG(log_error, logtype_dsi,
+            "dsi_stream_receive: request payload too large: %u > %u",
+            (unsigned int)dsi_len, (unsigned int)dsi->server_quantum);
+        return 0;
+    }
+
+    dsi->cmdlen = dsi_len;
 
     /* Work around bug in ASC 3.7.x when client sends a zero byte AFPWrite() */
     if (dsi->header.dsi_command == DSIFUNC_WRITE
-            && !(dsi->header.dsi_data.dsi_doff)) {
-        dsi->header.dsi_data.dsi_doff = 12;
+            && !dsi_doff) {
+        dsi_doff = 12;
+        dsi->header.dsi_data.dsi_doff = dsi_doff;
+    }
+
+    if (dsi->header.dsi_command == DSIFUNC_WRITE && dsi_doff > dsi_len) {
+        LOG(log_error, logtype_dsi,
+            "dsi_stream_receive: write data offset exceeds payload length: %u > %u",
+            (unsigned int)dsi_doff, (unsigned int)dsi_len);
+        return 0;
     }
 
     /* Receiving DSIWrite data is done in AFP function, not here */
     if (dsi->header.dsi_command == DSIFUNC_WRITE
-            && dsi->header.dsi_data.dsi_doff) {
+            && dsi_doff) {
         LOG(log_maxdebug, logtype_dsi, "dsi_stream_receive: write request");
-        dsi->cmdlen = dsi->header.dsi_data.dsi_doff;
+        dsi->cmdlen = dsi_doff;
     }
 
     if (dsi_stream_read(dsi, dsi->commands, dsi->cmdlen) != dsi->cmdlen) {
diff --git a/libatalk/dsi/dsi_tcp.c b/libatalk/dsi/dsi_tcp.c
index 9b0fbb0b6..b461d4bb9 100644
--- a/libatalk/dsi/dsi_tcp.c
+++ b/libatalk/dsi/dsi_tcp.c
@@ -150,6 +150,7 @@ static pid_t dsi_tcp_open(DSI *dsi)
         struct sigaction newact, oldact;
         uint8_t block[DSI_BLOCKSIZ];
         size_t stored;
+        uint32_t dsi_len;
         /* reset signals */
         server_reset_signal();
 #ifndef DEBUGGING
@@ -209,8 +210,16 @@ static pid_t dsi_tcp_open(DSI *dsi)
         memcpy(&dsi->header.dsi_reserved, block + 12,
                sizeof(dsi->header.dsi_reserved));
         dsi->clientID = ntohs(dsi->header.dsi_requestID);
-        /* make sure we don't over-write our buffers. */
-        dsi->cmdlen = min(ntohl(dsi->header.dsi_len), dsi->server_quantum);
+        dsi_len = ntohl(dsi->header.dsi_len);
+
+        if (dsi_len > dsi->server_quantum) {
+            LOG(log_error, logtype_dsi,
+                "dsi_tcp_open: request payload too large: %u > %u",
+                (unsigned int)dsi_len, (unsigned int)dsi->server_quantum);
+            exit(EXITERR_CLNT);
+        }
+
+        dsi->cmdlen = dsi_len;
         stored = 0;
 
         while (stored < dsi->cmdlen) {
