]> git.ipfire.org Git - thirdparty/xfsprogs-dev.git/commit - repair/rmap.c
xfs: track cow/shared record domains explicitly in xfs_refcount_irec
authorDarrick J. Wong <djwong@kernel.org>
Fri, 18 Nov 2022 11:23:54 +0000 (12:23 +0100)
committerCarlos Maiolino <cem@kernel.org>
Mon, 21 Nov 2022 14:26:48 +0000 (15:26 +0100)
commit6b2f464dd02c1eaa380f3862263d4b08fe9fc2ba
treef159a187a0e610ae9ac329f8ae2eb1a8447f6391
parentbec88ec727bc87cdbd1c9133a3c16e0bb5d5ad30
xfs: track cow/shared record domains explicitly in xfs_refcount_irec

Source kernel commit: 9a50ee4f8db6e4dd0d8d757b7adaf0591776860a

Just prior to committing the reflink code into upstream, the xfs
maintainer at the time requested that I find a way to shard the refcount
records into two domains -- one for records tracking shared extents, and
a second for tracking CoW staging extents.  The idea here was to
minimize mount time CoW reclamation by pushing all the CoW records to
the right edge of the keyspace, and it was accomplished by setting the
upper bit in rc_startblock.  We don't allow AGs to have more than 2^31
blocks, so the bit was free.

Unfortunately, this was a very late addition to the codebase, so most of
the refcount record processing code still treats rc_startblock as a u32
and pays no attention to whether or not the upper bit (the cow flag) is
set.  This is a weakness is theoretically exploitable, since we're not
fully validating the incoming metadata records.

Fuzzing demonstrates practical exploits of this weakness.  If the cow
flag of a node block key record is corrupted, a lookup operation can go
to the wrong record block and start returning records from the wrong
cow/shared domain.  This causes the math to go all wrong (since cow
domain is still implicit in the upper bit of rc_startblock) and we can
crash the kernel by tricking xfs into jumping into a nonexistent AG and
tripping over xfs_perag_get(mp, <nonexistent AG>) returning NULL.

To fix this, start tracking the domain as an explicit part of struct
xfs_refcount_irec, adjust all refcount functions to check the domain
of a returned record, and alter the function definitions to accept them
where necessary.

Found by fuzzing keys[2].cowflag = add in xfs/464.

Signed-off-by: Darrick J. Wong <djwong@kernel.org>
Reviewed-by: Dave Chinner <dchinner@redhat.com>
Signed-off-by: Carlos Maiolino <cem@kernel.org>
libxfs/xfs_refcount.c
libxfs/xfs_refcount.h
libxfs/xfs_refcount_btree.c
libxfs/xfs_types.h
repair/rmap.c