Race Condition Affecting perf package, versions <0:4.18.0-477.70.1.el8_8


Severity

Recommended
high

Based on Red Hat Enterprise Linux security rating

    Threat Intelligence

    EPSS
    0.05% (17th percentile)

Do your applications use this vulnerable package?

In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.

Test your applications
  • Snyk ID SNYK-RHEL8-PERF-7893608
  • published 4 Sep 2024
  • disclosed 1 Mar 2024

How to fix?

Upgrade RHEL:8 perf to version 0:4.18.0-477.70.1.el8_8 or higher.
This issue was patched in RHSA-2024:6206.

NVD Description

Note: Versions mentioned in the description apply only to the upstream perf package and not the perf package as distributed by RHEL. See How to fix? for RHEL:8 relevant fixed versions and status.

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

ipc/mqueue, msg, sem: avoid relying on a stack reference past its expiry

do_mq_timedreceive calls wq_sleep with a stack local address. The sender (do_mq_timedsend) uses this address to later call pipelined_send.

This leads to a very hard to trigger race where a do_mq_timedreceive call might return and leave do_mq_timedsend to rely on an invalid address, causing the following crash:

RIP: 0010:wake_q_add_safe+0x13/0x60 Call Trace: __x64_sys_mq_timedsend+0x2a9/0x490 do_syscall_64+0x80/0x680 entry_SYSCALL_64_after_hwframe+0x44/0xa9 RIP: 0033:0x7f5928e40343

The race occurs as:

  1. do_mq_timedreceive calls wq_sleep with the address of struct ext_wait_queue on function stack (aliased as ewq_addr here) - it holds a valid struct ext_wait_queue * as long as the stack has not been overwritten.

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

  3. Sender calls __pipelined_op::smp_store_release(&this->state, STATE_READY). Here is where the race window begins. (this is ewq_addr.)

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

  5. do_mq_timedreceive returns, and ewq_addr is no longer guaranteed to be a struct ext_wait_queue * since it was on do_mq_timedreceive's stack. (Although the address may not get overwritten until another function happens to touch it, which means it can persist around for an indefinite time.)

  6. do_mq_timedsend::__pipelined_op() still believes ewq_addr is a struct ext_wait_queue *, and uses it to find a task_struct to pass to the wake_q_add_safe call. In the lucky case where nothing has overwritten ewq_addr yet, ewq_addr-&gt;task is the right task_struct. In the unlucky case, __pipelined_op::wake_q_add_safe gets handed a bogus address as the receiver's task_struct causing the crash.

do_mq_timedsend::__pipelined_op() should not dereference this after setting STATE_READY, as the receiver counterpart is now free to return. Change __pipelined_op to call wake_q_add_safe on the receiver's task_struct returned by get_task_struct, instead of dereferencing this which sits on the receiver's stack.

As Manfred pointed out, the race potentially also exists in ipc/msg.c::expunge_all and ipc/sem.c::wake_up_sem_queue_prepare. Fix those in the same way.

CVSS Scores

version 3.1
Expand this section

Red Hat

5.5 medium
  • Attack Vector (AV)
    Local
  • Attack Complexity (AC)
    Low
  • Privileges Required (PR)
    Low
  • User Interaction (UI)
    None
  • Scope (S)
    Unchanged
  • Confidentiality (C)
    None
  • Integrity (I)
    None
  • Availability (A)
    High
Expand this section

SUSE

4.7 medium