CVE-2021-47069: Linux Kernel Vulnerability Resolved in ipc/mqueue, msg, sem - Avoiding Reliance on an Expired Stack Reference

The Linux kernel team has recently resolved a vulnerability in the ipc/mqueue, msg, and sem subsystems (commit link). This vulnerability, labeled as CVE-2021-47069, involved a rare race condition in which an invalid address would be relied upon during do_mq_timedsend and do_mq_timedreceive function calls, leading to a potential crash in the system.

In this post, we will dive into the details of this vulnerability, analyze the problematic code snippets, and discuss the solution implemented by the Linux kernel team.

The race condition occurs as follows

1. do_mq_timedreceive calls the wq_sleep function with the address of a stack local struct ext_wait_queue variable (ewq_addr). It's important to note that this address remains valid as long as the function's stack is not overwritten.

struct ext_wait_queue *ewq_addr;
wq_sleep(&ewq_addr, info);

2. The ewq_addr gets added to the info->e_wait_q[RECV].list in the wq_add function, and do_mq_timedsend receives it via wq_get_first_waiter(info, RECV) in order to call __pipelined_op.

wq_add(info, ewq_addr);

3. Sender calls __pipelined_op::smp_store_release(&this->state, STATE_READY). At this point, the race window starts. (this refers to ewq_addr)

ext_wait_queue *this;
smp_store_release(&this->state, STATE_READY);

4. If the receiver wakes up in do_mq_timedreceive::wq_sleep, it will see state == STATE_READY and exit the loop.

5. do_mq_timedreceive returns, and the ewq_addr is no longer guaranteed to be a valid struct ext_wait_queue * since it was on the function's stack. If the address is overwritten by another function, it can cause issues in the system.

6. do_mq_timedsend::__pipelined_op() still believes ewq_addr is a valid struct ext_wait_queue *, and uses it to find a task_struct to pass to wake_q_add_safe call. If nothing has overwritten the address, it works as expected. However, if the address is overwritten, the system crashes.

Proposed Solution

To fix this issue, the Linux kernel team recommended not to dereference this after setting STATE_READY in do_mq_timedsend::__pipelined_op(), as the receiver counterpart is now free to return. Instead, the function should call wake_q_add_safe on the receiver's task_struct returned by get_task_struct, avoiding the reliance on the potentially expired this pointer.

Additionally, as Manfred pointed out, the same race condition could potentially exist in ipc/msg.c::expunge_all and ipc/sem.c::wake_up_sem_queue_prepare. The Linux kernel team fixed these instances using the same approach.

1. Linux Kernel Commit - ipc/mqueue, msg, sem: avoid relying on a stack reference past its expiry
2. CVE-2021-47069 Details

In conclusion, the Linux kernel team has successfully addressed a rare race condition vulnerability (CVE-2021-47069) in the ipc/mqueue, msg, and sem subsystems by implementing a solution that avoids relying on an expired stack reference, effectively preventing system crashes that could occur due to this issue.

Timeline

Published on: 03/01/2024 22:15:46 UTC
Last modified on: 05/29/2024 05:01:35 UTC