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; }
