• Darrick J. Wong's avatar
    xfs: track cow/shared record domains explicitly in xfs_refcount_irec · 9a50ee4f
    Darrick J. Wong authored
    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: default avatarDarrick J. Wong <djwong@kernel.org>
    Reviewed-by: default avatarDave Chinner <dchinner@redhat.com>
    9a50ee4f
xfs_refcount_btree.c 13.1 KB