<div dir="ltr">Hi Simon,<br><br>  If a TCP listening socket is externally destroyed (e.g., via ss -K,<br>  or a process using NETLINK_SOCK_DIAG/SOCK_DESTROY), accept()<br>  permanently returns -1 with errno == EINVAL because the socket is no<br>  longer in TCP_LISTEN state. Since poll() keeps reporting the stale fd<br>  as readable, the main loop spins calling do_tcp_connection() -><br>  accept() indefinitely, consuming 100% CPU.<br><br>  Reproduce:<br><br>  $ dnsmasq --bind-dynamic --interface=lo --port=15353 --no-daemon &<br>  $ sudo ss -K state listening sport = :15353<br>  $ # dnsmasq now spins at 100% CPU<br><br>  The fix distinguishes transient errors from fatal ones. On transient<br>  errors (EAGAIN, ECONNABORTED, EMFILE, ENFILE, ENOMEM, ENOBUFS), we<br>  just return and retry on the next poll cycle. On fatal errors (EINVAL,<br>  EBADF, etc.), we close the tcpfd and mark it -1, so poll() no longer<br>  selects it.<br>  <br>  In --bind-dynamic mode, the listener will be automatically rebuilt on<br>  the next address change event via newaddress(). Alternatively, a<br>  dedicated event could trigger targeted listener rebuild without the<br>  DHCPv6/RA side effects of EVENT_NEWADDR — happy to implement that if<br>  you prefer.<br><br>  Tested: dnsmasq 2.93 on Linux 6.1/5.15, verified 0% CPU after socket<br><div>  destruction with the patch (vs 100% without).</div><div><br></div><div>  diff --git a/src/dnsmasq.c b/src/dnsmasq.c<br>  index c1e48fc..fa4a467 100644<br>  --- a/src/dnsmasq.c<br>  +++ b/src/dnsmasq.c<br>  @@ -2029,7 +2029,20 @@ static void do_tcp_connection(struct listener *listener, time_t now, int slot)<br>     while ((confd = accept(listener->tcpfd, NULL, NULL)) == -1 && errno == EINTR);<br>     <br>     if (confd == -1)<br>  -    return; <br>  +    {<br>  +      /* Transient errors: just return and retry on next poll cycle. */<br>  +      if (errno == EAGAIN || errno == ECONNABORTED ||<br>  +          errno == EMFILE || errno == ENFILE ||<br>  +          errno == ENOMEM || errno == ENOBUFS)<br>  +        return;<br>  +<br>  +      /* Fatal error (EINVAL, EBADF, etc): socket is permanently broken.<br>  +         Close it so poll() no longer selects it.  In --bind-dynamic mode<br>  +         the listener will be rebuilt on the next address change event. */<br>  +      close(listener->tcpfd);<br>  +      listener->tcpfd = -1;<br>  +      return;<br>  +    }<br><br>     if (getsockname(confd, (struct sockaddr *)&tcp_addr, &tcp_len) == -1)<br>       {<br>       <br>  Signed-off-by: Yuefu Zhou <a href="mailto:yuefu16.zhou@gmail.com">yuefu16.zhou@gmail.com</a><br><br></div></div>