xfs: prevent spoofing of rtbitmap blocks when recovering buffers

While reviewing the buffer item recovery code, the thought occurred to
me: in V5 filesystems we use log sequence number (LSN) tracking to avoid
replaying older metadata updates against newer log items. However, we
use the magic number of the ondisk buffer to find the LSN of the ondisk
metadata, which means that if an attacker can control the layout of the
realtime device precisely enough that the start of an rt bitmap block
matches the magic and UUID of some other kind of block, they can control
the purported LSN of that spoofed block and thereby break log replay.

Since realtime bitmap and summary blocks don't have headers at all, we
have no way to tell if a block really should be replayed. The best we
can do is replay unconditionally and hope for the best.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Reviewed-by: Carlos Maiolino <cmaiolino@redhat.com>

Changed files
+12 -2
fs
+12 -2
fs/xfs/xfs_buf_item_recover.c
··· 698 698 static xfs_lsn_t 699 699 xlog_recover_get_buf_lsn( 700 700 struct xfs_mount *mp, 701 - struct xfs_buf *bp) 701 + struct xfs_buf *bp, 702 + struct xfs_buf_log_format *buf_f) 702 703 { 703 704 uint32_t magic32; 704 705 uint16_t magic16; ··· 707 706 void *blk = bp->b_addr; 708 707 uuid_t *uuid; 709 708 xfs_lsn_t lsn = -1; 709 + uint16_t blft; 710 710 711 711 /* v4 filesystems always recover immediately */ 712 712 if (!xfs_sb_version_hascrc(&mp->m_sb)) 713 + goto recover_immediately; 714 + 715 + /* 716 + * realtime bitmap and summary file blocks do not have magic numbers or 717 + * UUIDs, so we must recover them immediately. 718 + */ 719 + blft = xfs_blft_from_flags(buf_f); 720 + if (blft == XFS_BLFT_RTBITMAP_BUF || blft == XFS_BLFT_RTSUMMARY_BUF) 713 721 goto recover_immediately; 714 722 715 723 magic32 = be32_to_cpu(*(__be32 *)blk); ··· 930 920 * the verifier will be reset to match whatever recover turns that 931 921 * buffer into. 932 922 */ 933 - lsn = xlog_recover_get_buf_lsn(mp, bp); 923 + lsn = xlog_recover_get_buf_lsn(mp, bp, buf_f); 934 924 if (lsn && lsn != -1 && XFS_LSN_CMP(lsn, current_lsn) >= 0) { 935 925 trace_xfs_log_recover_buf_skip(log, buf_f); 936 926 xlog_recover_validate_buf_type(mp, bp, buf_f, NULLCOMMITLSN);