Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package ntfs-3g_ntfsprogs for 
openSUSE:Factory checked in at 2026-07-21 22:54:52
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/ntfs-3g_ntfsprogs (Old)
 and      /work/SRC/openSUSE:Factory/.ntfs-3g_ntfsprogs.new.24530 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "ntfs-3g_ntfsprogs"

Tue Jul 21 22:54:52 2026 rev:36 rq:1366709 version:2022.10.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/ntfs-3g_ntfsprogs/ntfs-3g_ntfsprogs.changes      
2026-05-13 20:59:03.176435333 +0200
+++ 
/work/SRC/openSUSE:Factory/.ntfs-3g_ntfsprogs.new.24530/ntfs-3g_ntfsprogs.changes
   2026-07-21 22:54:53.662414748 +0200
@@ -1,0 +2,17 @@
+Tue Jul 14 14:26:30 UTC 2026 - Scott Bradnick <[email protected]>
+
+- Adding patch bundle from upstream (download.tuxera.com/opensource)
+  re:cve_2026-04 for v2022.10.3
+  * bsc#1271104 -> 1_ntfs-3g_2022.10.3-CVE-2026-42618.patch
+  * bsc#1271102 -> 2_ntfs-3g_2022.10.3-CVE-2026-42616.patch
+  * bsc#1271103 -> 3_ntfs-3g_2022.10.3-CVE-2026-42617.patch
+  * bsc#1271105 -> 4_ntfs-3g_2022.10.3-CVE-2026-46569.patch
+  * bsc#1271107 -> 5_ntfs-3g_2022.10.3-CVE-2026-46571.patch
+  * bsc#1271106 -> 6_ntfs-3g_2022.10.3-CVE-2026-46570.patch
+  * bsc#1271108 -> 7_ntfs-3g_2022.10.3-CVE-2026-46572.patch
+    - This file is identical to 3_...42617.patch
+  * bsc#1271109 -> 8_ntfs-3g_2022.10.3-CVE-2026-56135.patch
+  * bsc#1271110 -> 9_ntfs-3g_2022.10.3-CVE-2026-56136.patch
+    - This file is identical to 3_...42617.patch
+
+-------------------------------------------------------------------

New:
----
  1_ntfs-3g_2022.10.3-CVE-2026-42618.patch
  2_ntfs-3g_2022.10.3-CVE-2026-42616.patch
  3_ntfs-3g_2022.10.3-CVE-2026-42617.patch
  4_ntfs-3g_2022.10.3-CVE-2026-46569.patch
  5_ntfs-3g_2022.10.3-CVE-2026-46571.patch
  6_ntfs-3g_2022.10.3-CVE-2026-46570.patch
  8_ntfs-3g_2022.10.3-CVE-2026-56135.patch

----------(New B)----------
  New:  re:cve_2026-04 for v2022.10.3
  * bsc#1271104 -> 1_ntfs-3g_2022.10.3-CVE-2026-42618.patch
  * bsc#1271102 -> 2_ntfs-3g_2022.10.3-CVE-2026-42616.patch
  New:  * bsc#1271104 -> 1_ntfs-3g_2022.10.3-CVE-2026-42618.patch
  * bsc#1271102 -> 2_ntfs-3g_2022.10.3-CVE-2026-42616.patch
  * bsc#1271103 -> 3_ntfs-3g_2022.10.3-CVE-2026-42617.patch
  New:  * bsc#1271102 -> 2_ntfs-3g_2022.10.3-CVE-2026-42616.patch
  * bsc#1271103 -> 3_ntfs-3g_2022.10.3-CVE-2026-42617.patch
  * bsc#1271105 -> 4_ntfs-3g_2022.10.3-CVE-2026-46569.patch
  New:  * bsc#1271103 -> 3_ntfs-3g_2022.10.3-CVE-2026-42617.patch
  * bsc#1271105 -> 4_ntfs-3g_2022.10.3-CVE-2026-46569.patch
  * bsc#1271107 -> 5_ntfs-3g_2022.10.3-CVE-2026-46571.patch
  New:  * bsc#1271105 -> 4_ntfs-3g_2022.10.3-CVE-2026-46569.patch
  * bsc#1271107 -> 5_ntfs-3g_2022.10.3-CVE-2026-46571.patch
  * bsc#1271106 -> 6_ntfs-3g_2022.10.3-CVE-2026-46570.patch
  New:  * bsc#1271107 -> 5_ntfs-3g_2022.10.3-CVE-2026-46571.patch
  * bsc#1271106 -> 6_ntfs-3g_2022.10.3-CVE-2026-46570.patch
  * bsc#1271108 -> 7_ntfs-3g_2022.10.3-CVE-2026-46572.patch
  New:    - This file is identical to 3_...42617.patch
  * bsc#1271109 -> 8_ntfs-3g_2022.10.3-CVE-2026-56135.patch
  * bsc#1271110 -> 9_ntfs-3g_2022.10.3-CVE-2026-56136.patch
----------(New E)----------

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ ntfs-3g_ntfsprogs.spec ++++++
--- /var/tmp/diff_new_pack.U21UU7/_old  2026-07-21 22:54:54.386439403 +0200
+++ /var/tmp/diff_new_pack.U21UU7/_new  2026-07-21 22:54:54.386439403 +0200
@@ -34,6 +34,17 @@
 Patch0:         ntfs3g-unistr-use-after-free.patch
 # PATCH-FIX-UPSTREAM ntfs3g-heap-overflow.patch bsc#1262216 [email protected] 
-- fix heap overflow
 Patch1:         ntfs3g-heap-overflow.patch
+Patch2:         1_ntfs-3g_2022.10.3-CVE-2026-42618.patch
+Patch3:         2_ntfs-3g_2022.10.3-CVE-2026-42616.patch
+Patch4:         3_ntfs-3g_2022.10.3-CVE-2026-42617.patch
+Patch5:         4_ntfs-3g_2022.10.3-CVE-2026-46569.patch
+Patch6:         5_ntfs-3g_2022.10.3-CVE-2026-46571.patch
+Patch7:         6_ntfs-3g_2022.10.3-CVE-2026-46570.patch
+# This is the same as 3_...42617.patch
+#Patch8:         7_ntfs-3g_2022.10.3-CVE-2026-46572.patch
+Patch9:         8_ntfs-3g_2022.10.3-CVE-2026-56135.patch
+# This is the same as 3_...42617.patch
+#Patch10:        9_ntfs-3g_2022.10.3-CVE-2026-56136.patch
 BuildRequires:  gnutls-devel
 BuildRequires:  hwinfo-devel
 BuildRequires:  libgcrypt-devel

++++++ 1_ntfs-3g_2022.10.3-CVE-2026-42618.patch ++++++
diff --git a/libntfs-3g/compress.c b/libntfs-3g/compress.c
index 390b2d97d..50d9daf32 100644
--- a/libntfs-3g/compress.c
+++ b/libntfs-3g/compress.c
@@ -459,7 +459,8 @@ static unsigned int ntfs_compress_block(const char *inbuf, 
const int bufsize,
  * @cb_start is a pointer to the compression block which needs decompressing
  * and @cb_size is the size of @cb_start in bytes (8-64kiB).
  *
- * Return 0 if success or -EOVERFLOW on error in the compressed stream.
+ * Return 0 on success.  On error in the compressed stream, set errno to
+ * EOVERFLOW and return -1.
  */
 static int ntfs_decompress(u8 *dest, const u32 dest_size,
                u8 *const cb_start, const u32 cb_size)
@@ -479,153 +480,211 @@ static int ntfs_decompress(u8 *dest, const u32 
dest_size,
        /* Variables for tag and token parsing. */
        u8 tag;                 /* Current tag. */
        int token;              /* Loop counter for the eight tokens in tag. */
+       u16 hdr;                /* Current sub-block header. */
 
        ntfs_log_trace("Entering, cb_size = 0x%x.\n", (unsigned)cb_size);
 do_next_sb:
-       ntfs_log_debug("Beginning sub-block at offset = %d in the cb.\n",
-                       (int)(cb - cb_start));
-       /*
-        * Have we reached the end of the compression block or the end of the
-        * decompressed data?  The latter can happen for example if the current
-        * position in the compression block is one byte before its end so the
-        * first two checks do not detect it.
-        */
-       if (cb == cb_end || !le16_to_cpup((le16*)cb) || dest == dest_end) {
-               if (dest_end > dest)
-                       memset(dest, 0, dest_end - dest);
-               ntfs_log_debug("Completed. Returning success (0).\n");
-               return 0;
-       }
-       /* Setup offset for the current sub-block destination. */
-       dest_sb_start = dest;
-       dest_sb_end = dest + NTFS_SB_SIZE;
-       /* Check that we are still within allowed boundaries. */
-       if (dest_sb_end > dest_end)
-               goto return_overflow;
-       /* Does the minimum size of a compressed sb overflow valid range? */
-       if (cb + 6 > cb_end)
-               goto return_overflow;
-       /* Setup the current sub-block source pointers and validate range. */
-       cb_sb_start = cb;
-       cb_sb_end = cb_sb_start + (le16_to_cpup((le16*)cb) & NTFS_SB_SIZE_MASK)
-                       + 3;
-       if (cb_sb_end > cb_end)
-               goto return_overflow;
-       /* Now, we are ready to process the current sub-block (sb). */
-       if (!(le16_to_cpup((le16*)cb) & NTFS_SB_IS_COMPRESSED)) {
-               ntfs_log_debug("Found uncompressed sub-block.\n");
-               /* This sb is not compressed, just copy it into destination. */
-               /* Advance source position to first data byte. */
-               cb += 2;
-               /* An uncompressed sb must be full size. */
-               if (cb_sb_end - cb != NTFS_SB_SIZE)
-                       goto return_overflow;
-               /* Copy the block and advance the source position. */
-               memcpy(dest, cb, NTFS_SB_SIZE);
-               cb += NTFS_SB_SIZE;
-               /* Advance destination position to next sub-block. */
-               dest += NTFS_SB_SIZE;
-               goto do_next_sb;
-       }
-       ntfs_log_debug("Found compressed sub-block.\n");
-       /* This sb is compressed, decompress it into destination. */
-       /* Forward to the first tag in the sub-block. */
-       cb += 2;
-do_next_tag:
-       if (cb == cb_sb_end) {
-               /* Check if the decompressed sub-block was not full-length. */
-               if (dest < dest_sb_end) {
-                       int nr_bytes = dest_sb_end - dest;
-
-                       ntfs_log_debug("Filling incomplete sub-block with 
zeroes.\n");
-                       /* Zero remainder and update destination position. */
-                       memset(dest, 0, nr_bytes);
-                       dest += nr_bytes;
-               }
-               /* We have finished the current sub-block. */
-               goto do_next_sb;
-       }
-       /* Check we are still in range. */
-       if (cb > cb_sb_end || dest > dest_sb_end)
-               goto return_overflow;
-       /* Get the next tag and advance to first token. */
-       tag = *cb++;
-       /* Parse the eight tokens described by the tag. */
-       for (token = 0; token < 8; token++, tag >>= 1) {
-               u16 lg, pt, length, max_non_overlap;
-               register u16 i;
-               u8 *dest_back_addr;
-
-               /* Check if we are done / still in range. */
-               if (cb >= cb_sb_end || dest > dest_sb_end)
-                       break;
-               /* Determine token type and parse appropriately.*/
-               if ((tag & NTFS_TOKEN_MASK) == NTFS_SYMBOL_TOKEN) {
-                       /*
-                        * We have a symbol token, copy the symbol across, and
-                        * advance the source and destination positions.
-                        */
-                       *dest++ = *cb++;
-                       /* Continue with the next token. */
-                       continue;
-               }
+       {
+               ntfs_log_debug("Beginning sub-block at offset = %d in the "
+                               "cb.\n",
+                               (int)(cb - cb_start));
                /*
-                * We have a phrase token. Make sure it is not the first tag in
-                * the sb as this is illegal and would confuse the code below.
+                * Have we reached the end of the compression block or the end
+                * of the decompressed data?
+                *
+                * Check dest == dest_end before reading the next two-byte sub-
+                * block header.  The compressed pointer can legally be near the
+                * end of the buffer when the requested output has already been
+                * produced.
                 */
-               if (dest == dest_sb_start)
+               if (cb == cb_end || dest == dest_end)
+                       goto complete;
+               /* A sub-block header is two bytes. */
+               if ((size_t)(cb_end - cb) < 2)
                        goto return_overflow;
-               /*
-                * Determine the number of bytes to go back (p) and the number
-                * of bytes to copy (l). We use an optimized algorithm in which
-                * we first calculate log2(current destination position in sb),
-                * which allows determination of l and p in O(1) rather than
-                * O(n). We just need an arch-optimized log2() function now.
-                */
-               lg = 0;
-               for (i = dest - dest_sb_start - 1; i >= 0x10; i >>= 1)
-                       lg++;
-               /* Get the phrase token into i. */
-               pt = le16_to_cpup((le16*)cb);
-               /*
-                * Calculate starting position of the byte sequence in
-                * the destination using the fact that p = (pt >> (12 - lg)) + 1
-                * and make sure we don't go too far back.
-                */
-               dest_back_addr = dest - (pt >> (12 - lg)) - 1;
-               if (dest_back_addr < dest_sb_start)
+               hdr = le16_to_cpup((le16*)cb);
+               if (!hdr)
+                       goto complete;
+               /* Setup offset for the current sub-block destination. */
+               dest_sb_start = dest;
+               dest_sb_end = dest + NTFS_SB_SIZE;
+               /* Check that we are still within allowed boundaries. */
+               if (dest_sb_end > dest_end)
                        goto return_overflow;
-               /* Now calculate the length of the byte sequence. */
-               length = (pt & (0xfff >> lg)) + 3;
-               /* Verify destination is in range. */
-               if (dest + length > dest_sb_end)
+               /* Does the minimum size of a compressed sb overflow valid
+                * range? */
+               if ((size_t)(cb_end - cb) < 6)
                        goto return_overflow;
-               /* The number of non-overlapping bytes. */
-               max_non_overlap = dest - dest_back_addr;
-               if (length <= max_non_overlap) {
-                       /* The byte sequence doesn't overlap, just copy it. */
-                       memcpy(dest, dest_back_addr, length);
-                       /* Advance destination pointer. */
-                       dest += length;
-               } else {
+               /* Setup the current sub-block source pointers and validate
+                * range. */
+               cb_sb_start = cb;
+               cb_sb_end = cb_sb_start + (hdr & NTFS_SB_SIZE_MASK) + 3;
+               if (cb_sb_end > cb_end)
+                       goto return_overflow;
+               /* Now, we are ready to process the current sub-block (sb). */
+               if (!(hdr & NTFS_SB_IS_COMPRESSED)) {
+                       ntfs_log_debug("Found uncompressed sub-block.\n");
+                       /* This sb is not compressed, just copy it into
+                        * destination. */
+                       /* Advance source position to first data byte. */
+                       cb += 2;
+                       /* An uncompressed sb must be full size. */
+                       if (cb_sb_end - cb != NTFS_SB_SIZE)
+                               goto return_overflow;
+                       /* Copy the block and advance the source position. */
+                       memcpy(dest, cb, NTFS_SB_SIZE);
+                       cb += NTFS_SB_SIZE;
+                       /* Advance destination position to next sub-block. */
+                       dest += NTFS_SB_SIZE;
+                       goto do_next_sb;
+               }
+               ntfs_log_debug("Found compressed sub-block.\n");
+               /* This sb is compressed, decompress it into destination. */
+               /* Forward to the first tag in the sub-block. */
+               cb += 2;
+do_next_tag:
+               {
+                       /* Check we are still in range. */
+                       if (cb > cb_sb_end || dest > dest_sb_end)
+                               goto return_overflow;
                        /*
-                        * The byte sequence does overlap, copy non-overlapping
-                        * part and then do a slow byte by byte copy for the
-                        * overlapping part. Also, advance the destination
-                        * pointer.
+                        * Finish this compressed sub-block when either its
+                        * compressed input is exhausted or its 4 KiB output
+                        * slot is full.  If output becomes full first, skip the
+                        * remaining compressed bytes in this sub-block instead
+                        * of interpreting them as more tags/tokens.
                         */
-                       memcpy(dest, dest_back_addr, max_non_overlap);
-                       dest += max_non_overlap;
-                       dest_back_addr += max_non_overlap;
-                       length -= max_non_overlap;
-                       while (length--)
-                               *dest++ = *dest_back_addr++;
+                       if (cb == cb_sb_end || dest == dest_sb_end)
+                               goto finish_compressed_sb;
+                       /* Get the next tag and advance to first token. */
+                       tag = *cb++;
+                       /* Parse the eight tokens described by the tag. */
+                       for (token = 0; token < 8; token++, tag >>= 1) {
+                               u16 lg, pt, length, max_non_overlap;
+                               register u16 i;
+                               u8 *dest_back_addr;
+
+                               /* Check if we are done / still in range. */
+                               if (cb > cb_sb_end || dest > dest_sb_end)
+                                       goto return_overflow;
+                               if (cb == cb_sb_end || dest == dest_sb_end)
+                                       goto finish_compressed_sb;
+                               /* Determine token type and parse
+                                * appropriately. */
+                               if ((tag & NTFS_TOKEN_MASK) ==
+                                               NTFS_SYMBOL_TOKEN)
+                               {
+                                       /*
+                                        * We have a symbol token, copy the
+                                        * symbol across, and advance the source
+                                        * and destination positions.
+                                        */
+                                       *dest++ = *cb++;
+                                       /* Continue with the next token. */
+                                       continue;
+                               }
+                               /*
+                                * We have a phrase token. Make sure it is not
+                                * the first token in the sb as this is illegal
+                                * and would confuse the code below.
+                                */
+                               if (dest == dest_sb_start)
+                                       goto return_overflow;
+                               /*
+                                * A phrase token is a two-byte compressed word.
+                                * The generic token check above only proves
+                                * that at least one byte remains, which is
+                                * enough for a symbol token but not for a
+                                * phrase token.
+                                */
+                               if ((size_t)(cb_sb_end - cb) < 2)
+                                       goto return_overflow;
+                               /*
+                                * Determine the number of bytes to go back (p)
+                                * and the number of bytes to copy (l). We use
+                                * an optimized algorithm in which we first
+                                * calculate log2(current destination position
+                                * in sb), which allows determination of l and p
+                                * in O(1) rather than O(n). We just need an
+                                * arch-optimized log2() function now.
+                                */
+                               lg = 0;
+                               for (i = dest - dest_sb_start - 1; i >= 0x10;
+                                               i >>= 1)
+                               {
+                                       lg++;
+                               }
+                               /* Get the phrase token into i. */
+                               pt = le16_to_cpup((le16*)cb);
+                               /*
+                                * Calculate starting position of the byte
+                                * sequence in the destination using the fact
+                                * that p = (pt >> (12 - lg)) + 1 and make sure
+                                * we don't go too far back.
+                                */
+                               dest_back_addr = dest - (pt >> (12 - lg)) - 1;
+                               if (dest_back_addr < dest_sb_start)
+                                       goto return_overflow;
+                               /* Now calculate the length of the byte
+                                * sequence. */
+                               length = (pt & (0xfff >> lg)) + 3;
+                               /* Verify destination is in range. */
+                               if (dest + length > dest_sb_end)
+                                       goto return_overflow;
+                               /* The number of non-overlapping bytes. */
+                               max_non_overlap = dest - dest_back_addr;
+                               if (length <= max_non_overlap) {
+                                       /* The byte sequence doesn't overlap,
+                                        * just copy it. */
+                                       memcpy(dest, dest_back_addr, length);
+                                       /* Advance destination pointer. */
+                                       dest += length;
+                               } else {
+                                       /*
+                                        * The byte sequence does overlap, copy
+                                        * non-overlapping part and then do a
+                                        * slow byte by byte copy for the
+                                        * overlapping part. Also, advance the
+                                        * destination pointer.
+                                        */
+                                       memcpy(dest, dest_back_addr,
+                                               max_non_overlap);
+                                       dest += max_non_overlap;
+                                       dest_back_addr += max_non_overlap;
+                                       length -= max_non_overlap;
+                                       while (length--)
+                                               *dest++ = *dest_back_addr++;
+                               }
+                               /* Advance source position and continue with the
+                                * next token. */
+                               cb += 2;
+                       }
+                       /* No tokens left in the current tag. Continue with the
+                        * next tag. */
+                       goto do_next_tag;
                }
-               /* Advance source position and continue with the next token. */
-               cb += 2;
        }
-       /* No tokens left in the current tag. Continue with the next tag. */
-       goto do_next_tag;
+finish_compressed_sb:
+       /* Check if the decompressed sub-block was not full-length. */
+       if (dest < dest_sb_end) {
+               size_t nr_bytes = dest_sb_end - dest;
+
+               ntfs_log_debug("Filling incomplete sub-block with zeroes.\n");
+               /* Zero remainder and update destination position. */
+               memset(dest, 0, nr_bytes);
+               dest += nr_bytes;
+       }
+       /*
+        * If the output sub-block filled before the compressed sub-block input
+        * was exhausted, skip the remaining compressed bytes in this sub-block.
+        */
+       cb = cb_sb_end;
+       goto do_next_sb;
+complete:
+       if (dest_end > dest)
+               memset(dest, 0, dest_end - dest);
+       ntfs_log_debug("Completed. Returning success (0).\n");
+       return 0;
 return_overflow:
        errno = EOVERFLOW;
        ntfs_log_perror("Failed to decompress file");

++++++ 2_ntfs-3g_2022.10.3-CVE-2026-42616.patch ++++++
diff --git a/ntfsprogs/ntfscat.c b/ntfsprogs/ntfscat.c
index b8af25033..0f711815a 100644
--- a/ntfsprogs/ntfscat.c
+++ b/ntfsprogs/ntfscat.c
@@ -334,22 +334,17 @@ static int index_get_size(ntfs_inode *inode)
 static int cat(ntfs_volume *vol, ntfs_inode *inode, ATTR_TYPES type,
                ntfschar *name, int namelen)
 {
-       const int bufsize = 4096;
        char *buffer;
        ntfs_attr *attr;
        s64 bytes_read, written;
        s64 offset;
        u32 block_size;
-
-       buffer = malloc(bufsize);
-       if (!buffer)
-               return 1;
+       u32 bufsize = 4096;
 
        attr = ntfs_attr_open(inode, type, name, namelen);
        if (!attr) {
                ntfs_log_error("Cannot find attribute type 0x%x.\n",
                                le32_to_cpu(type));
-               free(buffer);
                return 1;
        }
 
@@ -360,6 +355,15 @@ static int cat(ntfs_volume *vol, ntfs_inode *inode, 
ATTR_TYPES type,
        else
                block_size = 0;
 
+       if (bufsize < block_size) {
+               bufsize = block_size;
+       }
+
+       buffer = malloc(bufsize);
+       if (!buffer) {
+               return 1;
+       }
+
        offset = 0;
        for (;;) {
                if (!opts.raw && block_size > 0) {

++++++ 3_ntfs-3g_2022.10.3-CVE-2026-42617.patch ++++++
diff --git a/include/ntfs-3g/index.h b/include/ntfs-3g/index.h
index d001863ae..1d5845d47 100644
--- a/include/ntfs-3g/index.h
+++ b/include/ntfs-3g/index.h
@@ -143,6 +143,7 @@ extern int ntfs_index_block_inconsistent(const INDEX_BLOCK 
*ib, u32 block_size,
                        u64 inum, VCN vcn);
 extern int ntfs_index_entry_inconsistent(const INDEX_ENTRY *ie,
                        COLLATION_RULES collation_rule, u64 inum);
+extern int ntfs_ie_stream_inconsistent(const INDEX_HEADER *ih, u64 inum);
 extern int ntfs_index_lookup(const void *key, const int key_len,
                ntfs_index_context *ictx) __attribute_warn_unused_result__;
 
diff --git a/libntfs-3g/attrib.c b/libntfs-3g/attrib.c
index efb919438..cfc2446e3 100644
--- a/libntfs-3g/attrib.c
+++ b/libntfs-3g/attrib.c
@@ -3528,6 +3528,10 @@ int ntfs_attr_inconsistent(const ATTR_RECORD *a, const 
MFT_REF mref)
                        if (a->non_resident
                            || (le32_to_cpu(a->value_length)
                                < offsetof(INDEX_ROOT, index.reserved))
+                           || (le32_to_cpu(ir->index_block_size)
+                               < NTFS_BLOCK_SIZE)
+                           || (le32_to_cpu(ir->index_block_size)
+                               & (le32_to_cpu(ir->index_block_size) - 1))
                            || (le32_to_cpu(ir->index.entries_offset)
                                < sizeof(INDEX_HEADER))
                            || (le32_to_cpu(ir->index.index_length)
@@ -3542,6 +3546,9 @@ int ntfs_attr_inconsistent(const ATTR_RECORD *a, const 
MFT_REF mref)
                                        (long long)inum);
                                errno = EIO;
                                ret = -1;
+                       } else if (ntfs_ie_stream_inconsistent(&ir->index, 
inum)) {
+                               errno = EIO;
+                               ret = -1;
                        }
                        break;
                case AT_STANDARD_INFORMATION :
diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c
index e48d6aafb..c9651a62c 100644
--- a/libntfs-3g/index.c
+++ b/libntfs-3g/index.c
@@ -504,6 +504,8 @@ int ntfs_index_block_inconsistent(const INDEX_BLOCK *ib, 
u32 block_size,
                                (unsigned long long)inum);
                return -1;
        }
+       if (ntfs_ie_stream_inconsistent(&ib->index, inum))
+               return -1;
 
        return (0);
 }
@@ -560,7 +562,40 @@ int ntfs_index_entry_inconsistent(const INDEX_ENTRY *ie,
        return (ret);
 }
 
-/** 
+int ntfs_ie_stream_inconsistent(const INDEX_HEADER *ih, u64 inum)
+{
+       const u8 *ies_start = (const u8 *)ih + le32_to_cpu(ih->entries_offset);
+       const u8 *ies_end = (const u8 *)ih + le32_to_cpu(ih->index_length);
+       const u8 *ie;
+
+       ntfs_log_trace("Entering\n");
+
+       for (ie = ies_start; ie < ies_end; ) {
+               u32 len;
+               const INDEX_ENTRY *ent = (const INDEX_ENTRY *)ie;
+
+               if ((size_t)(ies_end - ie) < sizeof(INDEX_ENTRY_HEADER))
+                       goto err;
+               len = le16_to_cpu(ent->length);
+               if (len < sizeof(INDEX_ENTRY_HEADER) || (len & 7))
+                       goto err;
+               if ((size_t)(ies_end - ie) < len)
+                       goto err;
+               if (ent->ie_flags & INDEX_ENTRY_END) {
+                       /* END must terminate the stream exactly. */
+                       if (ie + len != ies_end)
+                               goto err;
+                       return 0;
+               }
+               ie += len;
+       }
+err:
+       ntfs_log_error("Corrupt index entry stream in inode %lld\n",
+                       (long long)inum);
+       return -1;
+}
+
+/**
  * Find a key in the index block.
  * 
  * Return values:
@@ -1091,14 +1126,16 @@ out:
 
 static INDEX_BLOCK *ntfs_ir_to_ib(INDEX_ROOT *ir, VCN ib_vcn)
 {
+       u32 ib_size;
        INDEX_BLOCK *ib;
        INDEX_ENTRY *ie_last;
        char *ies_start, *ies_end;
        int i;
        
        ntfs_log_trace("Entering\n");
-       
-       ib = ntfs_ib_alloc(ib_vcn, le32_to_cpu(ir->index_block_size), 
LEAF_NODE);
+
+       ib_size = le32_to_cpu(ir->index_block_size);
+       ib = ntfs_ib_alloc(ib_vcn, ib_size, LEAF_NODE);
        if (!ib)
                return NULL;
        
@@ -1110,6 +1147,18 @@ static INDEX_BLOCK *ntfs_ir_to_ib(INDEX_ROOT *ir, VCN 
ib_vcn)
         * as well, which can never have any data.
         */
        i = (char *)ie_last - ies_start + le16_to_cpu(ie_last->length);
+
+       if (offsetof(INDEX_BLOCK, index) + le32_to_cpu(ib->index.entries_offset)
+                       + i > ib_size)
+       {
+               ntfs_log_error("Last entry in index root overflows the index "
+                               "block size: %d (index block size: %lu)\n",
+                               i, (unsigned long)ib_size);
+               free(ib);
+               errno = EIO;
+               return NULL;
+       }
+
        memcpy(ntfs_ie_get_first(&ib->index), ies_start, i);
        
        ib->index.ih_flags = ir->index.ih_flags;

++++++ 4_ntfs-3g_2022.10.3-CVE-2026-46569.patch ++++++
diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c
index e48d6aafb..6ebaa2e4a 100644
--- a/libntfs-3g/index.c
+++ b/libntfs-3g/index.c
@@ -1144,7 +1144,7 @@ static int ntfs_ib_copy_tail(ntfs_index_context *icx, 
INDEX_BLOCK *src,
 {
        u8 *ies_end;
        INDEX_ENTRY *ie_head;           /* first entry after the median */
-       int tail_size, ret;
+       int tail_size, dst_capacity, ret;
        INDEX_BLOCK *dst;
        
        ntfs_log_trace("Entering\n");
@@ -1158,6 +1158,25 @@ static int ntfs_ib_copy_tail(ntfs_index_context *icx, 
INDEX_BLOCK *src,
        
        ies_end = (u8 *)ntfs_ie_get_end(&src->index);
        tail_size = ies_end - (u8 *)ie_head;
+       dst_capacity = (int)(le32_to_cpu(dst->index.allocated_size)
+                            - le32_to_cpu(dst->index.entries_offset));
+
+       /*
+        * src->index.entries_offset (on-disk, only required to be >=
+        * sizeof(INDEX_HEADER) by ntfs_index_block_inconsistent) may be smaller
+        * than dst's, which ntfs_ib_alloc fixes (40 on a 4 KiB block).  When
+        * the median lands near the start of the source stream the gap lets
+        * tail_size exceed dst's usable space.  The < 0 guard catches a
+        * negative tail_size before memcpy sign-extends it into a huge size_t.
+        */
+       if (tail_size < 0 || tail_size > dst_capacity) {
+               ntfs_log_error("Invalid tail_size %d (dst capacity %d) in "
+                               "ntfs_ib_copy_tail\n", tail_size, dst_capacity);
+               free(dst);
+               errno = EIO;
+               return STATUS_ERROR;
+       }
+
        memcpy(ntfs_ie_get_first(&dst->index), ie_head, tail_size);
        
        dst->index.index_length = cpu_to_le32(tail_size + 

++++++ 5_ntfs-3g_2022.10.3-CVE-2026-46571.patch ++++++
diff --git a/libntfs-3g/reparse.c b/libntfs-3g/reparse.c
index 76dbe2f1b..54858caa7 100644
--- a/libntfs-3g/reparse.c
+++ b/libntfs-3g/reparse.c
@@ -196,11 +196,19 @@ static u64 ntfs_fix_file_name(ntfs_inode *dir_ni, 
ntfschar *uname,
                                lemref = entry->indexed_file;
                                mref = le64_to_cpu(lemref);
                                if (NVolCaseSensitive(vol) || !vol->locase) {
-                                       for (i=0; i<found->file_name_length; 
i++)
+                                       for (i=0; i<found->file_name_length;
+                                                       i++)
                                                uname[i] = found->file_name[i];
                                } else {
-                                       for (i=0; i<found->file_name_length; 
i++)
-                                               uname[i] = 
vol->locase[le16_to_cpu(found->file_name[i])];
+                                       for (i=0; i<found->file_name_length;
+                                                       i++) {
+                                               u16 u = le16_to_cpu(found->
+                                                               file_name[i]);
+                                               uname[i] = (u < vol->upcase_len)
+                                                               ? vol->locase[u]
+                                                               : found->
+                                                                 file_name[i];
+                                       }
                                }
                        }
                }

++++++ 6_ntfs-3g_2022.10.3-CVE-2026-46570.patch ++++++
diff --git a/libntfs-3g/index.c b/libntfs-3g/index.c
index e48d6aafb..4768128d2 100644
--- a/libntfs-3g/index.c
+++ b/libntfs-3g/index.c
@@ -2056,6 +2056,13 @@ static INDEX_ENTRY *ntfs_index_walk_down(INDEX_ENTRY *ie,
                        /* down from non-zero level */
                        
                        ictx->pindex++;
+                       if (ictx->pindex >= MAX_PARENT_VCN) {
+                               errno = EOPNOTSUPP;
+                               ntfs_log_perror("Index is over %d level deep",
+                                               MAX_PARENT_VCN);
+                               entry = (INDEX_ENTRY*)NULL;
+                               break;
+                       }
                }
                ictx->parent_pos[ictx->pindex] = 0;
                ictx->parent_vcn[ictx->pindex] = vcn;

++++++ 8_ntfs-3g_2022.10.3-CVE-2026-56135.patch ++++++
diff --git a/include/ntfs-3g/acls.h b/include/ntfs-3g/acls.h
index 932791e97..4f36389c5 100644
--- a/include/ntfs-3g/acls.h
+++ b/include/ntfs-3g/acls.h
@@ -168,6 +168,8 @@ char *ntfs_build_descr_posix(struct MAPPING* const 
mapping[],
 
 #endif /* POSIXACLS */
 
+int ntfs_inherit_acl_extra_size(const ACL *acl, const SID *usid,
+                       const SID *gsid);
 int ntfs_inherit_acl(const ACL *oldacl, ACL *newacl,
                        const SID *usid, const SID *gsid,
                        BOOL fordir, le16 inherited);
diff --git a/libntfs-3g/acls.c b/libntfs-3g/acls.c
index 9f16fecd0..f9ed58f40 100644
--- a/libntfs-3g/acls.c
+++ b/libntfs-3g/acls.c
@@ -560,7 +560,9 @@ static BOOL valid_acl(const ACL *pacl, unsigned int end)
                        pace = (const ACCESS_ALLOWED_ACE*)
                                &((const char*)pacl)[offace];
                        acesz = le16_to_cpu(pace->size);
-                       switch (pace->type) {
+                       if (acesz < sizeof(ACE_HEADER))
+                               ok = FALSE;
+                       else switch (pace->type) {
                        case ACCESS_ALLOWED_ACE_TYPE :
                        case ACCESS_DENIED_ACE_TYPE :
                                wantsz = ntfs_sid_size(&pace->sid) + 8;
@@ -681,6 +683,87 @@ BOOL ntfs_valid_descr(const char *securattr, unsigned int 
attrsz)
        return (ok);
 }
 
+/**
+ * ntfs_inherit_acl_extra_size: compute creator SID inheritance slack
+ * @acl: ACL to scan
+ * @usid: owner SID to substitute for CREATOR_OWNER
+ * @gsid: group SID to substitute for CREATOR_GROUP
+ *
+ * Walks @acl bounded by acl->size, adding slack for ALLOW and DENY ACEs
+ * whose SID matches either creator placeholder.  ntfs_inherit_acl() can
+ * replace those placeholders by @usid or @gsid and, for directories, can
+ * also keep a verbatim copy for child inheritance.  Count the worst-case
+ * extra bytes so the inherited descriptor allocation cannot be overrun.
+ *
+ * Return: extra bytes needed, or 0 if @acl is NULL or structurally rejected.
+ */
+
+int ntfs_inherit_acl_extra_size(const ACL *acl,
+                       const SID *usid, const SID *gsid)
+{
+       const ACCESS_ALLOWED_ACE *ace;
+       unsigned int off;
+       unsigned int acl_size;
+       unsigned int acesz;
+       unsigned int sidsz;
+       int usidsz;
+       int gsidsz;
+       int ownersidsz;
+       int groupsidsz;
+       int oldcnt;
+       int nace;
+       int extra;
+       BOOL usid_is_group_sid;
+
+       extra = 0;
+       if (!acl || !usid || !gsid)
+               return (0);
+       acl_size = le16_to_cpu(acl->size);
+       if (acl_size < sizeof(ACL))
+               return (0);
+       oldcnt = le16_to_cpu(acl->ace_count);
+       usidsz = ntfs_sid_size(usid);
+       gsidsz = ntfs_sid_size(gsid);
+       ownersidsz = sizeof(ownersidbytes);
+       groupsidsz = sizeof(groupsidbytes);
+       usid_is_group_sid = ntfs_same_sid(usid, groupsid);
+       off = sizeof(ACL);
+       for (nace = 0; nace < oldcnt; nace++) {
+               if (off + 8 > acl_size)
+                       break;
+               ace = (const ACCESS_ALLOWED_ACE*)((const char*)acl + off);
+               acesz = le16_to_cpu(ace->size);
+               if (acesz < 8 || acesz > acl_size - off)
+                       break;
+               switch (ace->type) {
+               case ACCESS_ALLOWED_ACE_TYPE :
+               case ACCESS_DENIED_ACE_TYPE :
+                       if ((acesz >= 8 + sizeof(ownersidbytes))
+                                       && ntfs_valid_sid(&ace->sid)) {
+                               sidsz = ntfs_sid_size(&ace->sid);
+                               if (sidsz <= acesz - 8) {
+                                       if (ntfs_same_sid(&ace->sid, ownersid))
+                                       {
+                                               extra += usidsz - ownersidsz +
+                                                               20;
+                                               if (usid_is_group_sid)
+                                                       extra += gsidsz -
+                                                               groupsidsz + 20;
+                                       }
+                                       if (ntfs_same_sid(&ace->sid, groupsid))
+                                               extra += gsidsz - groupsidsz +
+                                                               20;
+                               }
+                       }
+                       break;
+               default :
+                       break;
+               }
+               off += acesz;
+       }
+       return extra;
+}
+
 /*
  *             Copy the inheritable parts of an ACL
  *
diff --git a/libntfs-3g/security.c b/libntfs-3g/security.c
index acee0a5ed..d0e030e19 100644
--- a/libntfs-3g/security.c
+++ b/libntfs-3g/security.c
@@ -3944,6 +3944,30 @@ static le32 build_inherited_id(struct SECURITY_CONTEXT 
*scx,
        usidsz = ntfs_sid_size(usid);
        gsidsz = ntfs_sid_size(gsid);
        newattrsz = parentattrsz + 3*usidsz + 3*gsidsz;
+       /*
+        * The +3*usidsz + 3*gsidsz slack above only covers a few creator SID
+        * expansions during ntfs_inherit_acl().  Add worst-case slack for every
+        * ALLOW/DENY creator-owner and creator-group ACE in both the parent
+        * DACL and SACL.
+        */
+       if (pphead->dacl) {
+               offpacl = le32_to_cpu(pphead->dacl);
+               if ((unsigned int)offpacl + sizeof(ACL) <=
+                               (unsigned int)parentattrsz) {
+                       ppacl = (const ACL*)&parentattr[offpacl];
+                       newattrsz += ntfs_inherit_acl_extra_size(ppacl,
+                                       usid, gsid);
+               }
+       }
+       if (pphead->sacl) {
+               offpacl = le32_to_cpu(pphead->sacl);
+               if ((unsigned int)offpacl + sizeof(ACL) <=
+                               (unsigned int)parentattrsz) {
+                       ppacl = (const ACL*)&parentattr[offpacl];
+                       newattrsz += ntfs_inherit_acl_extra_size(ppacl,
+                                       usid, gsid);
+               }
+       }
        if (fordir)
                newattrsz *= 2;
        newattr = (char*)ntfs_malloc(newattrsz);

Reply via email to