diff options
Diffstat (limited to 'fs/ntfs3/index.c')
| -rw-r--r-- | fs/ntfs3/index.c | 45 |
1 files changed, 42 insertions, 3 deletions
diff --git a/fs/ntfs3/index.c b/fs/ntfs3/index.c index 5344b29b0577..ade276225999 100644 --- a/fs/ntfs3/index.c +++ b/fs/ntfs3/index.c @@ -1742,6 +1742,22 @@ static int indx_insert_into_root(struct ntfs_index *indx, struct ntfs_inode *ni, hdr_used = le32_to_cpu(hdr->used); hdr_total = le32_to_cpu(hdr->total); + /* + * The destination INDEX_BUFFER has 'hdr_total' bytes of payload + * available after the header, of which 'hdr_used' are already + * consumed by the single terminal END entry installed by + * indx_new(). A crafted image can present a resident root whose + * non-last entries (summing to 'to_move') exceed what fits in + * this buffer; copying them unchecked would overrun the + * kmalloc(1u << indx->index_bits) allocation backing the new + * buffer. Reject the copy in that case. + */ + if (to_move > hdr_total - hdr_used) { + err = -EINVAL; + ntfs_set_state(sbi, NTFS_DIRTY_ERROR); + goto out_put_n; + } + /* Copy root entries into new buffer. */ hdr_insert_head(hdr, re, to_move); @@ -1846,6 +1862,20 @@ indx_insert_into_buffer(struct ntfs_index *indx, struct ntfs_inode *ni, memcpy(up_e, sp, sp_size); used1 = le32_to_cpu(hdr1->used); + + /* + * hdr_find_split does not validate per-entry sizes, so a crafted + * NTFS_DE whose le16 size field is out of range can place sp such + * that (PtrOffset(hdr1, sp) + sp_size) exceeds used1. Without this + * guard the u32 'used = used1 - to_copy - sp_size' underflows and + * the subsequent memmove count becomes a near-4-GiB value, + * triggering an out-of-bounds kernel write. + */ + if (PtrOffset(hdr1, sp) + sp_size > used1) { + err = -EINVAL; + goto out; + } + hdr1_saved = kmemdup(hdr1, used1, GFP_NOFS); if (!hdr1_saved) { err = -ENOMEM; @@ -2022,13 +2052,21 @@ out1: 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) @@ -2049,7 +2087,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; } @@ -2462,7 +2501,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; |
