CVE Vulnerabilities

CVE-2021-47072

Published: Mar 01, 2024 | Modified: Mar 01, 2024
CVSS 3.x
N/A
Source:
NVD
CVSS 2.x
RedHat/V2
RedHat/V3
Ubuntu

In the Linux kernel, the following vulnerability has been resolved:

btrfs: fix removed dentries still existing after log is synced

When we move one inode from one directory to another and both the inode and its previous parent directory were logged before, we are not supposed to have the dentry for the old parent if we have a power failure after the log is synced. Only the new dentry is supposed to exist.

Generally this works correctly, however there is a scenario where this is not currently working, because the old parent of the file/directory that was moved is not authoritative for a range that includes the dir index and dir item keys of the old dentry. This case is better explained with the following example and reproducer:

The test requires a very specific layout of keys and items in the

fs/subvolume btree to trigger the bug. So we want to make sure that

on whatever platform we are, we have the same leaf/node size.

Currently in btrfs the node/leaf size can not be smaller than the page

size (but it can be greater than the page size). So use the largest

supported node/leaf size (64K).

$ mkfs.btrfs -f -n 65536 /dev/sdc $ mount /dev/sdc /mnt

testdir is inode 257.

$ mkdir /mnt/testdir $ chmod 755 /mnt/testdir

Create several empty files to have the directory testdir with its

items spread over several leaves (7 in this case).

$ for ((i = 1; i <= 1200; i++)); do echo -n > /mnt/testdir/file$i done

Create our test directory dira, inode number 1458, which gets all

its items in leaf 7.

The BTRFS_DIR_ITEM_KEY item for inode 257 (testdir) that points to

the entry named dira is in leaf 2, while the BTRFS_DIR_INDEX_KEY

item that points to that entry is in leaf 3.

For this particular filesystem node size (64K), file count and file

names, we endup with the directory entry items from inode 257 in

leaves 2 and 3, as previously mentioned - what matters for triggering

the bug exercised by this test case is that those items are not placed

in leaf 1, they must be placed in a leaf different from the one

containing the inode item for inode 257.

The corresponding BTRFS_DIR_ITEM_KEY and BTRFS_DIR_INDEX_KEY items for

the parent inode (257) are the following:

item 460 key (257 DIR_ITEM 3724298081) itemoff 48344 itemsize 34

location key (1458 INODE_ITEM 0) type DIR

transid 6 data_len 0 name_len 4

name: dira

and:

item 771 key (257 DIR_INDEX 1202) itemoff 36673 itemsize 34

location key (1458 INODE_ITEM 0) type DIR

transid 6 data_len 0 name_len 4

name: dira

$ mkdir /mnt/testdir/dira

Make sure everything done so far is durably persisted.

$ sync

Now do a change to inode 257 (testdir) that does not result in

COWing leaves 2 and 3 - the leaves that contain the directory items

pointing to inode 1458 (directory dira).

Changing permissions, the owner/group, updating or adding a xattr,

etc, will not change (COW) leaves 2 and 3. So for the sake of

simplicity change the permissions of inode 257, which results in

updating its inode item and therefore change (COW) only leaf 1.

$ chmod 700 /mnt/testdir

Now fsync directory inode 257.

Since only the first leaf was changed/COWed, we log the inode item of

inode 257 and only the dentries found in the first leaf, all have a

key type of BTRFS_DIR_ITEM_KEY, and no keys of type

BTRFS_DIR_INDEX_KEY, because they sort after the former type and none

exist in the first leaf.

We also log 3 items that represent ranges for dir items and dir

indexes for which the log is authoritative:

1) a key of type BTRFS_DIR_LOG_ITEM_KEY, which indicates the log is

authoritative for all BTRFS_DIR_ITEM_KEY keys that have an offset

in the range [0, 2285968570] (the offset here is th

—truncated—

References