Skip to content

net: TcpSocket::connect still renders resolver failures with ::gai_strerror, whose thread-safety is unestablished #640

Description

@Yaraslaut

TcpSocket::connect is now the one error path in morph::net whose renderer
has not been shown to be safe on the threads this subsystem runs. #625 replaced
the seven std::strerror calls in include/morph/net/detail/tcp_socket.hpp
with std::system_category().message(); it deliberately did not touch
::gai_strerror, and this issue records the residue rather than letting the
decision disappear into that pull request.

The site (include/morph/net/detail/tcp_socket.hpp:112 after #625, :111
before it):

throw std::runtime_error("TcpSocket::connect: getaddrinfo failed for " + host + ": " + ::gai_strerror(rc));

Why it was not folded into #625

There is no drop-in replacement. rc is an EAI_* code from ::getaddrinfo,
not an errno value, so std::system_category().message(rc) would render a
confidently wrong string (EAI_NONAME is -2 on glibc; system_category()
would describe errno -2). Any repair here needs its own design decision — a
hand-written EAI_* table, or an argument that the call is already safe — and
that is a different change from swapping one renderer for another.

Verification status

Not reproduced, and not resolved either way. What was actually measured, on
a8511aa6 with clang-tidy 22.1.8:

  1. clang-tidy's concurrency-mt-unsafe does not classify ::gai_strerror as
    unsafe, while it classifies std::strerror in the same translation unit as
    unsafe. Probe file and output:

    #include <netdb.h>
    #include <cstring>
    #include <string>
    std::string f(int e) { return ::gai_strerror(e); }
    std::string g(int e) { return std::strerror(e); }
    $ clang-tidy --checks='-*,concurrency-mt-unsafe' net625_gai.cpp -- -std=c++23
    1 warning generated.
    net625_gai.cpp:5:31: warning: function is not thread safe [concurrency-mt-unsafe]
        5 | std::string g(int e) { return std::strerror(e); }
          |                               ^
    
  2. The same check over the whole header reports nothing at the ::gai_strerror
    line — the six findings it does report on a8511aa6 are all std::strerror
    sites:

    $ clang-tidy -p build/net --checks='-*,concurrency-mt-unsafe' \
        --header-filter='include/morph/.*' tests/net/test_tcp_socket.cpp
    include/morph/net/detail/tcp_socket.hpp:195:92: error: function is not thread safe [concurrency-mt-unsafe,-warnings-as-errors]
    include/morph/net/detail/tcp_socket.hpp:206:90: error: function is not thread safe [concurrency-mt-unsafe,-warnings-as-errors]
    include/morph/net/detail/tcp_socket.hpp:211:92: error: function is not thread safe [concurrency-mt-unsafe,-warnings-as-errors]
    include/morph/net/detail/tcp_socket.hpp:276:75: error: function is not thread safe [concurrency-mt-unsafe,-warnings-as-errors]
    include/morph/net/detail/tcp_socket.hpp:353:81: error: function is not thread safe [concurrency-mt-unsafe,-warnings-as-errors]
    include/morph/net/detail/tcp_socket.hpp:371:80: error: function is not thread safe [concurrency-mt-unsafe,-warnings-as-errors]
    

What was NOT verified, and is the substance of this issue:

  • Whether POSIX requires gai_strerror to be thread-safe. Not checked against
    the standard text; man 3 gai_strerror is not installed on the machine this
    was measured on, so its ATTRIBUTES table was not read either.
  • Whether glibc's implementation is in fact MT-Safe. Inferred-only, from
    clang-tidy's classification, which is a tool's opinion about a function name
    and not a reading of the implementation.
  • Whether TcpSocket::connect is reachable from more than one thread at a
    time. It is called from SocketBackend's I/O thread
    (socket_backend.hpp:909) and directly from test threads; whether two
    connect calls can overlap in a real deployment was not established.

So the honest state is: a renderer whose thread-safety this project has
asserted nowhere, sitting on an error path in a subsystem that owns several
threads.
That is smaller than #625 was, and it is not nothing.

What would change the verdict

  • Close as invalid if POSIX requires gai_strerror to be thread-safe, or
    if the platforms this transport supports (Linux/macOS — the header is POSIX
    only) all document it MT-Safe. Cite the text, not a tool's check list.
  • Close as fixed if the call is replaced with something whose guarantee is
    written down — most plausibly an explicit EAI_* switch, since
    std::system_category() cannot describe these codes.
  • Keep open, and raise it, if it turns out connect() can run concurrently
    with itself on a supported platform where the function is not MT-Safe.

Found while implementing #625. Filed rather than folded, per AGENTS.md.

🤖 Generated with Claude Code

https://claude.ai/code/session_01VptDWG2fKr2vBnLSJcgzgW

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: coreSubsystem: corebugSomething isn't workingtriage: unverifiedPremise plausible but untested; blocked on an experiment

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions