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: | vulnerability | Assignee: | Product Security DevOps Team <prodsec-dev> |
| Status: | NEW --- | QA Contact: | |
| Severity: | medium | Docs Contact: | |
| Priority: | medium | ||
| Version: | unspecified | CC: | 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: | |||
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