Bug 2470013 (CVE-2026-89329) - CVE-2026-89329 device-mapper-multipath: Local Denial of Service via Blocking IPC Send Operations
Summary: CVE-2026-89329 device-mapper-multipath: Local Denial of Service via Blocking ...
Keywords:
Status: NEW
Alias: CVE-2026-89329
Product: Security Response
Classification: Other
Component: vulnerability
Version: unspecified
Hardware: All
OS: Linux
medium
medium
Target Milestone: ---
Assignee: Product Security
QA Contact:
URL:
Whiteboard:
Depends On: 2531974
Blocks:
TreeView+ depends on / blocked
 
Reported: 2026-05-11 20:39 UTC by OSIDB Bzimport
Modified: 2026-09-11 16:53 UTC (History)
18 users (show)

Fixed In Version:
Clone Of:
Environment:
Last Closed:
Embargoed:


Attachments (Terms of Use)

Description OSIDB Bzimport 2026-05-11 20:39:34 UTC
AI_ONLY_REPORT
package: device-mapper-multipath-0.9.9-18.el10
------
Summary: Local Denial of Service via Blocking IPC Send Operations:  
`multipathd` can block its IPC listener thread when replying on a blocking  
client socket to a local client that stops reading responses.
Requirements to exploit: Local access to the `multipathd` UNIX control  
socket, the ability to issue an allowed IPC command, and the ability to  
keep the connection open without reading replies until the daemon blocks in  
`send()`.
Component affected: `device-mapper-multipath-0.9.9-18.el10`, primarily  
`multipathd/uxlsnr.c` (`new_client()` and `client_state_machine()` in  
`CLT_SEND`), with socket reachability influenced by `libmpathutil/uxsock.c`
Version affected: `device-mapper-multipath-0.9.9-18.el10` when `multipathd`  
is running and its local IPC socket is reachable to the attacking user
Patch available: no released package fix established; proposed patch  
included below
Version fixed: unknown
Upstream coordination: Not notified.
CVSS: CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - 5.5 (MEDIUM)
AV:L - exploitation requires local access to the UNIX domain control  
socket.
AC:L - the attack is straightforward: send valid IPC commands and stop  
reading replies until the socket send buffer is exhausted.
PR:N - no prior privileges are required where the control socket is  
exposed to unprivileged local users.
UI:N - no user interaction is required.
S:U - the impact remains within the daemon's own security scope.
C:N - no confidentiality impact was established.
I:N - no integrity impact was established.
A:H - legitimate IPC operations can hang or time out once the listener  
thread blocks.
Impact: Moderate. This issue can let a local user deny availability of the  
`multipathd` IPC control path in deployments where the daemon's socket is  
reachable to that user. The available evidence does not show privilege  
escalation, code execution, or direct confidentiality or integrity impact,  
and the effect appears limited to the daemon's control interface rather  
than full system compromise. Under Red Hat's severity guidance, that makes  
Moderate more appropriate than Important or Critical, while still exceeding  
Low because the condition appears practically reachable in default-style  
local IPC exposure.
Embargo: no
Reason: the issue is a local-only denial of service with no  
demonstrated confidentiality, integrity, or code-execution impact, and  
exposure can be reduced through local access controls on the IPC interface.
Acknowledgement: Aisle Research
Vulnerability Details: Accepted client sockets are left in blocking mode,  
and the reply path uses blocking `send()` operations. A local client that  
sends a valid command and then stops reading can cause the listener thread  
to block once the kernel send buffer fills. Because the listener loop  
continues processing clients already in `CLT_SEND`, this can stall  
subsequent IPC servicing for other clients.
```c
static void new_client(int ux_sock)
{
...
fd = accept(ux_sock, &addr, &len);
...
c->fd = fd;          /* blocking accepted socket */
c->state = CLT_RECV;
}
```
```c
case CLT_SEND:
if (c->cmd_len == 0) {
size_t len = get_strbuf_len(&c->reply) + 1;
if (send(c->fd, &len, sizeof(len), MSG_NOSIGNAL) != sizeof(len))
c->error = -ECONNRESET;
c->cmd_len = len;
return STM_BREAK;
}
if (c->len < c->cmd_len) {
const char *buf = get_strbuf_str(&c->reply);
n = send(c->fd, buf + c->len, c->cmd_len - c->len, MSG_NOSIGNAL);
...
}
```
The available code also indicates that non-root clients are restricted to  
`list` commands, but those still produce replies and appear sufficient to  
exercise this condition where the socket is accessible:
```c
if (name[0] != '@' &&
chmod(name, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)  
== -1)
...
```
The precise time needed to trigger the blockage depends on runtime reply  
sizes and socket buffering, but the issue appears reachable by repeatedly  
issuing valid reply-generating commands on a connection that never consumes  
responses.
Steps to reproduce:
1. Start `multipathd` with its normal IPC listener enabled.
2. Connect to the local control socket from an unprivileged local account  
where access is permitted.
3. Send a valid command such as `list daemon` and do not read the reply.
4. Continue sending valid commands on the same connection until the  
server-side socket buffer is saturated.
5. From another terminal, issue a normal IPC command and observe that it  
hangs or times out while the listener is blocked in `send()`.
Minimal PoC client:
```python
#!/usr/bin/env python3
import socket, struct
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect("/run/multipathd.socket")   # adjust if needed
cmd = b"list daemon\x00"
frame = struct.pack("=Q", len(cmd)) + cmd
while True:
sock.sendall(frame)   # intentionally never read replies
```
Mitigation: Restrict access to the `multipathd` IPC socket to trusted local  
administrators only, or otherwise prevent untrusted local users from  
reaching the control interface. Environments that do not expose the  
listener to unprivileged users substantially reduce the practical attack  
surface.
Proposed Fix: Set accepted client sockets to non-blocking mode and avoid  
blocking in `CLT_SEND` unless `POLLOUT` is present. The following minimal  
patch addresses both points.
```diff
diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c
index XXXXXXX..YYYYYYY 100644
— a/multipathd/uxlsnr.c
+++ b/multipathd/uxlsnr.c
@@ -123,6 +123,13 @@ static void new_client(int ux_sock)
fd = accept(ux_sock, &addr, &len);
if (fd == -1)
return;
+	{
+		int flags = fcntl(fd, F_GETFL, 0);
+		if (flags != -1)
+			(void)fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+	}
+
c = (struct client *)calloc(1, sizeof(*c));
if (!c) {
close(fd);
@@ -551,6 +558,9 @@ static int client_state_machine(struct client *c,  
struct vectors *vecs,
case CLT_SEND:
+		if (!(revents & POLLOUT))
+			return STM_BREAK;
+
if (get_strbuf_len(&c->reply) == 0)
default_reply(c, c->error);
@@ -558,13 +568,21 @@ static int client_state_machine(struct client *c,  
struct vectors *vecs,
if (send(c>fd, &len, sizeof(len), MSG_NOSIGNAL)
-			    != sizeof(len))
c>error = -ECONNRESET;
+			n = send(c->fd, &len, sizeof(len),
+				 MSG_NOSIGNAL | MSG_DONTWAIT);
+			if (n == -1) {
+				if (!(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
+					c->error = -ECONNRESET;
+				return STM_BREAK;
+			}
+			if (n != (ssize_t)sizeof(len))
+				return STM_BREAK;
c->cmd_len = len;
return STM_BREAK;
}
if (c->len < c->cmd_len) {
const char *buf = get_strbuf_str(&c->reply);
n = send(c>fd, buf + c->len, c->cmd_len - c->len, MSG_NOSIGNAL);
+			n = send(c->fd, buf + c->len, c->cmd_len - c->len,
+				 MSG_NOSIGNAL | MSG_DONTWAIT);
if (n == -1) {
-				if (!(errno == EAGAIN || errno == EINTR))
+				if (!(errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR))
c->error = -ECONNRESET;
} else
c->len += n;
```
------
This report was generated using AI technology. Always review AI-generated  
content prior to use


Note You need to log in before you can comment on or make changes to this bug.