Version in base suite: 2022.10.3-5+deb13u2 Base version: ntfs-3g_2022.10.3-5+deb13u2 Target version: ntfs-3g_2022.10.3-5+deb13u3 Base file: /srv/ftp-master.debian.org/ftp/pool/main/n/ntfs-3g/ntfs-3g_2022.10.3-5+deb13u2.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/n/ntfs-3g/ntfs-3g_2022.10.3-5+deb13u3.dsc changelog | 17 + patches/Revert-attrib.c-check-the-multi-sector-transfer-fixu.patch | 41 ++ patches/ntfs-3g_2022.10.3-sa_2026-06-1_05.patch | 27 + patches/ntfs-3g_2022.10.3-sa_2026-06-1_07.patch | 46 +++ patches/ntfs-3g_2022.10.3-sa_2026-06-1_08.patch | 25 + patches/ntfs-3g_2022.10.3-sa_2026-06-1_10.patch | 113 +++++++ patches/ntfs-3g_2022.10.3-sa_2026-06-1_11.patch | 28 + patches/ntfs-3g_2022.10.3-sa_2026-06-1_12.patch | 73 +++++ patches/ntfs-3g_2022.10.3-sa_2026-06-1_19.patch | 142 ++++++++++ patches/ntfs-3g_2022.10.3-sa_2026-06-1_20.patch | 29 ++ patches/series | 9 11 files changed, 550 insertions(+) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmps5nlobvt/ntfs-3g_2022.10.3-5+deb13u2.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmps5nlobvt/ntfs-3g_2022.10.3-5+deb13u3.dsc: no acceptable signature found diff -Nru ntfs-3g-2022.10.3/debian/changelog ntfs-3g-2022.10.3/debian/changelog --- ntfs-3g-2022.10.3/debian/changelog 2026-07-15 05:02:26.000000000 +0000 +++ ntfs-3g-2022.10.3/debian/changelog 2026-10-01 21:06:57.000000000 +0000 @@ -1,3 +1,20 @@ +ntfs-3g (1:2022.10.3-5+deb13u3) trixie-security; urgency=high + + * Non-maintainer upload by the Security Team. + * [NTFS-3G-SA_2026-06-1_05] Heap out of bounds read/write in ntfs_ie_add_vcn() + * [NTFS-3G-SA_2026-06-1_07] Heap data corruption in + ntfs_mapping_pairs_decompress_i() + * [NTFS-3G-SA_2026-06-1_08] Heap buffer overflow in ntfs_external_attr_find() + * [NTFS-3G-SA_2026-06-1_10] Heap buffer overflow in ntfs_ea_check_wsldev() + * [NTFS-3G-SA_2026-06-1_11] Heap buffer overflow in ntfs_check_restart_area() + * [NTFS-3G-SA_2026-06-1_12] Denial-of-service in + ntfs_inode_attach_all_extents() + * [NTFS-3G-SA_2026-06-1_19] Heap buffer overflow in ntfs_same_sid() + * [NTFS-3G-SA_2026-06-1_20] Heap buffer overflow in ntfs_acl_owner() + * Revert "attrib.c: check the multi sector transfer fixup on read" + + -- Salvatore Bonaccorso Thu, 01 Oct 2026 23:06:57 +0200 + ntfs-3g (1:2022.10.3-5+deb13u2) trixie-security; urgency=high * Non-maintainer upload by the Security Team. diff -Nru ntfs-3g-2022.10.3/debian/patches/Revert-attrib.c-check-the-multi-sector-transfer-fixu.patch ntfs-3g-2022.10.3/debian/patches/Revert-attrib.c-check-the-multi-sector-transfer-fixu.patch --- ntfs-3g-2022.10.3/debian/patches/Revert-attrib.c-check-the-multi-sector-transfer-fixu.patch 1970-01-01 00:00:00.000000000 +0000 +++ ntfs-3g-2022.10.3/debian/patches/Revert-attrib.c-check-the-multi-sector-transfer-fixu.patch 2026-10-01 21:06:57.000000000 +0000 @@ -0,0 +1,41 @@ +From: Erik Larsson +Date: Mon, 28 Sep 2026 12:05:41 +0300 +Subject: Revert "attrib.c: check the multi sector transfer fixup on read" +Origin: https://github.com/unsound/ntfs-3g/commit/9398030d978b064c62f304b1a514c7a494d37b62 + +This is done in response to a regression from version 2026.7.7 reported +in github issue #217, reported by @amehn77, and in issue #218, opened by +@xcdmrCHP. + +This reverts commit 056ce3a90d995894bfd4ce282be7b66ebb20e7ac. +--- + libntfs-3g/attrib.c | 13 ++----------- + 1 file changed, 2 insertions(+), 11 deletions(-) + +diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c +index 3f12274708ef..87b30f950b58 100644 +--- a/libntfs-3g/attrib.c ++++ b/libntfs-3g/attrib.c +@@ -2673,17 +2673,8 @@ s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, + /* log errors unless silenced */ + warn = !na->ni || !na->ni->vol || !NVolNoFixupWarn(na->ni->vol); + for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst + +- bk_size) { +- /* +- * A record which cannot be multi sector transfer protected at +- * all (EINVAL) has not been deprotected, so it must not be +- * used. A record damaged by an incomplete transfer (EIO) is +- * left to the caller, which detects it by the BAAD magic. +- */ +- if (ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)dst, bk_size, +- warn) && (errno == EINVAL)) +- return -1; +- } ++ bk_size) ++ ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)dst, bk_size, warn); + /* Finally, return the number of blocks read. */ + return br; + } +-- +2.55.0 + diff -Nru ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_05.patch ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_05.patch --- ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_05.patch 1970-01-01 00:00:00.000000000 +0000 +++ ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_05.patch 2026-09-21 03:51:37.000000000 +0000 @@ -0,0 +1,27 @@ +diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c +index e48d6aafb..c2cdf23a7 100644 +--- a/libntfs-3g/index.c ++++ b/libntfs-3g/index.c +@@ -1379,12 +1379,19 @@ static int ntfs_ir_make_space(ntfs_index_context *icx, int data_size) + static int ntfs_ie_add_vcn(INDEX_ENTRY **ie) + { + INDEX_ENTRY *p, *old = *ie; +- +- old->length = cpu_to_le16(le16_to_cpu(old->length) + sizeof(VCN)); +- p = realloc(old, le16_to_cpu(old->length)); ++ u32 new_length = le16_to_cpu(old->length) + (u32)sizeof(VCN); ++ ++ if (new_length > 0xffff) { ++ ntfs_log_error("Index entry of length %u cannot hold a VCN\n", ++ (unsigned)le16_to_cpu(old->length)); ++ errno = EIO; ++ return STATUS_ERROR; ++ } ++ p = realloc(old, new_length); + if (!p) + return STATUS_ERROR; + ++ p->length = cpu_to_le16(new_length); + p->ie_flags |= INDEX_ENTRY_NODE; + *ie = p; + diff -Nru ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_07.patch ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_07.patch --- ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_07.patch 1970-01-01 00:00:00.000000000 +0000 +++ ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_07.patch 2026-09-21 03:54:03.000000000 +0000 @@ -0,0 +1,46 @@ +diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c +index efb919438..fdd8b17ee 100644 +--- a/libntfs-3g/attrib.c ++++ b/libntfs-3g/attrib.c +@@ -2671,8 +2671,17 @@ s64 ntfs_attr_mst_pread(ntfs_attr *na, const s64 pos, const s64 bk_cnt, + /* log errors unless silenced */ + warn = !na->ni || !na->ni->vol || !NVolNoFixupWarn(na->ni->vol); + for (end = (u8*)dst + br * bk_size; (u8*)dst < end; dst = (u8*)dst + +- bk_size) +- ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)dst, bk_size, warn); ++ bk_size) { ++ /* ++ * A record which cannot be multi sector transfer protected at ++ * all (EINVAL) has not been deprotected, so it must not be ++ * used. A record damaged by an incomplete transfer (EIO) is ++ * left to the caller, which detects it by the BAAD magic. ++ */ ++ if (ntfs_mst_post_read_fixup_warn((NTFS_RECORD*)dst, bk_size, ++ warn) && (errno == EINVAL)) ++ return -1; ++ } + /* Finally, return the number of blocks read. */ + return br; + } +diff --git a/libntfs-3g/runlist.c b/libntfs-3g/runlist.c +index cb01e5a77..9793f88d7 100644 +--- a/libntfs-3g/runlist.c ++++ b/libntfs-3g/runlist.c +@@ -790,7 +790,7 @@ static runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, + const u8 *buf; /* Current position in mapping pairs array. */ + const u8 *attr_end; /* End of attribute. */ + int err, rlsize; /* Size of runlist buffer. */ +- u16 rlpos; /* Current runlist position in units of ++ int rlpos; /* Current runlist position in units of + runlist_elements. */ + u8 b; /* Current byte offset in buf. */ + +@@ -832,7 +832,7 @@ static runlist_element *ntfs_mapping_pairs_decompress_i(const ntfs_volume *vol, + * Allocate more memory if needed, including space for the + * not-mapped and terminator elements. + */ +- if ((int)((rlpos + 3) * sizeof(*old_rl)) > rlsize) { ++ if ((rlpos + 3) > (rlsize / (int)sizeof(*old_rl))) { + runlist_element *rl2; + + rlsize += 0x1000; diff -Nru ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_08.patch ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_08.patch --- ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_08.patch 1970-01-01 00:00:00.000000000 +0000 +++ ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_08.patch 2026-09-21 03:54:33.000000000 +0000 @@ -0,0 +1,25 @@ +diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c +index efb919438..4570f6820 100644 +--- a/libntfs-3g/attrib.c ++++ b/libntfs-3g/attrib.c +@@ -3338,6 +3338,20 @@ do_next_attr_loop: + */ + if (al_entry->type != a->type) + break; ++ /* ++ * The same check is made in ntfs_attr_inconsistent() when the ++ * mft record is read, but that validation is skipped for the ++ * tools which set NVolNoFixupWarn(), so the name has to be ++ * checked here as well. ++ */ ++ if (a->name_length && ((le16_to_cpu(a->name_offset) ++ + a->name_length * sizeof(ntfschar)) ++ > le32_to_cpu(a->length))) { ++ ntfs_log_error("Corrupt attribute name" ++ " in MFT record %lld\n", ++ (long long)ctx->ntfs_ino->mft_no); ++ break; ++ } + if (!ntfs_names_are_equal((ntfschar*)((char*)a + + le16_to_cpu(a->name_offset)), + a->name_length, al_name, diff -Nru ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_10.patch ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_10.patch --- ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_10.patch 1970-01-01 00:00:00.000000000 +0000 +++ ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_10.patch 2026-09-21 03:55:07.000000000 +0000 @@ -0,0 +1,113 @@ +diff --git a/libntfs-3g/ea.c b/libntfs-3g/ea.c +index b86930abd..05daa53d1 100644 +--- a/libntfs-3g/ea.c ++++ b/libntfs-3g/ea.c +@@ -183,6 +183,60 @@ static int ntfs_update_ea(ntfs_inode *ni, const char *value, size_t size, + return (res); + } + ++/** ++ * ntfs_ea_inconsistent - check that one EA record is sane ++ * @p_ea: the EA record to check ++ * @offs: where the record starts in the EA buffer ++ * @size: size of the EA buffer ++ * ++ * Every EA record says where the next one starts. Check that the record fits ++ * in the buffer and that its name and value fit in the record : ++ * ++ * offs nextoffs ++ * | | ++ * | header | name | NUL | value | pad | ++ * |========|================|=====|================|=======| ++ * | | | | | | ++ * |<- 8 -->|<- name_length->|<-1->|<-value_length->|<-0..3>| ++ * |<---------------- next_entry_offset ------------------->| ++ * ++ * nextoffs is @offs + next_entry_offset. It has to move forward & be a ++ * multiple of 4 and stay inside the buffer. The name must not be empty and ++ * must be null terminated. Also the name and value must end at nextoffs or ++ * at most 3 bytes before it. The rest of it being padding. ++ * ++ * These are the checks which used to be inlined in ntfs_set_ntfs_ea(). The ++ * caller must have checked that @offs is inside the buffer and that the 8 ++ * byte header fits. ++ * ++ * Return 0 if the record is sane and -1 if it is not. ++ */ ++ ++static int ntfs_ea_inconsistent(const EA_ATTR *p_ea, size_t offs, size_t size) ++{ ++ size_t nextoffs; ++ BOOL ok; ++ ++ nextoffs = offs + le32_to_cpu(p_ea->next_entry_offset); ++ /* null offset to next not allowed */ ++ ok = (nextoffs > offs) ++ && (nextoffs <= size) ++ && !(nextoffs & 3) ++ && p_ea->name_length ++ /* zero sized value are allowed */ ++ && ((offs + offsetof(EA_ATTR,name) ++ + p_ea->name_length + 1 ++ + le16_to_cpu(p_ea->value_length)) ++ <= nextoffs) ++ && ((offs + offsetof(EA_ATTR,name) ++ + p_ea->name_length + 1 ++ + le16_to_cpu(p_ea->value_length)) ++ >= (nextoffs - 3)) ++ && !p_ea->name[p_ea->name_length]; ++ /* name not checked, as chkdsk accepts any chars */ ++ return ok ? 0 : -1; ++} ++ + /* + * Return the existing EA + * +@@ -263,22 +317,7 @@ int ntfs_set_ntfs_ea(ntfs_inode *ni, const char *value, size_t size, int flags) + while (ok && (offs < size)) { + p_ea = (const EA_ATTR*)&value[offs]; + nextoffs = offs + le32_to_cpu(p_ea->next_entry_offset); +- /* null offset to next not allowed */ +- ok = (nextoffs > offs) +- && (nextoffs <= size) +- && !(nextoffs & 3) +- && p_ea->name_length +- /* zero sized value are allowed */ +- && ((offs + offsetof(EA_ATTR,name) +- + p_ea->name_length + 1 +- + le16_to_cpu(p_ea->value_length)) +- <= nextoffs) +- && ((offs + offsetof(EA_ATTR,name) +- + p_ea->name_length + 1 +- + le16_to_cpu(p_ea->value_length)) +- >= (nextoffs - 3)) +- && !p_ea->name[p_ea->name_length]; +- /* name not checked, as chkdsk accepts any chars */ ++ ok = !ntfs_ea_inconsistent(p_ea, offs, size); + if (ok) { + if (p_ea->flags & NEED_EA) + ea_count++; +@@ -444,16 +483,20 @@ int ntfs_ea_check_wsldev(ntfs_inode *ni, dev_t *rdevp) + offset = 0; + found = FALSE; + do { ++ /* The fixed header must fit in the remaining buffer. */ ++ if ((lth - offset) < (int)offsetof(EA_ATTR, name)) ++ break; + p_ea = (const EA_ATTR*)&buf[offset]; ++ if (ntfs_ea_inconsistent(p_ea, offset, lth)) ++ break; + next = le32_to_cpu(p_ea->next_entry_offset); +- found = ((next > (int)(sizeof(lxdev) + sizeof(device))) +- && (p_ea->name_length == (sizeof(lxdev) - 1)) ++ found = ((p_ea->name_length == (sizeof(lxdev) - 1)) + && (p_ea->value_length + == const_cpu_to_le16(sizeof(device))) + && !memcmp(p_ea->name, lxdev, sizeof(lxdev))); + if (!found) + offset += next; +- } while (!found && (next > 0) && (offset < lth)); ++ } while (!found && (offset < lth)); + if (found) { + /* beware of alignment */ + memcpy(&device, &p_ea->name[p_ea->name_length + 1], diff -Nru ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_11.patch ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_11.patch --- ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_11.patch 1970-01-01 00:00:00.000000000 +0000 +++ ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_11.patch 2026-09-21 03:55:33.000000000 +0000 @@ -0,0 +1,28 @@ +diff --git a/libntfs-3g/logfile.c b/libntfs-3g/logfile.c +index 9c3155e78..e5e8af63c 100644 +--- a/libntfs-3g/logfile.c ++++ b/libntfs-3g/logfile.c +@@ -177,14 +177,16 @@ static BOOL ntfs_check_restart_area(RESTART_PAGE_HEADER *rp) + ra_ofs = le16_to_cpu(rp->restart_area_offset); + ra = (RESTART_AREA*)((u8*)rp + ra_ofs); + /* +- * Everything before ra->file_size must be before the first word +- * protected by an update sequence number. This ensures that it is +- * safe to access ra->client_array_offset. ++ * The fixed RESTART_AREA prefix must be entirely before the first ++ * MST-protected sector word. Fixups have not been applied yet, so this ++ * makes every fixed field accessed below safe to read. ++ * ++ * The subsequent client_array_offset check bounds any format-specific ++ * extension before the client array. + */ +- if (ra_ofs + offsetof(RESTART_AREA, file_size) > +- NTFS_BLOCK_SIZE - sizeof(u16)) { +- ntfs_log_error("$LogFile restart area specifies " +- "inconsistent file offset.\n"); ++ if (ra_ofs + sizeof(RESTART_AREA) > NTFS_BLOCK_SIZE - sizeof(u16)) { ++ ntfs_log_error("$LogFile restart area extends into the first " ++ "MST-protected sector word.\n"); + return FALSE; + } + /* diff -Nru ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_12.patch ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_12.patch --- ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_12.patch 1970-01-01 00:00:00.000000000 +0000 +++ ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_12.patch 2026-09-21 03:56:00.000000000 +0000 @@ -0,0 +1,73 @@ +diff --git a/include/ntfs-3g/attrlist.h b/include/ntfs-3g/attrlist.h +index 2952e48b8..44027933f 100644 +--- a/include/ntfs-3g/attrlist.h ++++ b/include/ntfs-3g/attrlist.h +@@ -26,6 +26,9 @@ + + #include "attrib.h" + ++extern int ntfs_attrlist_entry_inconsistent(const ntfs_inode *ni, ++ const ATTR_LIST_ENTRY *ale); ++ + extern int ntfs_attrlist_need(ntfs_inode *ni); + + extern int ntfs_attrlist_entry_add(ntfs_inode *ni, ATTR_RECORD *attr); +diff --git a/libntfs-3g/attrlist.c b/libntfs-3g/attrlist.c +index 29d408598..c06c0edb2 100644 +--- a/libntfs-3g/attrlist.c ++++ b/libntfs-3g/attrlist.c +@@ -45,6 +45,32 @@ + #include "logging.h" + #include "misc.h" + ++/** ++ * ntfs_attrlist_entry_inconsistent - check an attribute list entry ++ * @ni: inode whose attribute list is being walked ++ * @ale: entry to check ++ * ++ * The entry has to lie fully within the attribute list so that its fields can ++ * be read, and it has to be long enough for a walk to make progress. ++ * ++ * Return 0 if the entry may be used and -1 if it may not, with errno set to ++ * EIO. ++ */ ++int ntfs_attrlist_entry_inconsistent(const ntfs_inode *ni, ++ const ATTR_LIST_ENTRY *ale) ++{ ++ u32 length = le16_to_cpu(ale->length); ++ ++ if ((length < sizeof(ATTR_LIST_ENTRY)) || (((const u8*)ale + length) ++ > (ni->attr_list + ni->attr_list_size))) { ++ ntfs_log_error("Corrupt attribute list entry in inode %lld\n", ++ (long long)ni->mft_no); ++ errno = EIO; ++ return -1; ++ } ++ return 0; ++} ++ + /** + * ntfs_attrlist_need - check whether inode need attribute list + * @ni: opened ntfs inode for which perform check +@@ -85,6 +111,8 @@ int ntfs_attrlist_need(ntfs_inode *ni) + errno = 0; + ale = (ATTR_LIST_ENTRY *)ni->attr_list; + while ((u8*)ale < ni->attr_list + ni->attr_list_size) { ++ if (ntfs_attrlist_entry_inconsistent(ni, ale)) ++ return -1; + if (MREF_LE(ale->mft_reference) != ni->mft_no) + return 1; + ale = (ATTR_LIST_ENTRY *)((u8*)ale + le16_to_cpu(ale->length)); +diff --git a/libntfs-3g/inode.c b/libntfs-3g/inode.c +index 8819ff1dc..605b60dff 100644 +--- a/libntfs-3g/inode.c ++++ b/libntfs-3g/inode.c +@@ -716,6 +716,8 @@ int ntfs_inode_attach_all_extents(ntfs_inode *ni) + errno = 0; + ale = (ATTR_LIST_ENTRY *)ni->attr_list; + while ((u8*)ale < ni->attr_list + ni->attr_list_size) { ++ if (ntfs_attrlist_entry_inconsistent(ni, ale)) ++ return -1; + if (ni->mft_no != MREF_LE(ale->mft_reference) && + prev_attached != MREF_LE(ale->mft_reference)) { + if (!ntfs_extent_inode_open(ni, ale->mft_reference)) { diff -Nru ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_19.patch ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_19.patch --- ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_19.patch 1970-01-01 00:00:00.000000000 +0000 +++ ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_19.patch 2026-09-21 03:56:34.000000000 +0000 @@ -0,0 +1,142 @@ +diff --git a/libntfs-3g/acls.c b/libntfs-3g/acls.c +index 9f16fecd0..d7c5ef8cd 100644 +--- a/libntfs-3g/acls.c ++++ b/libntfs-3g/acls.c +@@ -595,6 +595,59 @@ static BOOL valid_acl(const ACL *pacl, unsigned int end) + return (ok); + } + ++/** ++ * sid_at_standard_offset - check whether an ACE keeps its SID where expected ++ * @pace: the ACE to check ++ * ++ * Most ACE types keep the SID just after the header and the access mask : ++ * ++ * | header | mask | SID | ++ * |========|======|===============================| ++ * 0 4 8 ++ * ++ * The object types first keep a flag word and up to two GUIDs. The flags tell ++ * which of the GUIDs are present so the SID may begin at 12 or 28 or 44 : ++ * ++ * | header | mask | flags | object type | inherited object type | SID | ++ * |========|======|=======|=============|=======================|=====| ++ * 0 4 8 12 28 44 ++ * ++ * So for an object type the SID cannot even be located from the type alone. ++ * ++ * The types listed below are the ones whose SID valid_acl() has checked, so ++ * for them the SID is known to lie within the ACE. The others either move the ++ * SID as shown above or are reserved with no defined layout, and for those the ++ * bytes at the standard offset are not a SID and must not be read as one. ++ * ++ * The list is the set of types in MS-DTYP section 2.4.4.1 which are neither an ++ * object type nor reserved. It has to be kept in step with valid_acl(). ++ * ++ * Return TRUE if the SID is at the standard offset and FALSE otherwise. ++ */ ++ ++static BOOL sid_at_standard_offset(const ACCESS_ALLOWED_ACE *pace) ++{ ++ BOOL standard; ++ ++ switch (pace->type) { ++ case ACCESS_ALLOWED_ACE_TYPE : ++ case ACCESS_DENIED_ACE_TYPE : ++ case SYSTEM_AUDIT_ACE_TYPE : ++ case ACCESS_ALLOWED_CALLBACK_ACE_TYPE : ++ case ACCESS_DENIED_CALLBACK_ACE_TYPE : ++ case SYSTEM_AUDIT_CALLBACK_ACE_TYPE : ++ case SYSTEM_MANDATORY_LABEL_ACE_TYPE : ++ case SYSTEM_RESOURCE_ATTRIBUTE_ACE_TYPE : ++ case SYSTEM_SCOPED_POLICY_ID_ACE_TYPE : ++ standard = TRUE; ++ break; ++ default : ++ standard = FALSE; ++ break; ++ } ++ return (standard); ++} ++ + /* + * Do sanity checks on security descriptors read from storage + * basically, we make sure that every field holds within +@@ -656,6 +709,30 @@ BOOL ntfs_valid_descr(const char *securattr, unsigned int attrsz) + && (ntfs_attr_size(securattr) <= attrsz) + && ntfs_valid_sid((const SID*)&securattr[offowner]) + && ntfs_valid_sid((const SID*)&securattr[offgroup]) ++ /* ++ * Check that the owner and group SIDs, and the ACLs, ++ * end within the allocated storage. ++ * ++ * ntfs_attr_size() cannot be relied on for this. It ++ * only takes a field into account when the field lies ++ * beyond the ones examined before it, so a field at a ++ * lower offset is left out of the size it returns. The ++ * owner SID is the usual case, as it is placed before ++ * the group SID in most descriptors. ++ * ++ * The SID sizes are known sane here because ++ * ntfs_valid_sid() has just bounded the sub authority ++ * count, and the ACL sizes are readable because the ++ * ACL headers were checked to be within storage. ++ */ ++ && ((offowner + (unsigned int)ntfs_sid_size((const SID*) ++ &securattr[offowner])) <= attrsz) ++ && ((offgroup + (unsigned int)ntfs_sid_size((const SID*) ++ &securattr[offgroup])) <= attrsz) ++ && (!offdacl ++ || ((offdacl + le16_to_cpu(pdacl->size)) <= attrsz)) ++ && (!offsacl ++ || ((offsacl + le16_to_cpu(psacl->size)) <= attrsz)) + /* + * if there is an ACL, as indicated by offdacl, + * require SE_DACL_PRESENT +@@ -3220,7 +3297,8 @@ static int build_std_permissions(const char *securattr, + } + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; +- if (!(pace->flags & INHERIT_ONLY_ACE)) { ++ if (!(pace->flags & INHERIT_ONLY_ACE) ++ && sid_at_standard_offset(pace)) { + if (ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) { + noown = FALSE; +@@ -3310,7 +3388,8 @@ static int build_owngrp_permissions(const char *securattr, + } + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; +- if (!(pace->flags & INHERIT_ONLY_ACE)) { ++ if (!(pace->flags & INHERIT_ONLY_ACE) ++ && sid_at_standard_offset(pace)) { + if ((ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) + && (pace->mask & WRITE_OWNER)) { +@@ -3501,7 +3580,8 @@ static int build_ownadmin_permissions(const char *securattr, + for (nace = 0; nace < acecnt; nace++) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if (!(pace->flags & INHERIT_ONLY_ACE) +- && !(~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK))) { ++ && !(~pace->mask & (ROOT_OWNER_UNMARK | ROOT_GROUP_UNMARK)) ++ && sid_at_standard_offset(pace)) { + if ((ntfs_same_sid(usid, &pace->sid) + || ntfs_same_sid(ownersid, &pace->sid)) + && (((pace->mask & WRITE_OWNER) && firstapply))) { +@@ -3748,6 +3828,16 @@ struct POSIX_SECURITY *ntfs_build_permissions_posix( + pctx = &ctx[0]; + } + ignore = FALSE; ++ /* ++ * Only look for a SID at the standard offset when the ++ * ACE type keeps one there. An ACE which does not ++ * could never contribute to the Posix ACL below, as ++ * that needs an allow or deny type. ++ */ ++ if (!sid_at_standard_offset(pace)) { ++ offace += le16_to_cpu(pace->size); ++ continue; ++ } + /* + * grants for root as a designated user or group + */ diff -Nru ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_20.patch ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_20.patch --- ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_20.patch 1970-01-01 00:00:00.000000000 +0000 +++ ntfs-3g-2022.10.3/debian/patches/ntfs-3g_2022.10.3-sa_2026-06-1_20.patch 2026-09-21 03:57:24.000000000 +0000 @@ -0,0 +1,29 @@ +diff --git a/libntfs-3g/acls.c b/libntfs-3g/acls.c +index 9f16fecd0..ff5f723db 100644 +--- a/libntfs-3g/acls.c ++++ b/libntfs-3g/acls.c +@@ -3581,14 +3581,22 @@ const SID *ntfs_acl_owner(const char *securattr) + acecnt = le16_to_cpu(pacl->ace_count); + offace = offdacl + sizeof(ACL); + nace = 0; +- do { ++ /* ++ * Test the count before reading an ACE. An empty ACL ++ * has none to read and the bytes which would hold the ++ * first one are not part of the ACL. valid_acl() has ++ * not checked them either because it had no ACE to ++ * walk. ++ */ ++ while (!found && (nace < acecnt)) { + pace = (const ACCESS_ALLOWED_ACE*)&securattr[offace]; + if ((pace->mask & WRITE_OWNER) + && (pace->type == ACCESS_ALLOWED_ACE_TYPE) + && ntfs_is_user_sid(&pace->sid)) + found = TRUE; + offace += le16_to_cpu(pace->size); +- } while (!found && (++nace < acecnt)); ++ nace++; ++ } + } + if (found) + usid = &pace->sid; diff -Nru ntfs-3g-2022.10.3/debian/patches/series ntfs-3g-2022.10.3/debian/patches/series --- ntfs-3g-2022.10.3/debian/patches/series 2026-07-15 04:59:36.000000000 +0000 +++ ntfs-3g-2022.10.3/debian/patches/series 2026-10-01 21:06:57.000000000 +0000 @@ -7,3 +7,12 @@ CVE-2026-46571.patch CVE-2026-46570.patch CVE-2026-56135.patch +ntfs-3g_2022.10.3-sa_2026-06-1_05.patch +ntfs-3g_2022.10.3-sa_2026-06-1_07.patch +ntfs-3g_2022.10.3-sa_2026-06-1_08.patch +ntfs-3g_2022.10.3-sa_2026-06-1_10.patch +ntfs-3g_2022.10.3-sa_2026-06-1_11.patch +ntfs-3g_2022.10.3-sa_2026-06-1_12.patch +ntfs-3g_2022.10.3-sa_2026-06-1_19.patch +ntfs-3g_2022.10.3-sa_2026-06-1_20.patch +Revert-attrib.c-check-the-multi-sector-transfer-fixu.patch