---

In early 2021, a subtle but severe bug was identified and patched in the Linux kernel’s Btrfs filesystem driver, under the identifier CVE-2021-46958. This post breaks down the vulnerability, how it could be exploited, and the core patch, helping users and sysadmins understand its risk and mitigation.

What Is the Bug? (A Simple Overview)

In short, a use-after-free vulnerability existed within the Btrfs filesystem, relating to how the kernel handled two concurrent operations:

- One process committing a transaction (Task A, like a metadata-changing operation such as deleting a file)

Another process performing fsync (Task B, syncing file data to disk)

If these overlapped in just the right way — specifically, if an error caused transaction abort (like an I/O error or running out of disk space), while another process simultaneously tried to write logs to disk — the memory for the log root tree could be freed, then accessed after free. This could crash the kernel (DoS) and, in theory, allow more serious exploits.

Let’s break down the chain of events, using simplified terms

1. Two concurrent tasks (processes/threads) are both involved with handling the same running Btrfs transaction:

Task B: Runs fsync() to flush file changes to disk.

2. fsync grabs a *pointer* to Btrfs’s internal log_root_tree — this is a data structure tracking changes to flush.

3. Task A triggers a transaction abort due to some error (disk, power loss, etc.), which causes log_root_tree to get deallocated (freed).

4. Task B wakes up and tries to access that freed log_root_tree pointer, causing a use-after-free bug, which leads to kernel memory corruption or an immediate crash with a trace similar to:

general protection fault, probably for non-canonical address x6b6b6b6b6b6b6b68 ...

RIP: 001:__mutex_lock+x139/xa40

...

btrfs_sync_log+x7c1/xf20 [btrfs]

Here’s a condensed look at the bug in the context of the 5.12.-rc5 kernel

// btrfs_sync_log() in fs/btrfs/tree-log.c
struct btrfs_root *log_root_tree = fs_info->log_root_tree;

// ... other stuff: fsync logic

// Concurrently, error in commit triggers:
kfree(fs_info->log_root_tree); // or equivalent free

// Task B (fsync) now uses a freed pointer: use-after-free!
write_all_supers(log_root_tree); // BAD if log_root_tree was just kfree()'d

Denial Of Service

The practical impact for most users would be a kernel crash (denial of service), especially on servers or virtual machines using Btrfs under stress, or where hardware or disk errors are more likely to trigger the error path.

Privilege Escalation

While not confirmed, use-after-free bugs can, in theory, be exploited for privilege escalation if attackers can control timing *and* some part of the kernel memory is over-written with attacker-controlled data between free and use. However, in this specific case, reliably exploiting beyond DoS is difficult and no public POC for escalation is known.


### How to Reproduce / Test (Simplified Example)

Researchers and sysadmins can simulate this race with stress tools and by inducing artificial I/O errors. Here’s a minimal test setup:

Create a Btrfs formatted loop device or VM image.

2. Use fsstress or custom workload to bombard the fs with fsync & commits.

Unplugging the storage or using dmsetup error to simulate block device errors during heavy writes.

If vulnerable, you’ll observe a crash trace matching the sample above.

The Patch — How Was It Fixed?

Original patch (commit) added better synchronization to ensure the log root tree can’t be freed while another thread might be using it.

Key changes

+       /* Hold an extra ref to log_root_tree during fsync */
+       struct btrfs_root *log_root_tree;
+       log_root_tree = btrfs_grab_root(fs_info->log_root_tree);
+       if (!log_root_tree)
+           return -ENOMEM;
+       ...
+       write_all_supers(log_root_tree);
+       btrfs_put_root(log_root_tree);

With this, fsync and similar operations now safely reference-count the log root tree, so even if another task aborts the transaction, memory won’t be freed until all references are dropped.

All Linux versions using Btrfs with kernels prior to the patch, up to 5.12-rc5

- Distributions with Btrfs as the root filesystem or for containers/virtual disks
- Workloads that do a lot of fsync/transaction activity, especially under bad storage conditions

Get a version at least as new as 5.12-rc6 (or with backported patches)

- All major distros have applied this patch to their LTS/backport kernels

Resources & References

- Upstream Patch Commit
- RedHat BZ #1940061
- Kernel mailing list discussion
- Btrfs upstream

Summary

CVE-2021-46958 demonstrates how complex kernel subsystems like Btrfs can have subtle yet critical races between seemingly unrelated operations like fsync and transaction commits. If you run Btrfs, make sure your Linux kernel is up to date — a simple software update is the best defense.

If you’re a kernel developer or advanced user, always check for obscure race conditions in shared structures, and consider memory reference counting (like the patch does) as a robust solution.

Timeline

Published on: 02/27/2024 19:04:06 UTC
Last modified on: 12/11/2024 14:43:21 UTC