Re: [PATCH] fs/ntfs3: add depth limit to indx_find_buffer to prevent stack overflow
Konstantin Komarov <[email protected]> Thu, 30 Apr 2026 13:58:15 +0200
| Newsgroups | dev.linux.lists.ntfs3,org.kernel.vger.linux-fsdevel,org.kernel.vger.stable |
|---|---|
| Message-ID | <[email protected]> |
On 4/13/26 15:31, Michael Bommarito wrote: > [You don't often get email from [email protected]. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ] > > indx_find_buffer() recursively descends the B+ tree index with no depth > limit. A crafted NTFS image with circular index node references causes > unbounded recursion, overflowing the kernel stack and panicking the > system. > > This is reachable by mounting a malicious NTFS filesystem (e.g. from a > USB drive via desktop automount) and deleting a file whose index entry > triggers the rebalancing fallback path in indx_delete_entry(). > > Add a depth parameter and bail out with -EINVAL when it reaches the > fnd->nodes array bound, matching the constraint already enforced by > fnd_push() in indx_find(). > > The related function indx_find() was previously patched for a similar > infinite-loop issue (commit 1732053c8a6b), but indx_find_buffer() was > missed. > > Fixes: 82cae269cfa9 ("fs/ntfs3: Add initialization of super block") > Cc: [email protected] > Assisted-by: Claude:claude-opus-4-6 > Assisted-by: Codex:gpt-5-4 > Signed-off-by: Michael Bommarito <[email protected]> > --- > Found during a broader arch/um/ and filesystem security audit. > This is the same class of bug as the one fixed by commit > 1732053c8a6b ("fs: ntfs3: check return value of indx_find to > avoid infinite loop"), which added a depth limit to indx_find() > but missed indx_find_buffer(). > > Reproduced on UML (ARCH=um) with a crafted NTFS image containing > a circular B+ tree directory index. Mounting the image and > deleting a specific file triggers indx_delete_entry() -> > indx_find_buffer() -> unbounded recursion -> stack overflow: > > Kernel panic - not syncing: Kernel tried to access user memory > at addr 0x606128c4, ip 0x6012907e > Call Trace: > [<60611ec2>] ? indx_read_ra+0x0/0x677 > > At 168+ bytes per frame, ~97 recursions overflow the 16KB kernel > stack. Desktop automount (udisks2 + ntfs3) means a crafted USB > drive can trigger this without privilege. > > Note: the pre-existing indx_node allocated by indx_read() during > the DFS is leaked when the new depth limit fires. This is a > pre-existing issue (the node was also leaked on any other error > return from indx_find_buffer); fixing it cleanly requires > restructuring the node ownership model and is left for a > follow-up patch. > > Reproducer script and crafted image builder available on request. > > fs/ntfs3/index.c | 15 ++++++++++++--- > 1 file changed, 12 insertions(+), 3 deletions(-) > > diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c > index 97f06c26fe1a..2c43e7c27861 100644 > --- a/fs/ntfs3/index.c > +++ b/fs/ntfs3/index.c > @@ -2013,13 +2013,21 @@ int indx_insert_entry(struct ntfs_index *indx, struct ntfs_inode *ni, > static struct indx_node *indx_find_buffer(struct ntfs_index *indx, > struct ntfs_inode *ni, > const struct INDEX_ROOT *root, > - __le64 vbn, struct indx_node *n) > + __le64 vbn, struct indx_node *n, > + int depth) > { > int err; > const struct NTFS_DE *e; > struct indx_node *r; > const struct INDEX_HDR *hdr = n ? &n->index->ihdr : &root->ihdr; > > + /* > + * Limit recursion depth to prevent stack overflow from crafted > + * images. Use the same bound as the fnd->nodes array (20). > + */ > + if (depth > ARRAY_SIZE(((struct ntfs_fnd *)NULL)->nodes)) > + return ERR_PTR(-EINVAL); > + > /* Step 1: Scan one level. */ > for (e = hdr_first_de(hdr);; e = hdr_next_de(hdr, e)) { > if (!e) > @@ -2040,7 +2048,8 @@ static struct indx_node *indx_find_buffer(struct ntfs_index *indx, > if (err) > return ERR_PTR(err); > > - r = indx_find_buffer(indx, ni, root, vbn, n); > + r = indx_find_buffer(indx, ni, root, vbn, n, > + depth + 1); > if (r) > return r; > } > @@ -2446,7 +2455,7 @@ int indx_delete_entry(struct ntfs_index *indx, struct ntfs_inode *ni, > > fnd_clear(fnd); > > - in = indx_find_buffer(indx, ni, root, sub_vbn, NULL); > + in = indx_find_buffer(indx, ni, root, sub_vbn, NULL, 0); > if (IS_ERR(in)) { > err = PTR_ERR(in); > goto out; > -- > 2.53.0 > Hello, Sorry for the delay. Your patch was applied, thanks. Regards, Konstantin