From a5e8bc6726d40d1e7f6ed44e6d930a3b31b91708 Mon Sep 17 00:00:00 2001
From: Daniel Markstedt <daniel@mindani.net>
Date: Thu, 25 Jun 2026 22:55:10 +0200
Subject: [PATCH] CVE-2026-62321: afpd: harden spotlight marshalling

Reported-by: Alejandro Ramos <aramosf@gmail.com>
Signed-off-by: Daniel Markstedt <daniel@mindani.net>
Reviewed-by: Andy Lemin (andylemin)
---
 etc/afpd/spotlight.c             |  62 +++++++++++-
 etc/afpd/spotlight_marshalling.c | 157 +++++++++++++++++--------------
 2 files changed, 149 insertions(+), 70 deletions(-)

diff --git a/etc/afpd/spotlight.c b/etc/afpd/spotlight.c
index 71ed2ec2a..dec83a6f4 100644
--- a/etc/afpd/spotlight.c
+++ b/etc/afpd/spotlight.c
@@ -50,6 +50,7 @@
 #include "etc/spotlight/spotlight_private.h"
 
 #define MAX_SL_RESULTS 20
+#define MAX_SL_REQINFO_ATTRS 64
 #define MAX_SL_QUERY_IDLE_TIME_ACTIVE 60
 #define MAX_SL_QUERY_IDLE_TIME_TERMINAL 300
 
@@ -304,6 +305,59 @@ static int sl_createCNIDArray(slq_t *slq, const DALLOC_CTX *p)
     EC_EXIT;
 }
 
+static bool sl_reqinfo_contains(sl_array_t *reqinfo, const char *attr)
+{
+    for (int i = 0; i < talloc_array_length(reqinfo->dd_talloc_array); i++) {
+        if (strequal(reqinfo->dd_talloc_array[i], attr)) {
+            return true;
+        }
+    }
+
+    return false;
+}
+
+static sl_array_t *sl_sanitize_reqinfo(TALLOC_CTX *mem_ctx,
+                                       const sl_array_t *reqinfo)
+{
+    sl_array_t *attrs;
+    const char *attr;
+    char *copy;
+    attrs = talloc_zero(mem_ctx, sl_array_t);
+
+    if (attrs == NULL) {
+        return NULL;
+    }
+
+    for (int i = 0; i < talloc_array_length(reqinfo->dd_talloc_array); i++) {
+        if (talloc_array_length(attrs->dd_talloc_array) >= MAX_SL_REQINFO_ATTRS) {
+            break;
+        }
+
+        if (reqinfo->dd_talloc_array[i] == NULL
+                || strcmp(talloc_get_name(reqinfo->dd_talloc_array[i]), "char *") != 0) {
+            talloc_free(attrs);
+            return NULL;
+        }
+
+        attr = reqinfo->dd_talloc_array[i];
+
+        if (sl_reqinfo_contains(attrs, attr)) {
+            continue;
+        }
+
+        copy = dalloc_strdup(attrs, attr);
+
+        if (copy == NULL) {
+            talloc_free(attrs);
+            return NULL;
+        }
+
+        dalloc_add(attrs, copy, char *);
+    }
+
+    return attrs;
+}
+
 /*!
  * @brief Add requested metadata for a query result element
  *
@@ -885,7 +939,13 @@ static int sl_rpc_openQuery(AFPObj *obj,
         EC_FAIL;
     }
 
-    slq->slq_reqinfo = talloc_steal(slq, reqinfo);
+    slq->slq_reqinfo = sl_sanitize_reqinfo(slq, reqinfo);
+
+    if (slq->slq_reqinfo == NULL) {
+        LOG(log_error, logtype_sl, "openQuery: invalid kMDAttributeArray");
+        EC_FAIL;
+    }
+
     scope_array = dalloc_value_for_key(query, "DALLOC_CTX", 0, "DALLOC_CTX", 1,
                                        "kMDScopeArray", "sl_array_t");
 
diff --git a/etc/afpd/spotlight_marshalling.c b/etc/afpd/spotlight_marshalling.c
index 3a1ed5af2..fd69f536a 100644
--- a/etc/afpd/spotlight_marshalling.c
+++ b/etc/afpd/spotlight_marshalling.c
@@ -72,8 +72,9 @@
 #define SUBQ_SAFETY_LIM 20
 
 /* Forward declarations */
-static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf,
-                        int *toc_idx);
+static int sl_pack_len(DALLOC_CTX *query, char *buf, off_t maxoff);
+static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, off_t maxoff,
+                        char *toc_buf, int *toc_idx);
 static int sl_unpack_loop(DALLOC_CTX *query, const char *buf, int offset,
                           uint count, size_t buf_len, const uint toc_offset,
                           uint toc_count, const uint encoding, int depth);
@@ -90,7 +91,7 @@ static int sl_unpack_r(DALLOC_CTX *query, const char *buf, size_t buf_len,
 
 static int sivalc(char *buf, off_t off, off_t maxoff, uint32_t val)
 {
-    if (off + sizeof(val) >= maxoff) {
+    if (off < 0 || maxoff < 0 || off > maxoff - (off_t)sizeof(val)) {
         LOG(log_error, logtype_sl, "sivalc: off: %zd, maxoff: %zd", off, maxoff);
         return -1;
     }
@@ -101,7 +102,7 @@ static int sivalc(char *buf, off_t off, off_t maxoff, uint32_t val)
 
 static int slvalc(char *buf, off_t off, off_t maxoff, uint64_t val)
 {
-    if (off + sizeof(val) >= maxoff) {
+    if (off < 0 || maxoff < 0 || off > maxoff - (off_t)sizeof(val)) {
         LOG(log_error, logtype_sl, "slvalc: off: %zd, maxoff: %zd", off, maxoff);
         return -1;
     }
@@ -147,7 +148,7 @@ static uint64_t sl_pack_tag(uint16_t type, uint16_t size_or_count, uint32_t val)
     return tag;
 }
 
-static int sl_pack_float(double d, char *buf, int offset)
+static int sl_pack_float(double d, char *buf, int offset, off_t maxoff)
 {
     EC_INIT;
     union {
@@ -155,8 +156,8 @@ static int sl_pack_float(double d, char *buf, int offset)
         uint64_t w;
     } ieee_fp_union;
     ieee_fp_union.d = d;
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_FLOAT, 2, 1)));
-    EC_ZERO(slvalc(buf, offset + 8, MAX_SLQ_DAT, ieee_fp_union.w));
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_FLOAT, 2, 1)));
+    EC_ZERO(slvalc(buf, offset + 8, maxoff, ieee_fp_union.w));
 EC_CLEANUP:
 
     if (ret != 0) {
@@ -166,11 +167,11 @@ static int sl_pack_float(double d, char *buf, int offset)
     return offset + 2 * sizeof(uint64_t);
 }
 
-static int sl_pack_uint64(uint64_t u, char *buf, int offset)
+static int sl_pack_uint64(uint64_t u, char *buf, int offset, off_t maxoff)
 {
     EC_INIT;
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_INT64, 2, 1)));
-    EC_ZERO(slvalc(buf, offset + 8, MAX_SLQ_DAT, u));
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_INT64, 2, 1)));
+    EC_ZERO(slvalc(buf, offset + 8, maxoff, u));
 EC_CLEANUP:
 
     if (ret != 0) {
@@ -180,10 +181,10 @@ static int sl_pack_uint64(uint64_t u, char *buf, int offset)
     return offset + 2 * sizeof(uint64_t);
 }
 
-static int sl_pack_bool(sl_bool_t bl, char *buf, int offset)
+static int sl_pack_bool(sl_bool_t bl, char *buf, int offset, off_t maxoff)
 {
     EC_INIT;
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_BOOL, 1,
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_BOOL, 1,
                    bl ? 1 : 0)));
 EC_CLEANUP:
 
@@ -194,10 +195,10 @@ static int sl_pack_bool(sl_bool_t bl, char *buf, int offset)
     return offset + sizeof(uint64_t);
 }
 
-static int sl_pack_nil(char *buf, int offset)
+static int sl_pack_nil(char *buf, int offset, off_t maxoff)
 {
     EC_INIT;
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_NULL, 1, 1)));
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_NULL, 1, 1)));
 EC_CLEANUP:
 
     if (ret != 0) {
@@ -207,7 +208,7 @@ static int sl_pack_nil(char *buf, int offset)
     return offset + sizeof(uint64_t);
 }
 
-static int sl_pack_date(sl_time_t t, char *buf, int offset)
+static int sl_pack_date(sl_time_t t, char *buf, int offset, off_t maxoff)
 {
     EC_INIT;
     uint64_t data = 0;
@@ -218,8 +219,8 @@ static int sl_pack_date(sl_time_t t, char *buf, int offset)
     ieee_fp_union.d = (double)(t.tv_sec - SPOTLIGHT_TIME_DELTA);
     ieee_fp_union.d += (double)t.tv_usec / 1000000;
     data = ieee_fp_union.w;
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATE, 2, 1)));
-    EC_ZERO(slvalc(buf, offset + 8, MAX_SLQ_DAT, data));
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_DATE, 2, 1)));
+    EC_ZERO(slvalc(buf, offset + 8, maxoff, data));
 EC_CLEANUP:
 
     if (ret != 0) {
@@ -229,12 +230,12 @@ static int sl_pack_date(sl_time_t t, char *buf, int offset)
     return offset + 2 * sizeof(uint64_t);
 }
 
-static int sl_pack_uuid(sl_uuid_t *uuid, char *buf, int offset)
+static int sl_pack_uuid(sl_uuid_t *uuid, char *buf, int offset, off_t maxoff)
 {
     EC_INIT;
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_UUID, 3, 1)));
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_UUID, 3, 1)));
 
-    if (offset + 8 + 16 >= MAX_SLQ_DAT) {
+    if (offset < 0 || maxoff < 0 || offset > maxoff - 24) {
         EC_FAIL;
     }
 
@@ -248,8 +249,8 @@ static int sl_pack_uuid(sl_uuid_t *uuid, char *buf, int offset)
     return offset + sizeof(uint64_t) + 16;
 }
 
-static int sl_pack_CNID(sl_cnids_t *cnids, char *buf, int offset, char *toc_buf,
-                        int *toc_idx)
+static int sl_pack_CNID(sl_cnids_t *cnids, char *buf, int offset, off_t maxoff,
+                        char *toc_buf, int *toc_idx)
 {
     EC_INIT;
     int len;
@@ -257,7 +258,7 @@ static int sl_pack_CNID(sl_cnids_t *cnids, char *buf, int offset, char *toc_buf,
     uint64_t id;
     EC_ZERO(slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC,
                    sl_pack_tag(SQ_CPX_TYPE_CNIDS, (offset + SL_OFFSET_DELTA) / 8, 0)));
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1,
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_COMPLEX, 1,
                    *toc_idx + 1)));
     *toc_idx += 1;
     offset += 8;
@@ -268,17 +269,17 @@ static int sl_pack_CNID(sl_cnids_t *cnids, char *buf, int offset, char *toc_buf,
     }
 
     /* 3rd parameter of sl_pack_tag has unknown meaning, but always 8 */
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_CNIDS, len, 8)));
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_CNIDS, len, 8)));
     offset += 8;
 
     if (cnid_count > 0) {
-        EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(cnids->ca_unkn1,
+        EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(cnids->ca_unkn1,
                        cnid_count, cnids->ca_context)));
         offset += 8;
 
         for (int i = 0; i < cnid_count; i++) {
             memcpy(&id, cnids->ca_cnids->dd_talloc_array[i], sizeof(uint64_t));
-            EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, id));
+            EC_ZERO(slvalc(buf, offset, maxoff, id));
             offset += 8;
         }
     }
@@ -292,7 +293,7 @@ static int sl_pack_CNID(sl_cnids_t *cnids, char *buf, int offset, char *toc_buf,
     return offset;
 }
 
-static int sl_pack_array(sl_array_t *array, char *buf, int offset,
+static int sl_pack_array(sl_array_t *array, char *buf, int offset, off_t maxoff,
                          char *toc_buf, int *toc_idx)
 {
     EC_INIT;
@@ -300,11 +301,11 @@ static int sl_pack_array(sl_array_t *array, char *buf, int offset,
     int octets = (offset + SL_OFFSET_DELTA) / 8;
     EC_ZERO(slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC,
                    sl_pack_tag(SQ_CPX_TYPE_ARRAY, octets, count)));
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1,
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_COMPLEX, 1,
                    *toc_idx + 1)));
     *toc_idx += 1;
     offset += 8;
-    EC_NEG1(offset = sl_pack_loop(array, buf, offset, toc_buf, toc_idx));
+    EC_NEG1(offset = sl_pack_loop(array, buf, offset, maxoff, toc_buf, toc_idx));
 EC_CLEANUP:
 
     if (ret != 0) {
@@ -314,8 +315,8 @@ static int sl_pack_array(sl_array_t *array, char *buf, int offset,
     return offset;
 }
 
-static int sl_pack_dict(sl_array_t *dict, char *buf, int offset, char *toc_buf,
-                        int *toc_idx)
+static int sl_pack_dict(sl_array_t *dict, char *buf, int offset, off_t maxoff,
+                        char *toc_buf, int *toc_idx)
 {
     EC_INIT;
     EC_ZERO(slvalc(toc_buf,
@@ -324,11 +325,11 @@ static int sl_pack_dict(sl_array_t *dict, char *buf, int offset, char *toc_buf,
                    sl_pack_tag(SQ_CPX_TYPE_DICT,
                                (offset + SL_OFFSET_DELTA) / 8,
                                talloc_array_length(dict->dd_talloc_array))));
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1,
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_COMPLEX, 1,
                    *toc_idx + 1)));
     *toc_idx += 1;
     offset += 8;
-    EC_NEG1(offset = sl_pack_loop(dict, buf, offset, toc_buf, toc_idx));
+    EC_NEG1(offset = sl_pack_loop(dict, buf, offset, maxoff, toc_buf, toc_idx));
 EC_CLEANUP:
 
     if (ret != 0) {
@@ -339,15 +340,20 @@ static int sl_pack_dict(sl_array_t *dict, char *buf, int offset, char *toc_buf,
 }
 
 static int sl_pack_filemeta(sl_filemeta_t *fm, char *buf, int offset,
-                            char *toc_buf, int *toc_idx)
+                            off_t maxoff, char *toc_buf, int *toc_idx)
 {
     EC_INIT;
     int fmlen;                  /* lenght of filemeta */
     int saveoff = offset;
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1,
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_COMPLEX, 1,
                    *toc_idx + 1)));
     offset += 16;
-    EC_NEG1(fmlen = sl_pack(fm, buf + offset));
+
+    if (maxoff - offset <= 16) {
+        EC_FAIL;
+    }
+
+    EC_NEG1(fmlen = sl_pack_len(fm, buf + offset, maxoff - offset));
     /* Check for empty filemeta array, if it's only 40 bytes, it's only the header but no content */
     LOG(log_debug, logtype_sl, "fmlen: %d", fmlen);
 
@@ -358,7 +364,7 @@ static int sl_pack_filemeta(sl_filemeta_t *fm, char *buf, int offset,
     }
 
     /* 3rd parameter of sl_pack_tag has unknown meaning, but always 8 */
-    EC_ZERO(slvalc(buf, saveoff + 8, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATA,
+    EC_ZERO(slvalc(buf, saveoff + 8, maxoff, sl_pack_tag(SQ_TYPE_DATA,
                    (fmlen / 8) + 1, 8)));
     EC_ZERO(slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC,
                    sl_pack_tag(SQ_CPX_TYPE_FILEMETA, (saveoff + SL_OFFSET_DELTA) / 8, fmlen / 8)));
@@ -373,36 +379,36 @@ static int sl_pack_filemeta(sl_filemeta_t *fm, char *buf, int offset,
 }
 
 static int sl_pack_string(char *s, char *buf, int offset, char *toc_buf,
-                          int *toc_idx)
+                          int *toc_idx, off_t maxoff)
 {
     EC_INIT;
-    int len, octets, used_in_last_octet;
+    int octets, used_in_last_octet;
     size_t alloc_size = talloc_get_size(s);
-    len = (int)strnlen(s, alloc_size);
+    size_t len = strnlen(s, alloc_size);
 
-    if (alloc_size == 0 || (size_t)len == alloc_size) {
+    if (alloc_size == 0 || len == alloc_size || len > INT_MAX) {
         EC_FAIL;
     }
 
-    octets = (len / 8) + (len & 7 ? 1 : 0);
+    octets = (int)((len / 8) + (len & 7 ? 1 : 0));
     used_in_last_octet = 8 - (octets * 8 - len);
     EC_ZERO(slvalc(toc_buf, *toc_idx * 8, MAX_SLQ_TOC,
                    sl_pack_tag(SQ_CPX_TYPE_STRING, (offset + SL_OFFSET_DELTA) / 8,
                                used_in_last_octet)));
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_COMPLEX, 1,
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_COMPLEX, 1,
                    *toc_idx + 1)));
     *toc_idx += 1;
     offset += 8;
-    EC_ZERO(slvalc(buf, offset, MAX_SLQ_DAT, sl_pack_tag(SQ_TYPE_DATA, octets + 1,
+    EC_ZERO(slvalc(buf, offset, maxoff, sl_pack_tag(SQ_TYPE_DATA, octets + 1,
                    used_in_last_octet)));
     offset += 8;
 
-    if (offset + octets * 8 > MAX_SLQ_DAT) {
+    if (offset < 0 || maxoff < 0 || octets > (maxoff - offset) / 8) {
         EC_FAIL;
     }
 
     memset(buf + offset, 0, octets * 8);
-    strncpy(buf + offset, s, len);
+    memcpy(buf + offset, s, len);
     offset += octets * 8;
 EC_CLEANUP:
 
@@ -413,8 +419,8 @@ static int sl_pack_string(char *s, char *buf, int offset, char *toc_buf,
     return offset;
 }
 
-static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf,
-                        int *toc_idx)
+static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, off_t maxoff,
+                        char *toc_buf, int *toc_idx)
 {
     EC_INIT;
     const char *type;
@@ -423,40 +429,40 @@ static int sl_pack_loop(DALLOC_CTX *query, char *buf, int offset, char *toc_buf,
         type = talloc_get_name(query->dd_talloc_array[n]);
 
         if (STRCMP(type, ==, "sl_array_t")) {
-            EC_NEG1(offset = sl_pack_array(query->dd_talloc_array[n], buf, offset, toc_buf,
-                                           toc_idx));
+            EC_NEG1(offset = sl_pack_array(query->dd_talloc_array[n], buf, offset, maxoff,
+                                           toc_buf, toc_idx));
         } else if (STRCMP(type, ==, "sl_dict_t")) {
-            EC_NEG1(offset = sl_pack_dict(query->dd_talloc_array[n], buf, offset, toc_buf,
-                                          toc_idx));
+            EC_NEG1(offset = sl_pack_dict(query->dd_talloc_array[n], buf, offset, maxoff,
+                                          toc_buf, toc_idx));
         } else if (STRCMP(type, ==, "sl_filemeta_t")) {
             EC_NEG1(offset = sl_pack_filemeta(query->dd_talloc_array[n], buf, offset,
-                                              toc_buf, toc_idx));
+                                              maxoff, toc_buf, toc_idx));
         } else if (STRCMP(type, ==, "uint64_t")) {
             uint64_t i;
             memcpy(&i, query->dd_talloc_array[n], sizeof(uint64_t));
-            EC_NEG1(offset = sl_pack_uint64(i, buf, offset));
+            EC_NEG1(offset = sl_pack_uint64(i, buf, offset, maxoff));
         } else if (STRCMP(type, ==, "char *")) {
             EC_NEG1(offset = sl_pack_string(query->dd_talloc_array[n], buf, offset, toc_buf,
-                                            toc_idx));
+                                            toc_idx, maxoff));
         } else if (STRCMP(type, ==, "sl_bool_t")) {
             sl_bool_t bl;
             memcpy(&bl, query->dd_talloc_array[n], sizeof(sl_bool_t));
-            EC_NEG1(offset = sl_pack_bool(bl, buf, offset));
+            EC_NEG1(offset = sl_pack_bool(bl, buf, offset, maxoff));
         } else if (STRCMP(type, ==, "double")) {
             double d;
             memcpy(&d, query->dd_talloc_array[n], sizeof(double));
-            EC_NEG1(offset = sl_pack_float(d, buf, offset));
+            EC_NEG1(offset = sl_pack_float(d, buf, offset, maxoff));
         } else if (STRCMP(type, ==, "sl_nil_t")) {
-            EC_NEG1(offset = sl_pack_nil(buf, offset));
+            EC_NEG1(offset = sl_pack_nil(buf, offset, maxoff));
         } else if (STRCMP(type, ==, "sl_time_t")) {
             sl_time_t t;
             memcpy(&t, query->dd_talloc_array[n], sizeof(sl_time_t));
-            EC_NEG1(offset = sl_pack_date(t, buf, offset));
+            EC_NEG1(offset = sl_pack_date(t, buf, offset, maxoff));
         } else if (STRCMP(type, ==, "sl_uuid_t")) {
-            EC_NEG1(offset = sl_pack_uuid(query->dd_talloc_array[n], buf, offset));
+            EC_NEG1(offset = sl_pack_uuid(query->dd_talloc_array[n], buf, offset, maxoff));
         } else if (STRCMP(type, ==, "sl_cnids_t")) {
-            EC_NEG1(offset = sl_pack_CNID(query->dd_talloc_array[n], buf, offset, toc_buf,
-                                          toc_idx));
+            EC_NEG1(offset = sl_pack_CNID(query->dd_talloc_array[n], buf, offset, maxoff,
+                                          toc_buf, toc_idx));
         }
     }
 
@@ -1088,25 +1094,33 @@ static int sl_unpack_loop(DALLOC_CTX *query,
  * Global functions for packing und unpacking
  **************************************************************************************************/
 
-int sl_pack(DALLOC_CTX *query, char *buf)
+static int sl_pack_len(DALLOC_CTX *query, char *buf, off_t maxoff)
 {
     EC_INIT;
     char toc_buf[MAX_SLQ_TOC];
     int toc_index = 0;
     int len = 0;
+    off_t toc_len;
+
+    if (maxoff <= 16) {
+        EC_FAIL;
+    }
+
     memcpy(buf, "432130dm", 8);
-    EC_NEG1_LOG(len = sl_pack_loop(query, buf + 16, 0, toc_buf + 8, &toc_index));
-    EC_ZERO(sivalc(buf, 8, MAX_SLQ_DAT, len / 8 + 1 + toc_index + 1));
-    EC_ZERO(sivalc(buf, 12, MAX_SLQ_DAT, len / 8 + 1));
+    EC_NEG1_LOG(len = sl_pack_loop(query, buf + 16, 0, maxoff - 16, toc_buf + 8,
+                                   &toc_index));
+    EC_ZERO(sivalc(buf, 8, maxoff, len / 8 + 1 + toc_index + 1));
+    EC_ZERO(sivalc(buf, 12, maxoff, len / 8 + 1));
     EC_ZERO(slvalc(toc_buf, 0, MAX_SLQ_TOC, sl_pack_tag(SQ_TYPE_TOC, toc_index + 1,
                    0)));
+    toc_len = (toc_index + 1) * 8;
 
-    if ((16 + len + ((toc_index + 1) * 8)) >= MAX_SLQ_DAT) {
+    if (16 + len + toc_len >= maxoff) {
         EC_FAIL;
     }
 
-    memcpy(buf + 16 + len, toc_buf, (toc_index + 1) * 8);
-    len += 16 + (toc_index + 1) * 8;
+    memcpy(buf + 16 + len, toc_buf, toc_len);
+    len += 16 + toc_len;
 EC_CLEANUP:
 
     if (ret != 0) {
@@ -1116,6 +1130,11 @@ int sl_pack(DALLOC_CTX *query, char *buf)
     return len;
 }
 
+int sl_pack(DALLOC_CTX *query, char *buf)
+{
+    return sl_pack_len(query, buf, MAX_SLQ_DAT);
+}
+
 static int sl_unpack_r(DALLOC_CTX *query, const char *buf, size_t buf_len,
                        int depth)
 {
