Bug 2462869 (CVE-2026-128987)

Summary: CVE-2026-128987 dbus: Infinite CPU-spin loop in libdbus socket transport when EPIPE is received during write operations
Product: [Other] Security Response Reporter: OSIDB Bzimport <bzimport>
Component: vulnerabilityAssignee: Product Security DevOps Team <prodsec-dev>
Status: NEW --- QA Contact:
Severity: medium Docs Contact:
Priority: medium    
Version: unspecifiedCC: rhel-process-autobot, security-response-team, watson-tool-maintainers
Target Milestone: ---Keywords: Security
Target Release: ---   
Hardware: All   
OS: Linux   
Whiteboard:
Fixed In Version: Doc Type: ---
Doc Text:
A flaw was found in dbus. A local attacker can exploit this vulnerability by connecting to an affected service, requesting a response, and stopping socket reads while keeping the connection open. Because the socket transport handles broken pipe write errors as retryable, the application enters a continuous retry loop. This can lead to excessive CPU consumption, resulting in a denial of service (DoS) for the target process.
Story Points: ---
Clone Of: Environment:
Last Closed: Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description OSIDB Bzimport 2026-04-26 20:18:26 UTC
AI_ONLY_REPORT
package: dbus-1.14.10-5.el10
------
Summary: Denial of Service via Infinite Loop on EPIPE during Write: a local  
peer can trigger repeated `EPIPE` write failures in the libdbus socket  
transport and keep the target process in a sustained high-CPU retry loop.
Requirements to exploit: An attacker needs local IPC/socket access to a  
process using the packaged libdbus socket transport, permission to connect  
as a local client, and a reachable code path that queues outbound data back  
to that client. The reported behavior is relevant to watch-driven  
event-loop integrations; deployments that do not expose such a local  
endpoint or do not use this write-watch path are not reached by the  
described attack.
Component affected: `dbus-1.14.10-5.el10`, libdbus socket transport in  
`dbus/dbus-transport-socket.c` (`do_writing()` and `check_write_watch()`)
Version affected: `dbus-1.14.10-5.el10`
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 - 6.2 (MEDIUM)
AV:L - The attacker needs local IPC/socket access to the target process.
AC:L - Once a reachable local endpoint exists, the trigger sequence is  
straightforward: cause a reply to be queued, stop reading, and keep the  
connection open.
PR:N - No elevated privileges are required beyond being able to connect  
as a local client to the exposed endpoint.
UI:N - No separate user interaction is required.
S:U - The impact remains within the security scope of the vulnerable  
process.
C:N - No confidentiality impact is established.
I:N - No integrity impact is established.
A:H - The process can be driven into persistent high CPU usage,  
materially degrading availability.
Impact: Moderate. The flaw can significantly affect availability for an  
affected process, but the attack is local, depends on a reachable local  
endpoint and watch-driven write handling, and no confidentiality,  
integrity, or privilege-escalation impact is shown. Under Red Hat's  
severity guidance, that fits a Moderate availability issue more closely  
than Important or Critical.
Embargo: no
Reason: This is a local, availability-only issue with  
deployment-dependent reachability and a direct mitigation path. That is not  
the usual profile for an embargoed issue.
Acknowledgement: Aisle Research
Vulnerability Details: In `dbus/dbus-transport-socket.c`, `do_writing()`  
treats `EPIPE` as retryable and returns without disconnecting the transport  
or removing the queued outbound data:
```c
/* dbus/dbus-transport-socket.c */
if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno) ||  
_dbus_get_is_errno_epipe (saved_errno))
goto out;
```
`check_write_watch()` then re-enables the write watch whenever queued  
outgoing messages remain:
```c
needed = _dbus_connection_has_messages_to_send_unlocked  
(transport->connection);
_dbus_connection_toggle_watch_unlocked (transport->connection,
socket_transport->write_watch,
needed);
```
Taken together, those code paths preserve the conditions for repeated write  
callbacks after `EPIPE` with no forward progress. In watch-driven  
integrations, that can cause sustained CPU consumption while the queued  
reply keeps write activity armed. The exact callback rate can vary by  
platform and event loop, but the code does not clear the write condition or  
otherwise stop further write attempts after the broken-pipe state.
Steps to reproduce:
1. Start a local process linked against the packaged libdbus socket  
transport using normal watch-based main-loop integration.
2. Connect to that process as an unprivileged local client.
3. Send a method call or other request that causes the target to queue a  
reply.
4. On the client socket, call `shutdown(fd, SHUT_RD)` so replies are no  
longer read, but keep the socket otherwise open.
5. Keep the connection alive.
6. Observe the target process repeatedly re-enter `do_writing()` and hit  
`EPIPE`, with CPU usage rising while the queued outbound message keeps  
write-watch handling active.
Mitigation: Until a code fix is available, reduce exposure by restricting  
untrusted local peers from connecting to the affected socket or bus  
endpoint. If behavior changes are acceptable, disconnecting the peer or  
otherwise stopping further write scheduling after `EPIPE` also prevents the  
retry loop.
Proposed Fix: The least behavior-changing fix is to preserve the current  
decision not to immediately disconnect on `EPIPE`, but record that `EPIPE`  
occurred and stop re-arming write activity for that connection while still  
allowing reads.
```diff
diff --git a/dbus/dbus-transport-socket.c b/dbus/dbus-transport-socket.c
— a/dbus/dbus-transport-socket.c
+++ b/dbus/dbus-transport-socket.c
@@ -60,6 +60,7 @@ typedef struct
DBusString encoded_outgoing;
DBusString encoded_incoming;
int message_bytes_written;
+  dbus_bool_t epipe_seen;
} DBusTransportSocket;
@@ -136,7 +137,8 @@ check_write_watch (DBusTransport *transport)
if (_dbus_transport_try_to_authenticate (transport))
   needed = _dbus_connection_has_messages_to_send_unlocked  
(transport->connection);
+    needed = _dbus_connection_has_messages_to_send_unlocked  
(transport->connection) &&
+             !socket_transport->epipe_seen;
    else
      {


@@ -667,8 +669,14 @@ do_writing (DBusTransport *transport)
         if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno) ||  
_dbus_get_is_errno_epipe (saved_errno))
+          if (_dbus_get_is_errno_eagain_or_ewouldblock (saved_errno))
              goto out;
+          else if (_dbus_get_is_errno_epipe (saved_errno))
+            {
+              /* Peer is no longer reading: keep transport for reads,
+               * but stop re-arming write activity to avoid CPU spin.
+               */
+              socket_transport->epipe_seen = TRUE;
+              goto out;
+            }


@@ -1308,6 +1316,7 @@
socket_transport->message_bytes_written = 0;
+  socket_transport->epipe_seen = FALSE;
```
------
This report was generated using AI technology. Always review AI-generated  
content prior to use