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


The following commit(s) were added to refs/heads/master by this push:
     new 03685825c9f fs/binfmt: Enforce POSIX execute permissions prior to 
binary load
03685825c9f is described below

commit 03685825c9f56ca2221ed455ed497e9c3eea17f3
Author: Abhishek Mishra <[email protected]>
AuthorDate: Tue Jun 9 08:02:57 2026 +0000

    fs/binfmt: Enforce POSIX execute permissions prior to binary load
    
    Adds a pre-load permission check in exec_internal() to verify the
    calling task has the required execute (x) bits for the target file.
    Properly evaluates root (euid == 0), owner, group, and other permissions.
    
    This ensures POSIX compliance and cleanly rejects unauthorized files
    with -EACCES before they reach the ELF loader, preventing unnecessary
    memory allocation and downstream hardware execution faults.
    
    Signed-off-by: Abhishek Mishra <[email protected]>
---
 binfmt/CMakeLists.txt      |   4 ++
 binfmt/Makefile            |   4 ++
 binfmt/binfmt.h            |  20 +++++++++
 binfmt/binfmt_checkexec.c  | 100 +++++++++++++++++++++++++++++++++++++++++++++
 binfmt/binfmt_exec.c       |   7 ++--
 binfmt/binfmt_loadmodule.c |   8 ++++
 binfmt/builtin.c           |  22 +++++++---
 binfmt/elf.c               |  24 +++++++----
 binfmt/nxflat.c            |  26 ++++++++++++
 9 files changed, 199 insertions(+), 16 deletions(-)

diff --git a/binfmt/CMakeLists.txt b/binfmt/CMakeLists.txt
index 0f0578ebce3..1af4cd3042b 100644
--- a/binfmt/CMakeLists.txt
+++ b/binfmt/CMakeLists.txt
@@ -41,6 +41,10 @@ list(
   binfmt_copyactions.c
   binfmt_dumpmodule.c)
 
+if(CONFIG_SCHED_USER_IDENTITY)
+  list(APPEND SRCS binfmt_checkexec.c)
+endif()
+
 if(CONFIG_BINFMT_LOADABLE)
   list(APPEND SRCS binfmt_exit.c)
 endif()
diff --git a/binfmt/Makefile b/binfmt/Makefile
index e88e1a9c27e..31d84b1d50c 100644
--- a/binfmt/Makefile
+++ b/binfmt/Makefile
@@ -28,6 +28,10 @@ CSRCS  = binfmt_globals.c binfmt_initialize.c 
binfmt_register.c binfmt_unregiste
 CSRCS += binfmt_loadmodule.c binfmt_unloadmodule.c binfmt_execmodule.c
 CSRCS += binfmt_exec.c binfmt_copyargv.c binfmt_copyactions.c 
binfmt_dumpmodule.c
 
+ifeq ($(CONFIG_SCHED_USER_IDENTITY),y)
+CSRCS += binfmt_checkexec.c
+endif
+
 ifeq ($(CONFIG_BINFMT_LOADABLE),y)
 CSRCS += binfmt_exit.c
 endif
diff --git a/binfmt/binfmt.h b/binfmt/binfmt.h
index 3cdf07ecbfa..c362bf041c6 100644
--- a/binfmt/binfmt.h
+++ b/binfmt/binfmt.h
@@ -213,6 +213,26 @@ void binfmt_freeactions(FAR const 
posix_spawn_file_actions_t *copy);
 #  define binfmt_freeactions(copy)
 #endif
 
+#ifdef CONFIG_SCHED_USER_IDENTITY
+/****************************************************************************
+ * Name: binfmt_checkexecperm
+ *
+ * Description:
+ *   Verify that the calling task has execute permission on the file
+ *   described by 'bin'.  The file owner, group, and mode must already be
+ *   populated in the binary_s structure.
+ *
+ * Input Parameters:
+ *   bin - Load structure with uid, gid, and mode populated
+ *
+ * Returned Value:
+ *   Zero (OK) on success; -EACCES if execute permission is denied.
+ *
+ ****************************************************************************/
+
+int binfmt_checkexecperm(FAR struct binary_s *bin);
+#endif
+
 #ifdef CONFIG_BUILTIN
 /****************************************************************************
  * Name: builtin_initialize
diff --git a/binfmt/binfmt_checkexec.c b/binfmt/binfmt_checkexec.c
new file mode 100644
index 00000000000..1436298dc24
--- /dev/null
+++ b/binfmt/binfmt_checkexec.c
@@ -0,0 +1,100 @@
+/****************************************************************************
+ * binfmt/binfmt_checkexec.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 <nuttx/config.h>
+
+#include <sys/stat.h>
+#include <errno.h>
+
+#include <nuttx/sched.h>
+#include <nuttx/binfmt/binfmt.h>
+
+#include "binfmt.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: binfmt_checkexecperm
+ *
+ * Description:
+ *   Verify that the calling task has execute permission on the file
+ *   described by 'bin'.  The file owner, group, and mode must already be
+ *   populated in the binary_s structure before calling this function.
+ *
+ * Input Parameters:
+ *   bin - Pointer to the binary descriptor with uid, gid, and mode set.
+ *
+ * Returned Value:
+ *   Zero (OK) on success; -EACCES if execute permission is denied.
+ *
+ ****************************************************************************/
+
+int binfmt_checkexecperm(FAR struct binary_s *bin)
+{
+  FAR struct tcb_s *rtcb;
+  mode_t xbits;
+
+  rtcb = nxsched_self();
+
+  if (bin == NULL || rtcb == NULL || rtcb->group == NULL)
+    {
+      return OK;
+    }
+
+  if (rtcb->group->tg_euid == 0)
+    {
+      /* Root can execute any file that has at least one execute bit set */
+
+      if ((bin->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
+        {
+          return -EACCES;
+        }
+
+      return OK;
+    }
+
+  if (rtcb->group->tg_euid == bin->uid)
+    {
+      xbits = S_IXUSR;
+    }
+  else if (rtcb->group->tg_egid == bin->gid)
+    {
+      xbits = S_IXGRP;
+    }
+  else
+    {
+      xbits = S_IXOTH;
+    }
+
+  if ((bin->mode & xbits) == 0)
+    {
+      return -EACCES;
+    }
+
+  return OK;
+}
diff --git a/binfmt/binfmt_exec.c b/binfmt/binfmt_exec.c
index c2c73ee876b..ae3e4643b1e 100644
--- a/binfmt/binfmt_exec.c
+++ b/binfmt/binfmt_exec.c
@@ -31,7 +31,6 @@
 #include <errno.h>
 
 #include <nuttx/kmalloc.h>
-#include <nuttx/sched.h>
 #include <nuttx/binfmt/binfmt.h>
 
 #include "binfmt.h"
@@ -81,11 +80,13 @@ static int exec_internal(FAR const char *filename,
                          FAR const posix_spawnattr_t *attr, bool spawn)
 {
   FAR struct binary_s *bin;
-  int pid;
-  int ret;
 #ifndef CONFIG_BINFMT_LOADABLE
   struct binary_s sbin;
+#endif
+  int pid;
+  int ret;
 
+#ifndef CONFIG_BINFMT_LOADABLE
   bin = &sbin;
   memset(bin, 0, sizeof(*bin));
 #else
diff --git a/binfmt/binfmt_loadmodule.c b/binfmt/binfmt_loadmodule.c
index 4d5f9416de0..06d56c0c9b5 100644
--- a/binfmt/binfmt_loadmodule.c
+++ b/binfmt/binfmt_loadmodule.c
@@ -134,6 +134,14 @@ static int load_absmodule(FAR struct binary_s *bin, FAR 
const char *filename,
           binfmt_dumpmodule(bin);
           break;
         }
+      else if (ret == -EACCES)
+        {
+          /* Access explicitly denied -- stop here; do not let a fallback
+           * loader bypass the execute-permission check that already ran.
+           */
+
+          break;
+        }
     }
 
   return ret;
diff --git a/binfmt/builtin.c b/binfmt/builtin.c
index b2116806f46..5ec818b3dd9 100644
--- a/binfmt/builtin.c
+++ b/binfmt/builtin.c
@@ -34,6 +34,8 @@
 #include <nuttx/binfmt/binfmt.h>
 #include <nuttx/lib/builtin.h>
 
+#include "binfmt.h"
+
 #ifdef CONFIG_BUILTIN
 
 /****************************************************************************
@@ -76,6 +78,9 @@ static int builtin_loadbinary(FAR struct binary_s *binp,
   FAR const struct builtin_s *builtin;
   FAR char *name;
   int index;
+#ifdef CONFIG_SCHED_USER_IDENTITY
+  int chk;
+#endif
 
   binfo("Loading file: %s\n", filename);
 
@@ -105,14 +110,21 @@ static int builtin_loadbinary(FAR struct binary_s *binp,
       return -ENOENT;
     }
 
+#ifdef CONFIG_SCHED_USER_IDENTITY
+  binp->uid  = builtin->uid;
+  binp->gid  = builtin->gid;
+  binp->mode = builtin->mode;
+
+  chk = binfmt_checkexecperm(binp);
+  if (chk < 0)
+    {
+      return chk;
+    }
+#endif
+
   binp->entrypt   = builtin->main;
   binp->stacksize = builtin->stacksize;
   binp->priority  = builtin->priority;
-#ifdef CONFIG_SCHED_USER_IDENTITY
-  binp->uid       = builtin->uid;
-  binp->gid       = builtin->gid;
-  binp->mode      = builtin->mode;
-#endif
 
   return OK;
 }
diff --git a/binfmt/elf.c b/binfmt/elf.c
index 087c73589ae..cfe146cb26e 100644
--- a/binfmt/elf.c
+++ b/binfmt/elf.c
@@ -37,6 +37,8 @@
 #include <nuttx/binfmt/binfmt.h>
 #include <nuttx/kmalloc.h>
 
+#include "binfmt.h"
+
 #ifdef CONFIG_ELF
 
 /****************************************************************************
@@ -110,6 +112,20 @@ static int elf_loadbinary(FAR struct binary_s *binp,
       goto errout_with_init;
     }
 
+#ifdef CONFIG_SCHED_USER_IDENTITY
+  /* Save IDs and mode from file system before loading segments */
+
+  binp->uid  = loadinfo.fileuid;
+  binp->gid  = loadinfo.filegid;
+  binp->mode = loadinfo.filemode;
+
+  ret = binfmt_checkexecperm(binp);
+  if (ret < 0)
+    {
+      goto errout_with_init;
+    }
+#endif
+
   /* Load the program binary */
 
   ret = libelf_load_with_addrenv(&loadinfo);
@@ -201,14 +217,6 @@ static int elf_loadbinary(FAR struct binary_s *binp,
   binp->mod.nfini   = loadinfo.nfini;
 #endif
 
-#ifdef CONFIG_SCHED_USER_IDENTITY
-  /* Save IDs and mode from file system */
-
-  binp->uid  = loadinfo.fileuid;
-  binp->gid  = loadinfo.filegid;
-  binp->mode = loadinfo.filemode;
-#endif
-
   libelf_dumpentrypt(&loadinfo);
 #ifdef CONFIG_PIC
   if (loadinfo.gotindex >= 0)
diff --git a/binfmt/nxflat.c b/binfmt/nxflat.c
index 010cf42546a..f390e5081f5 100644
--- a/binfmt/nxflat.c
+++ b/binfmt/nxflat.c
@@ -27,6 +27,7 @@
 #include <nuttx/config.h>
 
 #include <sys/param.h>
+#include <sys/stat.h>
 #include <sys/types.h>
 #include <stdint.h>
 #include <string.h>
@@ -36,10 +37,13 @@
 
 #include <arpa/inet.h>
 
+#include <nuttx/fs/fs.h>
 #include <nuttx/kmalloc.h>
 #include <nuttx/binfmt/binfmt.h>
 #include <nuttx/binfmt/nxflat.h>
 
+#include "binfmt.h"
+
 #ifdef CONFIG_NXFLAT
 
 /****************************************************************************
@@ -142,6 +146,9 @@ static int nxflat_loadbinary(FAR struct binary_s *binp,
                              int nexports)
 {
   struct nxflat_loadinfo_s loadinfo;  /* Contains globals for libnxflat */
+#ifdef CONFIG_SCHED_USER_IDENTITY
+  struct stat              st;
+#endif
   int                      ret;
 
   binfo("Loading file: %s\n", filename);
@@ -156,6 +163,25 @@ static int nxflat_loadbinary(FAR struct binary_s *binp,
       goto errout;
     }
 
+#ifdef CONFIG_SCHED_USER_IDENTITY
+  ret = file_fstat(&loadinfo.file, &st);
+  if (ret < 0)
+    {
+      berr("Failed to stat NXFLAT program binary: %d\n", ret);
+      goto errout_with_init;
+    }
+
+  binp->uid  = st.st_uid;
+  binp->gid  = st.st_gid;
+  binp->mode = st.st_mode;
+
+  ret = binfmt_checkexecperm(binp);
+  if (ret < 0)
+    {
+      goto errout_with_init;
+    }
+#endif
+
   /* Load the program binary */
 
   ret = nxflat_load(&loadinfo);

Reply via email to