ListenConnections stops listening for good after one temporary accept() failure such as EMFILE

Abierto
#384 0 comentarios 0 reacciones 0 asignados Ver en GitHub

Los mantenedores suelen responder en 1 día

Nadie ha tomado este issue todavía.

Evaluación

Dificultad
3/5
Tiempo estimado
1-2 días
Aptitud para principiantes
57/100
Tipo de issue
Error
Claridad
Bien especificado
Estado de actividad
Activo
Stack tecnológico
cpp

Línea de trabajo

Start with _Listen() in include/mp/proxy-io.h and the accept error handler in src/mp/proxy.cpp; then read listen_tests.cpp and its ListenSetup helper. Add a test that wraps the receiver so its first accept() returns OVERLOADED, then confirm a later client is served after retry. Done means temporary overloads retry after a delay while other accept errors still stop the listener.

Escrito por el modelo de indexación a partir del texto del issue.

Descripción

bug hardening

If accept() fails once in ListenConnections, even with a temporary error like running out of file descriptors (EMFILE), the listener stops for good: nothing accepts new clients again until the process restarts. If other IPC connections are open, the listening socket stays open and new clients connect but are never served; once those connections close, or right away if there were none, the socket is closed and clients get "connection refused". Temporary errors should be retried after a short delay.

Where

_Listen() adds receiver->accept().then(...) to the event loop's task set and only schedules the next accept from inside the success continuation. A failed accept goes to the task set's error handler, which logs "Uncaught exception in daemonized task" and drops the task. Nothing schedules another accept, and the dropped continuation held a std::shared_ptr<Listener>. The only other references are in the disconnect callbacks of open connections, so the receiver and its socket are destroyed when the last of those closes, or right away if there are none.

kj reports EMFILE, ENFILE, ENOBUFS and ENOMEM from accept() as kj::Exception::Type::OVERLOADED, so these errors are recognizable as temporary.

Origin

The missing retry has been there since ListenConnections was added in bitcoin-core/libmultiprocess@d519e1885990 (2019). The accept continuation was then the receiver's only owner, so a failed accept closed the socket at once. #269 (connection limit, merged 2026-07-08) moved the receiver into a shared Listener that open connections also reference, which is why new clients now hang until those connections close. In Bitcoin Core the listener is reachable since -ipcbind was added (bitcoin/bitcoin#30509, v30.0); the hanging variant arrived with the subtree update bitcoin/bitcoin#35684 and is in v32.0.

Impact on Bitcoin Core

An -ipcbind listener in bitcoin-node stops serving new IPC clients until restart (they hang while an existing client such as a mining client stays connected, and are refused after that) if the process runs out of descriptors while a client is connecting. The node itself keeps running. bitcoin/bitcoin#35037 now counts IPC listeners and connection slots in the node's descriptor budget, so this needs a descriptor leak or other open files, but the listener should survive it. Other users of the library have no such budget.

Possible fix

Catch only OVERLOADED errors from the accept, log a warning once per run of failures (including the error text, so EMFILE can be told apart from ENOMEM), wait briefly on the event loop's timer, and call _Listen() again. Other errors keep ending the listener as they do now, so a permanent failure cannot become a busy loop. A capped backoff (start small, double up to about a second) would be a little friendlier than a fixed delay.

This is not the blanket retry rejected in #310 (review). That concern was an immediate retry on every error, which would spin on permanent failures and on resource exhaustion; a delayed retry limited to OVERLOADED errors does neither.

Test

No descriptor limits need to change. Wrap the real listening socket in a kj::ConnectionReceiver whose first accept() returns an OVERLOADED exception and forwards later calls, pass it to _Listen(), connect a client and check it is served:

class FailFirstAccept final : public kj::ConnectionReceiver
{
public:
    FailFirstAccept(kj::Own<kj::ConnectionReceiver> inner, kj::Exception::Type type)
        : m_inner(kj::mv(inner)), m_type(type) {}

    kj::Promise<kj::Own<kj::AsyncIoStream>> accept() override
    {
        if (m_fail) {
            m_fail = false;
            return kj::Exception(m_type, __FILE__, __LINE__, kj::str("simulated accept failure"));
        }
        return m_inner->accept();
    }

    uint getPort() override { return m_inner->getPort(); }

    kj::Own<kj::ConnectionReceiver> m_inner;
    kj::Exception::Type m_type;
    bool m_fail{true};
};

With ListenSetup in listen_tests.cpp building the listener from that wrapper, the test fails at once without a fix (no other connections are open, so the socket closes and the client's connect() is refused) and passes with the retry, in about the length of the delay. Checked 2026-10-15 against Bitcoin Core master (bitcoin/bitcoin@badc9574810, which carries libmultiprocess v13 in its subtree). Full test and fix: https://ipcbugs.ofsky.org/bugs/mp-2026-accept-failure-stops-listener/#reproduction

Reported privately in an independent security review, 2026-10-15 (audit report AR-79).

Links

Lenguaje dominante
C++
Estrellas
62
Forks
58
Merge medio
6 d 11 h
PR fusionados (30 d)
7

Preparar el entorno

Este proyecto no incluye contenedor de desarrollo, Dockerfile ni guía de contribución, así que la configuración corre por tu cuenta: empieza por su README y consulta nuestra guía para la primera contribución para los pasos generales.

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Más de bitcoin-core/libmultiprocess

Todos los issues de bitcoin-core/libmultiprocess

Issues similares

Más issues de C++

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.