mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-03 17:04:50 -04:00
Merge tag 'fix-rtmount-overflows-6.8_2023-12-06' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux into xfs-6.8-mergeA
xfs: fix realtime geometry integer overflows While reading through the realtime geometry support code in xfsprogs, I noticed a discrepancy between the sb_rextslog computation used when writing out the superblock during mkfs and the validation code used in xfs_repair. This discrepancy would lead to system failure for a runt rt volume having more than 1 rt block but zero rt extents in length. Most people aren't going to configure a 1M extent size for their 360k rt floppy disk volume, but I did! In the process of studying that code, it occurred to me that there is a second bug in the computation -- the use of highbit32 for a 64-bit value means that the upper 32 bits are not considered in the search for a high bit. This causes the creation of a realtime summary file that is the wrong length. If rextents is a multiple of U32_MAX then this will appear to work fine because highbit32 returns -1 for an input of 0; but for all other cases the rt summary is undersized, leading to failures. Fix the first problem by standardizing the computation with a helper in libxfs; and the second problem by correcting the computation. This will cause any existing rt volumes larger than 2^32 blocks to fail validation but they probably were already crashing anyway. This has been lightly tested with fstests. Enjoy! Signed-off-by: Darrick J. Wong <djwong@kernel.org> Signed-off-by: Chandan Babu R <chandanbabu@kernel.org> * tag 'fix-rtmount-overflows-6.8_2023-12-06' of https://git.kernel.org/pub/scm/linux/kernel/git/djwong/xfs-linux: xfs: don't allow overly small or large realtime volumes xfs: fix 32-bit truncation in xfs_compute_rextslog xfs: make rextslog computation consistent with mkfs
This commit is contained in:
@@ -1130,6 +1130,20 @@ xfs_rtbitmap_blockcount(
|
||||
return howmany_64(rtextents, NBBY * mp->m_sb.sb_blocksize);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the maximum level number of the realtime summary file, as defined by
|
||||
* mkfs. The historic use of highbit32 on a 64-bit quantity prohibited correct
|
||||
* use of rt volumes with more than 2^32 extents.
|
||||
*/
|
||||
uint8_t
|
||||
xfs_compute_rextslog(
|
||||
xfs_rtbxlen_t rtextents)
|
||||
{
|
||||
if (!rtextents)
|
||||
return 0;
|
||||
return xfs_highbit64(rtextents);
|
||||
}
|
||||
|
||||
/*
|
||||
* Compute the number of rtbitmap words needed to populate every block of a
|
||||
* bitmap that is large enough to track the given number of rt extents.
|
||||
|
||||
@@ -351,6 +351,20 @@ xfs_rtfree_extent(
|
||||
int xfs_rtfree_blocks(struct xfs_trans *tp, xfs_fsblock_t rtbno,
|
||||
xfs_filblks_t rtlen);
|
||||
|
||||
uint8_t xfs_compute_rextslog(xfs_rtbxlen_t rtextents);
|
||||
|
||||
/* Do we support an rt volume having this number of rtextents? */
|
||||
static inline bool
|
||||
xfs_validate_rtextents(
|
||||
xfs_rtbxlen_t rtextents)
|
||||
{
|
||||
/* No runt rt volumes */
|
||||
if (rtextents == 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
xfs_filblks_t xfs_rtbitmap_blockcount(struct xfs_mount *mp, xfs_rtbxlen_t
|
||||
rtextents);
|
||||
unsigned long long xfs_rtbitmap_wordcount(struct xfs_mount *mp,
|
||||
@@ -369,6 +383,8 @@ unsigned long long xfs_rtsummary_wordcount(struct xfs_mount *mp,
|
||||
# define xfs_rtsummary_read_buf(a,b) (-ENOSYS)
|
||||
# define xfs_rtbuf_cache_relse(a) (0)
|
||||
# define xfs_rtalloc_extent_is_free(m,t,s,l,i) (-ENOSYS)
|
||||
# define xfs_compute_rextslog(rtx) (0)
|
||||
# define xfs_validate_rtextents(rtx) (false)
|
||||
static inline xfs_filblks_t
|
||||
xfs_rtbitmap_blockcount(struct xfs_mount *mp, xfs_rtbxlen_t rtextents)
|
||||
{
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "xfs_da_format.h"
|
||||
#include "xfs_health.h"
|
||||
#include "xfs_ag.h"
|
||||
#include "xfs_rtbitmap.h"
|
||||
|
||||
/*
|
||||
* Physical superblock buffer manipulations. Shared with libxfs in userspace.
|
||||
@@ -508,8 +509,9 @@ xfs_validate_sb_common(
|
||||
rbmblocks = howmany_64(sbp->sb_rextents,
|
||||
NBBY * sbp->sb_blocksize);
|
||||
|
||||
if (sbp->sb_rextents != rexts ||
|
||||
sbp->sb_rextslog != xfs_highbit32(sbp->sb_rextents) ||
|
||||
if (!xfs_validate_rtextents(rexts) ||
|
||||
sbp->sb_rextents != rexts ||
|
||||
sbp->sb_rextslog != xfs_compute_rextslog(rexts) ||
|
||||
sbp->sb_rbmblocks != rbmblocks) {
|
||||
xfs_notice(mp,
|
||||
"realtime geometry sanity check failed");
|
||||
|
||||
@@ -963,8 +963,10 @@ xfs_growfs_rt(
|
||||
*/
|
||||
nrextents = nrblocks;
|
||||
do_div(nrextents, in->extsize);
|
||||
if (!xfs_validate_rtextents(nrextents))
|
||||
return -EINVAL;
|
||||
nrbmblocks = xfs_rtbitmap_blockcount(mp, nrextents);
|
||||
nrextslog = xfs_highbit32(nrextents);
|
||||
nrextslog = xfs_compute_rextslog(nrextents);
|
||||
nrsumlevels = nrextslog + 1;
|
||||
nrsumblocks = xfs_rtsummary_blockcount(mp, nrsumlevels, nrbmblocks);
|
||||
nrsumsize = XFS_FSB_TO_B(mp, nrsumblocks);
|
||||
@@ -1031,7 +1033,7 @@ xfs_growfs_rt(
|
||||
nsbp->sb_rblocks = min(nrblocks, nrblocks_step);
|
||||
nsbp->sb_rextents = xfs_rtb_to_rtx(nmp, nsbp->sb_rblocks);
|
||||
ASSERT(nsbp->sb_rextents != 0);
|
||||
nsbp->sb_rextslog = xfs_highbit32(nsbp->sb_rextents);
|
||||
nsbp->sb_rextslog = xfs_compute_rextslog(nsbp->sb_rextents);
|
||||
nrsumlevels = nmp->m_rsumlevels = nsbp->sb_rextslog + 1;
|
||||
nrsumblocks = xfs_rtsummary_blockcount(mp, nrsumlevels,
|
||||
nsbp->sb_rbmblocks);
|
||||
|
||||
Reference in New Issue
Block a user