This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx.git

commit 152ebca59969b5dc84415572a626e9fdb570471b
Author: vela-autotest05 <[email protected]>
AuthorDate: Tue Jul 28 04:11:08 2026 +0800

    fs/inode: fix relative-path truncation causing wrong EISDIR
    
    Root cause: _inode_search() built the absolute form of a relative
    path with snprintf(buf, PATH_MAX, "%s/%s", cwd, path), silently
    truncating it when cwd + "/" + path exceeded PATH_MAX. The truncated
    buffer was then handed to _inode_canonicalize(), which collapsed
    ".." segments against the wrong cut-off suffix. A valid relative
    path of PATH_MAX-1 bytes (legal per pathconf(_PC_PATH_MAX)) could
    thus collapse onto a directory and open() returned EISDIR instead
    of resolving the file.
    
    Fix: size the temp buffer to hold the full uncanonicalized
    "<cwd>/<path>" form so canonicalization sees the complete path.
    lib_get_tempbuffer falls back to a malloc'd buffer when the size
    exceeds PATH_MAX (CONFIG_LIBC_TEMPBUFFER_MALLOC). The existing
    PATH_MAX check in _inode_canonicalize() still rejects any
    canonicalized result that is too long, so ENAMETOOLONG semantics
    are preserved.
    
    Signed-off-by: dengwenqi <[email protected]>
---
 fs/inode/fs_inodesearch.c | 45 +++++++++++++++++++++++++++++++++++----------
 1 file changed, 35 insertions(+), 10 deletions(-)

diff --git a/fs/inode/fs_inodesearch.c b/fs/inode/fs_inodesearch.c
index 969ded7453b..fac611a7c8f 100644
--- a/fs/inode/fs_inodesearch.c
+++ b/fs/inode/fs_inodesearch.c
@@ -398,25 +398,50 @@ static int _inode_search(FAR struct inode_search_s *desc)
 
   if (desc->buffer == NULL)
     {
-      desc->buffer = lib_get_tempbuffer(PATH_MAX);
+      FAR const char *cwd = NULL;
+      size_t buflen;
+
+      /* For a relative path the absolute form is "<cwd>/<path>".  That
+       * concatenation can exceed PATH_MAX even when the relative path
+       * itself is within PATH_MAX: a relative path of PATH_MAX-1 bytes
+       * is legal per pathconf(_PC_PATH_MAX), but the prefix added by the
+       * cwd pushes the uncanonicalized form past the limit.  Size the
+       * buffer to hold the full absolute form so that ".." segments are
+       * collapsed against the correct suffix; truncating first could
+       * drop the trailing component and let ".." collapse the path onto
+       * a directory (yielding the wrong errno, e.g. EISDIR, instead of
+       * resolving the file).  _inode_canonicalize() still rejects any
+       * result whose canonicalized length reaches PATH_MAX.
+       */
+
+      if (*desc->path != '/')
+        {
+          cwd = _inode_getcwd();
+          buflen = strlen(cwd) + 1 + strlen(desc->path) + 1;
+        }
+      else
+        {
+          buflen = strlen(desc->path) + 1;
+        }
+
+      if (buflen < PATH_MAX)
+        {
+          buflen = PATH_MAX;
+        }
+
+      desc->buffer = lib_get_tempbuffer(buflen);
       if (desc->buffer == NULL)
         {
           return -ENOMEM;
         }
 
-      /* Convert relative path to absolute, or just copy absolute path.
-       * Use desc->path directly (points to caller's string) as source
-       * to avoid overlap with desc->buffer.
-       */
-
-      if (*desc->path != '/')
+      if (cwd != NULL)
         {
-          snprintf(desc->buffer, PATH_MAX, "%s/%s",
-                   _inode_getcwd(), desc->path);
+          snprintf(desc->buffer, buflen, "%s/%s", cwd, desc->path);
         }
       else
         {
-          strlcpy(desc->buffer, desc->path, PATH_MAX);
+          strlcpy(desc->buffer, desc->path, buflen);
         }
 
       desc->path = desc->buffer;

Reply via email to