nuttx/libs/libc/termios/lib_ttynamer.c
Junbo Zheng 82b2f1993b 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>
2026-10-05 18:42:52 +08:00

100 lines
3.3 KiB
C

/****************************************************************************
* libs/libc/termios/lib_ttynamer.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <nuttx/lib/lib.h>
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: ttyname_r
*
* Description:
* The ttyname_r() function shall store the null-terminated pathname of
* the terminal associated with the file descriptor fildes in the
* character array referenced by name. The array is namesize characters
* long and should have space for the name and the terminating null
* character. The maximum length of the terminal name shall be
* {TTY_NAME_MAX}.
*
* Input Parameters:
* fd - The 'fd' argument is an open file descriptor associated with
* a terminal.
* buf - Caller provided buffer to hold tty name.
* buflen - The size of the caller-provided buffer.
*
* Returned Value:
* If successful, the ttyname_r() function shall return zero.
* Otherwise, an error number shall be returned to indicate the error.
*
****************************************************************************/
int ttyname_r(int fd, FAR char *buf, size_t buflen)
{
if (!isatty(fd))
{
return ENOTTY;
}
/* 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
{
FAR char *path = lib_get_tempbuffer(PATH_MAX);
int ret;
if (fcntl(fd, F_GETPATH, path) < 0)
{
ret = get_errno();
}
else if (strlen(path) >= buflen)
{
ret = ERANGE;
}
else
{
strlcpy(buf, path, buflen);
ret = OK;
}
lib_put_tempbuffer(path);
return ret;
}
}