Commit 3e78b9a4 authored by Brian Foster's avatar Brian Foster Committed by Darrick J. Wong

xfs: shutdown if block allocation overruns tx reservation

The ->t_blk_res_used field tracks how many blocks have been used in
the current transaction. This should never exceed the block
reservation (->t_blk_res) for a particular transaction. We currently
assert this condition in the transaction block accounting code, but
otherwise take no additional action should this situation occur.

The overrun generally has no effect if space ends up being available
and the associated transaction commits. If the transaction is
duplicated, however, the current block usage is used to determine
the remaining block reservation to be transferred to the new
transaction. If usage exceeds reservation, this calculation
underflows and creates a transaction with an invalid and excessive
reservation. When the second transaction commits, the release of
unused blocks corrupts the in-core free space counters. With lazy
superblock accounting enabled, this inconsistency eventually
trickles to the on-disk superblock and corrupts the filesystem.

Replace the transaction block usage accounting assert with an
explicit overrun check. If the transaction overruns the reservation,
shutdown the filesystem immediately to prevent corruption. Add a new
assert to xfs_trans_dup() to catch any callers that might induce
this invalid state in the future.
Signed-off-by: default avatarBrian Foster <bfoster@redhat.com>
Reviewed-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
Signed-off-by: default avatarDarrick J. Wong <darrick.wong@oracle.com>
parent 57e80956
...@@ -119,8 +119,11 @@ xfs_trans_dup( ...@@ -119,8 +119,11 @@ xfs_trans_dup(
/* We gave our writer reference to the new transaction */ /* We gave our writer reference to the new transaction */
tp->t_flags |= XFS_TRANS_NO_WRITECOUNT; tp->t_flags |= XFS_TRANS_NO_WRITECOUNT;
ntp->t_ticket = xfs_log_ticket_get(tp->t_ticket); ntp->t_ticket = xfs_log_ticket_get(tp->t_ticket);
ASSERT(tp->t_blk_res >= tp->t_blk_res_used);
ntp->t_blk_res = tp->t_blk_res - tp->t_blk_res_used; ntp->t_blk_res = tp->t_blk_res - tp->t_blk_res_used;
tp->t_blk_res = tp->t_blk_res_used; tp->t_blk_res = tp->t_blk_res_used;
ntp->t_rtx_res = tp->t_rtx_res - tp->t_rtx_res_used; ntp->t_rtx_res = tp->t_rtx_res - tp->t_rtx_res_used;
tp->t_rtx_res = tp->t_rtx_res_used; tp->t_rtx_res = tp->t_rtx_res_used;
ntp->t_pflags = tp->t_pflags; ntp->t_pflags = tp->t_pflags;
...@@ -344,13 +347,14 @@ xfs_trans_mod_sb( ...@@ -344,13 +347,14 @@ xfs_trans_mod_sb(
break; break;
case XFS_TRANS_SB_FDBLOCKS: case XFS_TRANS_SB_FDBLOCKS:
/* /*
* Track the number of blocks allocated in the * Track the number of blocks allocated in the transaction.
* transaction. Make sure it does not exceed the * Make sure it does not exceed the number reserved. If so,
* number reserved. * shutdown as this can lead to accounting inconsistency.
*/ */
if (delta < 0) { if (delta < 0) {
tp->t_blk_res_used += (uint)-delta; tp->t_blk_res_used += (uint)-delta;
ASSERT(tp->t_blk_res_used <= tp->t_blk_res); if (tp->t_blk_res_used > tp->t_blk_res)
xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE);
} }
tp->t_fdblocks_delta += delta; tp->t_fdblocks_delta += delta;
if (xfs_sb_version_haslazysbcount(&mp->m_sb)) if (xfs_sb_version_haslazysbcount(&mp->m_sb))
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment