CVE-2021-47069

Source
https://nvd.nist.gov/vuln/detail/CVE-2021-47069
Import Source
https://storage.googleapis.com/cve-osv-conversion/osv-output/CVE-2021-47069.json
JSON Data
https://api.osv.dev/v1/vulns/CVE-2021-47069
Related
Published
2024-03-01T22:15:46Z
Modified
2024-09-18T01:00:20Z
Summary
[none]
Details

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

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

domqtimedreceive calls wqsleep with a stack local address. The sender (domqtimedsend) uses this address to later call pipelinedsend.

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

RIP: 0010:wakeqaddsafe+0x13/0x60 Call Trace: _x64sysmqtimedsend+0x2a9/0x490 dosyscall64+0x80/0x680 entrySYSCALL64after_hwframe+0x44/0xa9 RIP: 0033:0x7f5928e40343

The race occurs as:

  1. domqtimedreceive 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->ewaitq[RECV].list in wqadd, and domqtimedsend receives it via wqgetfirstwaiter(info, RECV) to call _pipelinedop.

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

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

  5. domqtimedreceive returns, and ewq_addr is no longer guaranteed to be a struct ext_wait_queue * since it was on domqtimedreceive'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. domqtimedsend::_pipelinedop() still believes ewq_addr is a struct ext_wait_queue *, and uses it to find a taskstruct to pass to the wakeqaddsafe call. In the lucky case where nothing has overwritten ewq_addr yet, ewq_addr->task is the right taskstruct. In the unlucky case, _pipelinedop::wakeqaddsafe gets handed a bogus address as the receiver's task_struct causing the crash.

domqtimedsend::_pipelinedop() should not dereference this after setting STATEREADY, as the receiver counterpart is now free to return. Change _pipelinedop to call wakeqaddsafe on the receiver's taskstruct returned by gettask_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::expungeall and ipc/sem.c::wakeupsemqueue_prepare. Fix those in the same way.

References

Affected packages

Debian:11 / linux

Package

Name
linux
Purl
pkg:deb/debian/linux?arch=source

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
5.10.40-1

Ecosystem specific

{
    "urgency": "not yet assigned"
}

Debian:12 / linux

Package

Name
linux
Purl
pkg:deb/debian/linux?arch=source

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
5.10.40-1

Ecosystem specific

{
    "urgency": "not yet assigned"
}

Debian:13 / linux

Package

Name
linux
Purl
pkg:deb/debian/linux?arch=source

Affected ranges

Type
ECOSYSTEM
Events
Introduced
0Unknown introduced version / All previous versions are affected
Fixed
5.10.40-1

Ecosystem specific

{
    "urgency": "not yet assigned"
}