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

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

commit 0b396d26e0e9f045d59e8f02ea5f322a57c2eb8f
Author: Marco Casaroli <[email protected]>
AuthorDate: Wed Nov 4 15:10:52 2026 +0100

    binfmt/builtin: Add an option to match a builtin by path as POSIX specifies.
    
    The builtin binary format looks up only the last part of a path, so
    exec("/nonexistent/hello") starts the builtin "hello", and so does
    exec("/tmp/hello") for a file that is not a program.  POSIX wants ENOENT
    and ENOEXEC.
    
    With the new option CONFIG_BINFMT_BUILTIN_STRICT_PATH (default n), a
    name with a '/' matches a builtin only if it names a file in a mounted
    BINFS.  A bare name still matches.  Without the option nothing changes.
    
    The default stays, because code depends on it: without a BINFS at /bin,
    popen() without LIBC_ENVPATH uses "/bin/sh" and that path starts the
    builtin "sh".  The help text lists what the option breaks.
    
    Assisted-by: Claude Code:claude-opus-5-5
    Signed-off-by: Marco Casaroli <[email protected]>
---
 binfmt/Kconfig   | 21 +++++++++++++++++++++
 binfmt/builtin.c | 43 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 64 insertions(+)

diff --git a/binfmt/Kconfig b/binfmt/Kconfig
index 37bf0962588..4cc63485263 100644
--- a/binfmt/Kconfig
+++ b/binfmt/Kconfig
@@ -59,6 +59,27 @@ config BINFMT_SCRIPT
                The interpreter itself must be loadable by another registered
                binfmt handler (builtin, ELF, etc.).
 
+config BINFMT_BUILTIN_STRICT_PATH
+       bool "Match a builtin application by path as POSIX specifies"
+       default n
+       depends on BUILTIN
+       ---help---
+               By default, the builtin binary format looks up only the last 
part of
+               a path, so exec("/nonexistent/hello") starts the builtin 
application
+               "hello", and so does exec("/tmp/hello") for a file that is not a
+               program.  POSIX wants ENOENT and ENOEXEC.
+
+               With this option, a name with a '/' matches a builtin 
application
+               only if it names a file in a mounted BINFS (FS_BINFS).  A bare 
name
+               still matches.
+
+               Code that starts a builtin application by a path with no file 
there
+               then fails.  Without a BINFS at /bin this includes popen() 
without
+               LIBC_ENVPATH ("/bin/sh": no shell, so a shell command fails with
+               ENOENT), a script with "#!/bin/sh", the shell "/bin/sh" of 
BASIC and
+               dropbear, the default shell of ADB, and the shell "/bin/nsh" 
that
+               getpwnam() reports for root.
+
 config ELF
        bool "Enable the ELF Binary Format"
        default n
diff --git a/binfmt/builtin.c b/binfmt/builtin.c
index fa82eab54fb..4ec3b85dbf9 100644
--- a/binfmt/builtin.c
+++ b/binfmt/builtin.c
@@ -26,18 +26,25 @@
 
 #include <nuttx/config.h>
 
+#include <stdbool.h>
 #include <stdint.h>
 #include <string.h>
+#include <fcntl.h>
 #include <nuttx/debug.h>
 #include <errno.h>
 
 #include <nuttx/binfmt/binfmt.h>
+#include <nuttx/fs/fs.h>
 #include <nuttx/lib/builtin.h>
 
 #include "binfmt.h"
 
 #ifdef CONFIG_BUILTIN
 
+#if defined(CONFIG_BINFMT_BUILTIN_STRICT_PATH) && defined(CONFIG_FS_BINFS)
+extern const struct mountpt_operations g_binfs_operations;
+#endif
+
 /****************************************************************************
  * Private Function Prototypes
  ****************************************************************************/
@@ -62,6 +69,35 @@ static struct binfmt_s g_builtin_binfmt =
  * Private Functions
  ****************************************************************************/
 
+/****************************************************************************
+ * Name: builtin_inbinfs
+ *
+ * Description:
+ *   Return true if the path names a file in a mounted BINFS.
+ *
+ ****************************************************************************/
+
+#ifdef CONFIG_BINFMT_BUILTIN_STRICT_PATH
+static bool builtin_inbinfs(FAR const char *path)
+{
+  bool inbinfs = false;
+#ifdef CONFIG_FS_BINFS
+  struct file file;
+
+  if (file_open(&file, path, O_RDONLY | O_CLOEXEC) >= 0)
+    {
+      inbinfs = INODE_IS_MOUNTPT(file.f_inode) &&
+                file.f_inode->u.i_mops == &g_binfs_operations;
+      file_close(&file);
+    }
+#else
+  UNUSED(path);
+#endif
+
+  return inbinfs;
+}
+#endif
+
 /****************************************************************************
  * Name: builtin_loadbinary
  *
@@ -84,6 +120,13 @@ static int builtin_loadbinary(FAR struct binary_s *binp,
   name = strrchr(filename, '/');
   if (name != NULL)
     {
+#ifdef CONFIG_BINFMT_BUILTIN_STRICT_PATH
+      if (!builtin_inbinfs(filename))
+        {
+          return -ENOEXEC;
+        }
+#endif
+
       filename = name + 1;
     }
 

Reply via email to