libc/termios: Fix the ttyname_r buffer overflow with long tty paths.

ttyname_r() passed the caller buffer straight to fcntl(F_GETPATH)
whenever buflen >= TTY_NAME_MAX, but every FIOC_FILEPATH handler
writes the path bounded by PATH_MAX and ignores the caller buffer
size.  A tty registered under a nested /dev path, or reached through
rpmsgfs, has a path longer than TTY_NAME_MAX and overwrote the caller
buffer, silently corrupting memory behind a zero return code.  The
small-buffer branch had the same defect against its own stack local
char name[TTY_NAME_MAX].  Gate the direct write on PATH_MAX instead
and stage the path through a PATH_MAX path buffer obtained via
lib_get_tempbuffer(), returning ERANGE when it does not fit.

Verified on sim:nsh with a test driver registered at an 85-character
tty path: pre-fix, ttyname_r(buf, TTY_NAME_MAX) returned 0 and
smashed the canaries behind the buffer, and the small-buffer branch
panicked; post-fix both cases return ERANGE with the canaries intact.

Assisted-by: Claude Code (glm-5.3) <claude@anthropic.com>
Signed-off-by: Junbo Zheng <zhengjunbo1@xiaomi.com>
This commit is contained in:
Junbo Zheng 2026-10-02 23:07:43 +08:00 • committed by Xiang Xiao
parent acc7d9bd3c
commit 82b2f1993b

View file

@ -29,6 +29,8 @@
#include <string.h>
#include <unistd.h>
#include <nuttx/lib/lib.h>
/****************************************************************************
* Public Functions
****************************************************************************/
@ -63,25 +65,36 @@ int ttyname_r(int fd, FAR char *buf, size_t buflen)
return ENOTTY;
}
if (buflen >= TTY_NAME_MAX)
/* The F_GETPATH handler copies the file path into the caller buffer
* bounded by PATH_MAX, not by any tty-specific limit, so the path must
* always land in a PATH_MAX-sized buffer first. Only a caller buffer
* of that size can receive it directly.
*/
if (buflen >= PATH_MAX)
{
return fcntl(fd, F_GETPATH, buf) < 0 ? get_errno() : 0;
}
else
{
char name[TTY_NAME_MAX];
FAR char *path = lib_get_tempbuffer(PATH_MAX);
int ret;
if (fcntl(fd, F_GETPATH, name) < 0)
if (fcntl(fd, F_GETPATH, path) < 0)
{
return get_errno();
ret = get_errno();
}
else if (strlen(path) >= buflen)
{
ret = ERANGE;
}
else
{
strlcpy(buf, path, buflen);
ret = OK;
}
if (strlen(name) >= buflen)
{
return ERANGE;
}
strlcpy(buf, name, buflen);
return OK;
lib_put_tempbuffer(path);
return ret;
}
}