]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ntfs: fix out-of-bounds write in ntfs_index_walk_down()
authorDaeMyung Kang <charsyam@gmail.com>
Thu, 7 May 2026 02:18:31 +0000 (11:18 +0900)
committerNamjae Jeon <linkinjeon@kernel.org>
Fri, 8 May 2026 14:51:10 +0000 (23:51 +0900)
ntfs_index_walk_down() used to update the index traversal depth
directly before writing parent_pos[] and parent_vcn[]. A malformed
directory index with too many child-node levels can therefore advance
pindex past MAX_PARENT_VCN and write past the fixed arrays in struct
ntfs_index_context, corrupting context state used by later index
traversal.

Use ntfs_icx_parent_inc() for walk-down transitions so the existing
depth limit is enforced before the arrays are updated. Make the helper
check the limit before incrementing pindex so failed callers do not
leave the context at an out-of-range depth.

This is reachable by iterating a crafted NTFS directory after the volume
has been mounted, including read-only mounts. The reproducer uses
getdents64() on an index root that points to an excessively deep chain
of child index blocks.

A crafted directory index with a chain of child-node entries reproduced
UBSAN array-index-out-of-bounds reports in ntfs_index_walk_down() and
subsequent KASAN reports in ntfs_index_walk_up(). With this change, the
same image is rejected with "Index is over 32 level deep" and no KASAN
or UBSAN report is emitted.

Fixes: 0a8ac0c1fa0b ("ntfs: update directory operations")
Suggested-by: Namjae Jeon <linkinjeon@kernel.org>
Signed-off-by: DaeMyung Kang <charsyam@gmail.com>
Reviewed-by: Hyunchul Lee <hyc.lee@gmail.com>
Signed-off-by: Namjae Jeon <linkinjeon@kernel.org>
fs/ntfs/index.c

index a547bdcfa4561f6bc39c4ef1e30ee0e6d50f9f10..146e011c1a418260592c395425f1b0fc6bc67d94 100644 (file)
@@ -677,11 +677,11 @@ static int ntfs_ib_read(struct ntfs_index_context *icx, s64 vcn, struct index_bl
 
 static int ntfs_icx_parent_inc(struct ntfs_index_context *icx)
 {
-       icx->pindex++;
-       if (icx->pindex >= MAX_PARENT_VCN) {
+       if (icx->pindex >= MAX_PARENT_VCN - 1) {
                ntfs_error(icx->idx_ni->vol->sb, "Index is over %d level deep", MAX_PARENT_VCN);
                return -EOPNOTSUPP;
        }
+       icx->pindex++;
        return 0;
 }
 
@@ -1970,6 +1970,7 @@ struct index_entry *ntfs_index_walk_down(struct index_entry *ie, struct ntfs_ind
 {
        struct index_entry *entry;
        struct index_block *ib;
+       int err;
        s64 vcn;
 
        entry = ie;
@@ -1979,14 +1980,20 @@ struct index_entry *ntfs_index_walk_down(struct index_entry *ie, struct ntfs_ind
                        ib = kvzalloc(ictx->block_size, GFP_NOFS);
                        if (!ib)
                                return ERR_PTR(-ENOMEM);
-                       /* down from level zero */
+                       /*
+                        * Descending from root index (level 0) to the first
+                        * child level. is_in_root == true implies pindex == 0,
+                        * so advance to level 1.
+                        */
+                       ictx->pindex = 1;
                        ictx->ir = NULL;
                        ictx->ib = ib;
-                       ictx->pindex = 1;
                        ictx->is_in_root = false;
                } else {
                        /* down from non-zero level */
-                       ictx->pindex++;
+                       err = ntfs_icx_parent_inc(ictx);
+                       if (err)
+                               return ERR_PTR(err);
                }
 
                ictx->parent_pos[ictx->pindex] = 0;