CVE-2021-46938 - Double Free Vulnerability in Linux Kernel’s device-mapper (dm) Request Queue Management

In early 2021, the Linux kernel development community addressed a subtle but severe vulnerability in the device-mapper’s request-queue (dm rq) logic: CVE-2021-46938. This vulnerability occurs when handling request-based device-mapper (dm-mq) devices, and can trigger a double-free bug, leading to kernel oopses, potential local Denial of Service (DoS), and possibly privilege escalation if weaponized. Here’s what you need to know, how it happens, exploit details, and most critically: how to stay safe.

The Vulnerability: How Double Free Happens

Summary:
When loading a device-mapper table involving a request-based (dm-mq) device, if the allocation and initialization of a blk_mq_tag_set fails, a future device removal could try to free the same object twice.

Why is Double Free Dangerous?
Most of the time: freeing memory twice leads to immediate kernel panic (DoS). Sometimes, especially if memory is reallocated and overwritten between the two frees, it can lead to arbitrary code execution, with attackers hijacking kernel execution flow.

The Accident Sequence

1. Device-mapper table loading happens, tries to allocate blk_mq_tag_set for the request-based device.

BUT... the pointer isn’t NULLed after free!

5. Later, on device removal (e.g. by user or udev), the same cleanup routine is called again, sees the non-NULL pointer, and frees it again.

Typical dmesg output (highlights added)

device-mapper: core: Cannot initialize queue for request-based dm-mq mapped device
device-mapper: ioctl: unable to set up device queue for new table.
...
Oops: 0038 ilc:3 [#1] SMP
...
[<...>] kfree+x42/x330
[<...>] blk_mq_free_tag_set+x72/xb8
[<...>] dm_mq_cleanup_mapped_device+x38/x50 [dm_mod]
[<...>] free_dev+x52/xd [dm_mod]
[<...>] __dm_destroy+x150/x1d [dm_mod]
...
Kernel panic - not syncing: Fatal exception: panic_on_oops

Pseudocode: What Went Wrong

// In dm_mq_init_request_queue()
if (blk_mq_alloc_tag_set_failed) {
    blk_mq_free_tag_set(set); // free tag set
    // set is still pointing to the freed memory!
    return error;
}

// Later, during device removal:
if (md->tag_set) {
    blk_mq_free_tag_set(md->tag_set); // free again: double free!
}

Original References

- LKML Patch Discussion
- SUSE Bug Report
- kernel.org commit 473686baeff
- NVD CVE Entry

Unload the device (trigger cleanup) and cause the double free.

Result:

Reliable kernel panic (Denial of Service)

- Possible control if attacker can manipulate freed memory and then cause another allocation elsewhere in between the two frees. This is very hard, but not impossible in research or targeted settings.

The Patch: Safe Memory Management

Developers fixed the issue by setting the tag set pointer to NULL immediately after freeing.

Patch Code Snippet

// In dm_mq_init_request_queue()
if (allocation_failed) {
    blk_mq_free_tag_set(md->tag_set);
    md->tag_set = NULL; // <== FIX, prevent double free
    return error;
}

// In dm_mq_cleanup_mapped_device()
if (md->tag_set) {
    blk_mq_free_tag_set(md->tag_set);
    md->tag_set = NULL; // <== Clean up, stay safe if called twice
}

For system administrators:

- Patch hosts with kernel updates, especially servers running dm-multipath and vendors with custom kernel modules.

Final Thoughts

Double-free errors are a common class of memory safety vulnerability, and in kernel-space, they are always severe. CVE-2021-46938 is notable because it could be triggered inadvertently (by swapping device-mapper tables around in low-memory conditions), or abused by a local attacker, leading to a system crash or worse.

Linux kernel security is a community and vendor commitment—apply security updates as soon as they’re available, and stay vigilant for new dm/userland interactions that touch low-level subsystems.

References

- Original Patch Email
- NVD CVE-2021-46938
- Patch to mainline kernel

Timeline

Published on: 02/27/2024 19:04:05 UTC
Last modified on: 04/10/2024 19:20:55 UTC