Index: netatalk-3.1.0/libatalk/adouble/ad_open.c
===================================================================
--- netatalk-3.1.0.orig/libatalk/adouble/ad_open.c	2013-10-28 14:43:14.000000000 +0100
+++ netatalk-3.1.0/libatalk/adouble/ad_open.c	2022-03-22 13:50:00.331317475 +0100
@@ -387,13 +387,13 @@ static int new_ad_header(struct adouble
     return 0;
 }
 
-/* -------------------------------------
-   read in the entries
-*/
-static void parse_entries(struct adouble *ad, char *buf, uint16_t nentries)
+/**
+ * Read an AppleDouble buffer, returns 0 on success, -1 if an entry was malformatted
+ **/
+static int parse_entries(struct adouble *ad, uint16_t nentries, size_t valid_data_len)
 {
     uint32_t   eid, len, off;
-    int        warning = 0;
+    uint8_t *buf = ad->ad_data + AD_HEADER_LEN;
 
     /* now, read in the entry bits */
     for (; nentries > 0; nentries-- ) {
@@ -407,17 +407,20 @@ static void parse_entries(struct adouble
         len = ntohl( len );
         buf += sizeof( len );
 
-        if (eid
-            && eid < ADEID_MAX
-            && off < sizeof(ad->ad_data)
-            && (off + len <= sizeof(ad->ad_data) || eid == ADEID_RFORK)) {
-            ad->ad_eid[ eid ].ade_off = off;
-            ad->ad_eid[ eid ].ade_len = len;
-        } else if (!warning) {
-            warning = 1;
-            LOG(log_warning, logtype_ad, "parse_entries: bogus eid: %d", eid);
+        if (!eid
+            || eid > ADEID_MAX
+            || off >= valid_data_len
+            || ((eid != ADEID_RFORK) && (off + len >  valid_data_len)))
+        {
+            LOG(log_warning, logtype_ad, "parse_entries: bogus eid: %u, off: %u, len: %u",
+                (uint)eid, (uint)off, (uint)len);
+	    return -1;
         }
+        ad->ad_eid[eid].ade_off = off;
+        ad->ad_eid[eid].ade_len = len;
     }
+
+    return 0;
 }
 
 /* this reads enough of the header so that we can figure out all of
@@ -431,7 +434,6 @@ static int ad_header_read(const char *pa
 {
     char                *buf = ad->ad_data;
     uint16_t            nentries;
-    int                 len;
     ssize_t             header_len;
     struct stat         st;
 
@@ -457,25 +459,24 @@ static int ad_header_read(const char *pa
 
     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
     nentries = ntohs( nentries );
+    if (nentries > 16) {
+        LOG(log_error, logtype_ad, "ad_open: too many entries: %"PRIu16, nentries);
+        errno = EIO;
+        return -1;
+    }
 
-    /* read in all the entry headers. if we have more than the
-     * maximum, just hope that the rfork is specified early on. */
-    len = nentries*AD_ENTRY_LEN;
-
-    if (len + AD_HEADER_LEN > sizeof(ad->ad_data))
-        len = sizeof(ad->ad_data) - AD_HEADER_LEN;
-
-    buf += AD_HEADER_LEN;
-    if (len > header_len - AD_HEADER_LEN) {
-        LOG(log_error, logtype_ad, "ad_header_read: can't read entry info.");
+    if ((nentries * AD_ENTRY_LEN) + AD_HEADER_LEN > header_len) {
+        LOG(log_error, logtype_ad, "ad_header_read: too many entries: %zd", header_len);
         errno = EIO;
         return -1;
     }
 
-    /* figure out all of the entry offsets and lengths. if we aren't
-     * able to read a resource fork entry, bail. */
-    nentries = len / AD_ENTRY_LEN;
-    parse_entries(ad, buf, nentries);
+    if (parse_entries(ad, nentries, header_len) != 0) {
+        LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
+            path ? fullpathname(path) : "");
+        errno = EIO;
+        return -1;
+    }
     if (!ad_getentryoff(ad, ADEID_RFORK)
         || (ad_getentryoff(ad, ADEID_RFORK) > sizeof(ad->ad_data))
         ) {
@@ -555,7 +556,6 @@ static int ad_header_read_osx(const char
     struct adouble      adosx;
     char                *buf = &adosx.ad_data[0];
     uint16_t            nentries;
-    int                 len;
     ssize_t             header_len;
     struct stat         st;
 
@@ -582,20 +582,24 @@ static int ad_header_read_osx(const char
 
     memcpy(&nentries, buf + ADEDOFF_NENTRIES, sizeof( nentries ));
     nentries = ntohs(nentries);
-    len = nentries * AD_ENTRY_LEN;
-
-    if (len + AD_HEADER_LEN > sizeof(adosx.ad_data))
-        len = sizeof(adosx.ad_data) - AD_HEADER_LEN;
+    if (nentries > 16) {
+        LOG(log_error, logtype_ad, "ad_header_read_osx: too many entries: %"PRIu16, nentries);
+        errno = EIO;
+        return -1;
+    }
 
-    buf += AD_HEADER_LEN;
-    if (len > header_len - AD_HEADER_LEN) {
-        LOG(log_error, logtype_ad, "ad_header_read_osx: can't read entry info.");
+    if ((nentries * AD_ENTRY_LEN) + AD_HEADER_LEN > header_len) {
+        LOG(log_error, logtype_ad, "ad_header_read_osx: too many entries: %zd", header_len);
         errno = EIO;
         return -1;
     }
 
-    nentries = len / AD_ENTRY_LEN;
-    parse_entries(&adosx, buf, nentries);
+    if (parse_entries(&adosx, nentries, header_len) != 0) {
+        LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
+            path ? fullpathname(path) : "");
+        errno = EIO;
+        EC_FAIL;
+    }
 
     if (ad_getentryoff(&adosx, ADEID_RFORK) == 0
         || ad_getentryoff(&adosx, ADEID_RFORK) > sizeof(ad->ad_data)
@@ -662,7 +666,12 @@ static int ad_header_read_ea(const char
     }
 
     /* Now parse entries */
-    parse_entries(ad, buf + AD_HEADER_LEN, nentries);
+    if (parse_entries(ad, nentries, header_len)) {
+        LOG(log_warning, logtype_ad, "ad_header_read(%s): malformed AppleDouble",
+            path ? fullpathname(path) : "");
+        errno = EINVAL;
+        EC_FAIL;
+    }
 
     if (nentries != ADEID_NUM_EA
         || !ad_entry(ad, ADEID_FINDERI)
