netutils/telnetd: keep the listen socket out of the standard-stream range

When telnetd_daemon() starts without open standard streams - exactly
what happens when nsh_telnetstart() spawns "telnetd &" before the
console device exists (e.g. CONFIG_NSH_USBCONSOLE boards, where
nsh_initialize() runs before the USB console is connected) - socket()
returns a descriptor in the 0..2 range.  The accept loop's own "go
silent" close(0)..close(2) then destroys the listen socket on the
first iteration: every subsequent accept4() fails with EBADF and the
daemon serves nothing.

Observed on hardware (RP2350, CDC-ACM console + CDC-NCM composite):
the daemon task was alive but port 23 refused every connection; the
thread list showed it looping on the failed accept.  Duplicate the
descriptor above the standard-stream range before using it.

Assisted-by: Claude Fable 5 <noreply@anthropic.com>
Signed-off-by: Ricard Rosson <ricard@groundbits.com>
This commit is contained in:
Ricard Rosson 2026-07-23 18:59:54 +01:00
parent d273af4834
commit ac73e41fc3

View file

@ -125,6 +125,28 @@ int telnetd_daemon(FAR const struct telnetd_config_s *config)
goto errout;
}
/* If the daemon was started without standard streams (e.g. spawned by
* nsh_telnetstart() before a USB console device exists), socket() may
* have returned a descriptor in 0..2. The "go silent"
* close(0)..close(2) at the top of the accept loop below would then
* destroy the listen socket: every subsequent accept4() fails and the
* daemon serves nothing. Move the descriptor above the
* standard-stream range.
*/
if (listensd <= 2)
{
int highsd = fcntl(listensd, F_DUPFD_CLOEXEC, 3);
if (highsd < 0)
{
nerr("ERROR: F_DUPFD_CLOEXEC failed: %d\n", errno);
goto errout_with_socket;
}
close(listensd);
listensd = highsd;
}
#ifdef CONFIG_NET_SOCKOPTS
/* Set socket to reuse address */