mirror of
https://github.com/torvalds/linux.git
synced 2026-11-07 06:17:02 +01:00
ocfs2: validate suballoc bit during inode read
i_suballoc_bit of a dinode is currently not validated at all. A corrupted dinode can carry an abnormally large i_suballoc_bit, which bypasses ocfs2_validate_inode_block(). When the inode is deleted, ocfs2_remove_inode() calls ocfs2_free_dinode(), which passes the unvalidated bit to _ocfs2_free_suballoc_bits() and triggers BUG_ON((count + start_bit) > ocfs2_bits_per_group(cl)). A suballocator block group bitmap is contained in a single block and starts after the group descriptor header, so a valid suballoc bit must be smaller than the number of bits fitting in the remaining space. Reject oversized i_suballoc_bit values during dinode validation. The bound is derived from ocfs2_group_bitmap_size() so it is also tight when discontig_bg caps the suballocator bitmap at OCFS2_MAX_BG_BITMAP_SIZE. Note the above check alone is not sufficient since the freeing path compares the bit against ocfs2_bits_per_group(), which is derived from cl_cpg/cl_bpc of the allocator dinode that is not validated against the actual group capacity and can be artificially smaller on a corrupted image. Convert this BUG_ON in _ocfs2_free_suballoc_bits() to ocfs2_error() as well. Link: https://lore.kernel.org/20260901125221.1634686-3-joseph.qi@linux.alibaba.com Signed-off-by: Joseph Qi <joseph.qi@linux.alibaba.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Cc: Changwei Ge <gechangwei@live.cn> Cc: Heming Zhao <heming.zhao@suse.com> Cc: Joel Becker <jlbec@evilplan.org> Cc: Jun Piao <piaojun@huawei.com> Cc: Junxiao Bi <junxiao.bi@oracle.com> Cc: Mark Fasheh <mark@fasheh.com>
This commit is contained in:
parent
25bf9e5493
commit
e706b89c6a
|
|
@ -1547,6 +1547,22 @@ int ocfs2_validate_inode_block(struct super_block *sb,
|
|||
goto bail;
|
||||
}
|
||||
|
||||
/*
|
||||
* A suballocator block group bitmap is contained in a single block
|
||||
* and starts after the group descriptor header, so a valid suballoc
|
||||
* bit can never exceed ocfs2_suballoc_bits_per_block(). Otherwise
|
||||
* deleting the inode will pass the oversized bit to
|
||||
* _ocfs2_free_suballoc_bits() via ocfs2_free_dinode() and trigger
|
||||
* BUG_ON((count + start_bit) > ocfs2_bits_per_group(cl)), since any
|
||||
* group holds at most ocfs2_suballoc_bits_per_block() bits.
|
||||
*/
|
||||
if (le16_to_cpu(di->i_suballoc_bit) >= ocfs2_suballoc_bits_per_block(sb)) {
|
||||
rc = ocfs2_error(sb, "Invalid dinode %llu: suballoc bit %u\n",
|
||||
(unsigned long long)bh->b_blocknr,
|
||||
le16_to_cpu(di->i_suballoc_bit));
|
||||
goto bail;
|
||||
}
|
||||
|
||||
if ((le32_to_cpu(di->i_flags) & OCFS2_ORPHANED_FL) &&
|
||||
le16_to_cpu(di->i_orphaned_slot) >= OCFS2_SB(sb)->max_slots) {
|
||||
rc = ocfs2_error(sb, "Invalid dinode %llu: orphaned slot %u\n",
|
||||
|
|
|
|||
|
|
@ -593,6 +593,18 @@ static inline int ocfs2_supports_discontig_bg(struct ocfs2_super *osb)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A suballocator block group bitmap starts right after the group
|
||||
* descriptor header, so a suballoc bit can never exceed this number
|
||||
* of bits. Derive it from ocfs2_group_bitmap_size() which also caps
|
||||
* it at OCFS2_MAX_BG_BITMAP_SIZE when discontig_bg is enabled.
|
||||
*/
|
||||
static inline u32 ocfs2_suballoc_bits_per_block(struct super_block *sb)
|
||||
{
|
||||
return ocfs2_group_bitmap_size(sb, 1,
|
||||
OCFS2_SB(sb)->s_feature_incompat) * 8;
|
||||
}
|
||||
|
||||
static inline unsigned int ocfs2_link_max(struct ocfs2_super *osb)
|
||||
{
|
||||
if (ocfs2_supports_indexed_dirs(osb))
|
||||
|
|
|
|||
|
|
@ -3040,10 +3040,22 @@ static int _ocfs2_free_suballoc_bits(handle_t *handle,
|
|||
/* The alloc_bh comes from ocfs2_free_dinode() or
|
||||
* ocfs2_free_clusters(). The callers have all locked the
|
||||
* allocator and gotten alloc_bh from the lock call. This
|
||||
* validates the dinode buffer. Any corruption that has happened
|
||||
* is a code bug. */
|
||||
* validates the dinode buffer. */
|
||||
BUG_ON(!OCFS2_IS_VALID_DINODE(fe));
|
||||
BUG_ON((count + start_bit) > ocfs2_bits_per_group(cl));
|
||||
|
||||
/*
|
||||
* ocfs2_bits_per_group() is derived from cl_cpg and cl_bpc of the
|
||||
* allocator dinode, which are not validated against the volume
|
||||
* geometry. A corrupted image can carry a suballoc bit beyond it,
|
||||
* so error out instead of crashing.
|
||||
*/
|
||||
if ((count + start_bit) > ocfs2_bits_per_group(cl)) {
|
||||
return ocfs2_error(alloc_inode->i_sb,
|
||||
"Allocator #%llu: freeing bits %u+%u exceeds bits per group %u\n",
|
||||
(unsigned long long)le64_to_cpu(fe->i_blkno),
|
||||
count, start_bit,
|
||||
ocfs2_bits_per_group(cl));
|
||||
}
|
||||
|
||||
trace_ocfs2_free_suballoc_bits(
|
||||
(unsigned long long)OCFS2_I(alloc_inode)->ip_blkno,
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user