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


The following commit(s) were added to refs/heads/master by this push:
     new db6ab892a24 fs/binfmt: close symlink TOCTOU and harden setuid/setgid 
exec hygiene
db6ab892a24 is described below

commit db6ab892a24f99a8b52d99963f16d54dd89294d7
Author: Abhishek Mishra <[email protected]>
AuthorDate: Sat Aug 1 13:02:19 2026 +0000

    fs/binfmt: close symlink TOCTOU and harden setuid/setgid exec hygiene
    
    Perform pseudo-filesystem permission checks inside inode_reserve() and
    inode_remove() while the inode tree lock is held, and hold that lock across
    pseudorename mutations so symlink swaps cannot bypass directory checks.
    Hold a read lock around pseudo-fs open permission checks.
    
    On setuid/setgid exec, update saved set-IDs, mark the task group secure,
    sanitize dangerous environment variables, clear debug/dumpable flags, and
    add issetugid(), secure_getenv(), and PR_SET/GET_DUMPABLE support.
    
    Signed-off-by: Abhishek Mishra <[email protected]>
---
 binfmt/binfmt_execmodule.c       |  28 +++++++--
 fs/inode/fs_inoderemove.c        |  12 ++++
 fs/inode/fs_inodereserve.c       |  11 ++++
 fs/inode/inode.h                 |   5 ++
 fs/vfs/fs_mkdir.c                |  16 ++---
 fs/vfs/fs_open.c                 |  14 ++++-
 fs/vfs/fs_rename.c               |  77 ++++++++++--------------
 fs/vfs/fs_unlink.c               |   8 ---
 include/nuttx/sched.h            |   4 +-
 include/stdlib.h                 |   1 +
 libs/libc/unistd/CMakeLists.txt  |   3 +-
 libs/libc/unistd/Make.defs       |   1 +
 libs/libc/unistd/lib_issetugid.c |  46 ++++++++++++++
 sched/environ/CMakeLists.txt     |   3 +-
 sched/environ/Make.defs          |   2 +-
 sched/environ/env_secureexec.c   | 126 +++++++++++++++++++++++++++++++++++++++
 sched/environ/environ.h          |  13 ++++
 sched/group/CMakeLists.txt       |   3 +-
 sched/group/Make.defs            |   1 +
 sched/group/group_create.c       |   2 +
 sched/group/group_issetugid.c    |  53 ++++++++++++++++
 sched/sched/sched.h              |   2 +
 sched/task/task_prctl.c          |  40 +++++++++++++
 23 files changed, 392 insertions(+), 79 deletions(-)

diff --git a/binfmt/binfmt_execmodule.c b/binfmt/binfmt_execmodule.c
index 653632bc025..867091c61a0 100644
--- a/binfmt/binfmt_execmodule.c
+++ b/binfmt/binfmt_execmodule.c
@@ -45,6 +45,7 @@
 #include <nuttx/binfmt/binfmt.h>
 
 #include "binfmt.h"
+#include "environ/environ.h"
 
 #ifndef CONFIG_BINFMT_DISABLE
 
@@ -301,14 +302,29 @@ int exec_module(FAR struct binary_s *binp,
 #endif
 
 #ifdef CONFIG_SCHED_USER_IDENTITY
-  if (binp->mode & S_ISUID)
-    {
-      tcb->group->tg_euid = binp->uid;
-    }
+  /* Apply set-user-ID / set-group-ID credentials for the new image. */
 
-  if (binp->mode & S_ISGID)
+  if ((binp->mode & (S_ISUID | S_ISGID)) != 0)
     {
-      tcb->group->tg_egid = binp->gid;
+      FAR struct task_group_s *group = tcb->group;
+
+      if ((binp->mode & S_ISUID) != 0)
+        {
+          group->tg_euid = binp->uid;
+          group->tg_suid = binp->uid;
+        }
+
+      if ((binp->mode & S_ISGID) != 0)
+        {
+          group->tg_egid = binp->gid;
+          group->tg_sgid = binp->gid;
+        }
+
+      group->tg_flags |= GROUP_FLAG_SECURE_EXEC;
+      group->tg_flags &= ~(GROUP_FLAG_FD_BACKTRACE | GROUP_FLAG_DUMPABLE);
+#ifndef CONFIG_DISABLE_ENVIRON
+      env_sanitize_secure(group);
+#endif
     }
 #endif
 
diff --git a/fs/inode/fs_inoderemove.c b/fs/inode/fs_inoderemove.c
index 87c6d63339d..5c41948e896 100644
--- a/fs/inode/fs_inoderemove.c
+++ b/fs/inode/fs_inoderemove.c
@@ -79,6 +79,18 @@ static FAR struct inode *inode_unlink(FAR const char *path)
       inode = desc.node;
       DEBUGASSERT(inode != NULL);
 
+#ifdef CONFIG_FS_PERMISSION
+      if (desc.parent != NULL)
+        {
+          ret = inode_checkperm(desc.parent, W_OK);
+          if (ret < 0)
+            {
+              inode = NULL;
+              goto errout;
+            }
+        }
+#endif
+
       /* If peer is non-null, then remove the node from the right of
        * of that peer node.
        */
diff --git a/fs/inode/fs_inodereserve.c b/fs/inode/fs_inodereserve.c
index e255f3f3753..f3b3e3b7f12 100644
--- a/fs/inode/fs_inodereserve.c
+++ b/fs/inode/fs_inodereserve.c
@@ -226,6 +226,17 @@ int inode_reserve(FAR const char *path,
   left   = desc.peer;
   parent = desc.parent;
 
+#ifdef CONFIG_FS_PERMISSION
+  if (parent != NULL)
+    {
+      ret = inode_checkperm(parent, W_OK | X_OK);
+      if (ret < 0)
+        {
+          goto errout_with_search;
+        }
+    }
+#endif
+
   for (; ; )
     {
       FAR struct inode *node;
diff --git a/fs/inode/inode.h b/fs/inode/inode.h
index 7cacd0697f3..74d9ef89c00 100644
--- a/fs/inode/inode.h
+++ b/fs/inode/inode.h
@@ -374,6 +374,7 @@ void inode_root_reserve(void);
  *
  *   EINVAL - 'path' is invalid for this operation
  *   EEXIST - An inode already exists at 'path'
+ *   EACCES - Caller lacks permission on the parent directory
  *   ENOMEM - Failed to allocate in-memory resources for the operation
  *
  ****************************************************************************/
@@ -390,6 +391,10 @@ int inode_reserve(FAR const char *path,
  *   inode is in-use, then it will be unlinked, but will not be freed until
  *   the last reference to the inode is released.
  *
+ * Returned Value:
+ *   Zero (OK) on success; a negated errno value on failure, including
+ *   -EACCES if the caller lacks write permission on the parent directory.
+ *
  * Assumptions/Limitations:
  *   The caller must hold the inode semaphore
  *
diff --git a/fs/vfs/fs_mkdir.c b/fs/vfs/fs_mkdir.c
index 65b0fdb1f1b..253be08f27a 100644
--- a/fs/vfs/fs_mkdir.c
+++ b/fs/vfs/fs_mkdir.c
@@ -137,19 +137,11 @@ int mkdir(const char *pathname, mode_t mode)
 
   else
     {
-      /* Verify write+search permission on the parent directory before
-       * adding a new name to the pseudo-filesystem tree.  POSIX requires
-       * both W_OK and X_OK to create a directory entry.
-       */
-
-      ret = inode_checkperm(desc.parent, W_OK | X_OK);
-      if (ret < 0)
-        {
-          errcode = -ret;
-          goto errout_with_search;
-        }
-
       /* Create an inode in the pseudo-filesystem at this path.
+       * inode_reserve() resolves the path and checks parent-directory
+       * permissions while the inode tree lock is held, closing symlink
+       * TOCTOU windows.
+       *
        * NOTE that the new inode will be created with a reference
        * count of zero.
        */
diff --git a/fs/vfs/fs_open.c b/fs/vfs/fs_open.c
index b08f7909221..0959527defa 100644
--- a/fs/vfs/fs_open.c
+++ b/fs/vfs/fs_open.c
@@ -172,7 +172,19 @@ static int file_vopen(FAR struct file *filep, FAR const 
char *path,
 
   /* Validate operation support and pseudo-filesystem permissions */
 
-  ret = inode_checkopenperm(inode, oflags);
+#ifndef CONFIG_DISABLE_MOUNTPOINT
+  if (INODE_IS_MOUNTPT(inode))
+    {
+      ret = inode_checkopenperm(inode, oflags);
+    }
+  else
+#endif
+    {
+      inode_rlock();
+      ret = inode_checkopenperm(inode, oflags);
+      inode_runlock();
+    }
+
   if (ret < 0)
     {
       goto errout_with_inode;
diff --git a/fs/vfs/fs_rename.c b/fs/vfs/fs_rename.c
index 8d9840aeb4a..99c66279e77 100644
--- a/fs/vfs/fs_rename.c
+++ b/fs/vfs/fs_rename.c
@@ -71,32 +71,34 @@ static int pseudorename(FAR const char *oldpath, FAR struct 
inode *oldinode,
                         FAR const char *newpath)
 {
   struct inode_search_s newdesc;
-  struct inode_search_s pardesc;
+  struct inode_search_s olddesc;
   FAR struct inode *newinode;
-  FAR struct inode *parnode;
   FAR char *subdir = NULL;
 #ifdef CONFIG_FS_NOTIFY
   bool isdir = INODE_IS_PSEUDODIR(oldinode);
 #endif
   int ret;
 
-  /* SETUP_SEARCH early so RELEASE_SEARCH at errout is safe. */
+  /* Hold the inode tree lock across resolve / permission checks / mutate
+   * so symbolic links cannot be swapped between check and use.
+   * Destination parent permissions are enforced by inode_reserve().
+   */
 
-  SETUP_SEARCH(&newdesc, newpath, true);
+  inode_lock();
 
-  /* Verify source parent write permission. */
+  SETUP_SEARCH(&newdesc, newpath, true);
 
   ret = inode_checkperm(oldparent, W_OK);
   if (ret < 0)
     {
-      goto errout;
+      goto errout_with_lock;
     }
 
   /* According to POSIX, any new inode at this path should be removed
    * first, provided that it is not a directory.
    */
 
-  ret = inode_find(&newdesc);
+  ret = inode_search(&newdesc);
   if (ret >= 0)
     {
       /* We found it.  Get the search results */
@@ -110,9 +112,8 @@ static int pseudorename(FAR const char *oldpath, FAR struct 
inode *oldinode,
 
       if (oldinode == newinode)
         {
-          inode_release(newinode);
           ret = OK;
-          goto errout; /* Same name, this is not an error case. */
+          goto errout_with_lock;
         }
 
 #ifndef CONFIG_DISABLE_MOUNTPOINT
@@ -120,9 +121,8 @@ static int pseudorename(FAR const char *oldpath, FAR struct 
inode *oldinode,
 
       if (INODE_IS_MOUNTPT(newinode))
         {
-          inode_release(newinode);
           ret = -EXDEV;
-          goto errout;
+          goto errout_with_lock;
         }
 #endif
 
@@ -145,7 +145,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct 
inode *oldinode,
             {
               subdir = NULL;
               ret = -ENOMEM;
-              goto errout;
+              goto errout_with_lock;
             }
 
           newpath = subdir;
@@ -162,37 +162,16 @@ static int pseudorename(FAR const char *oldpath, FAR 
struct inode *oldinode,
            * won't really be removed until we call inode_release();
            */
 
-          inode_remove(newpath);
+          ret = inode_remove(newpath);
+          if (ret < 0 && ret != -EBUSY)
+            {
+              goto errout_with_lock;
+            }
+
 #ifdef CONFIG_FS_NOTIFY
           notify_unlink(newpath);
 #endif
         }
-
-      inode_release(newinode);
-    }
-
-  /* Re-resolve the final destination parent after path rewrite. */
-
-  SETUP_SEARCH(&pardesc, newpath, true);
-  inode_find(&pardesc);   /* pardesc.parent valid even if node not found */
-  parnode = pardesc.node;
-
-  ret = inode_checkperm(pardesc.parent, W_OK);
-
-  /* inode_find() holds a reference on parnode; RELEASE_SEARCH() only
-   * frees pardesc.buffer.
-   */
-
-  if (parnode != NULL)
-    {
-      inode_release(parnode);
-    }
-
-  RELEASE_SEARCH(&pardesc);
-
-  if (ret < 0)
-    {
-      goto errout;
     }
 
   /* Create a new, empty inode at the destination location.
@@ -200,16 +179,21 @@ static int pseudorename(FAR const char *oldpath, FAR 
struct inode *oldinode,
    * of  zero.
    */
 
-  inode_lock();
   ret = inode_reserve(newpath, 0777, &newinode);
   if (ret < 0)
     {
-      /* It is an error if a node at newpath already exists in the tree
-       * OR if we fail to allocate memory for the new inode (and possibly
-       * any new intermediate path segments).
-       */
+      goto errout_with_lock;
+    }
+
+  /* Re-resolve the source under the same lock before unlinking it. */
 
-      ret = -EEXIST;
+  SETUP_SEARCH(&olddesc, oldpath, true);
+  ret = inode_search(&olddesc);
+  RELEASE_SEARCH(&olddesc);
+  if (ret < 0 || olddesc.node != oldinode)
+    {
+      inode_remove(newpath);
+      ret = -ENOENT;
       goto errout_with_lock;
     }
 
@@ -265,6 +249,7 @@ static int pseudorename(FAR const char *oldpath, FAR struct 
inode *oldinode,
   ret = OK;
 
 errout_with_lock:
+  RELEASE_SEARCH(&newdesc);
   inode_unlock();
 
 #ifdef CONFIG_FS_NOTIFY
@@ -274,8 +259,6 @@ errout_with_lock:
     }
 #endif
 
-errout:
-  RELEASE_SEARCH(&newdesc);
   if (subdir != NULL)
     {
       fs_heap_free(subdir);
diff --git a/fs/vfs/fs_unlink.c b/fs/vfs/fs_unlink.c
index 7f4d3ffaadf..8726610a260 100644
--- a/fs/vfs/fs_unlink.c
+++ b/fs/vfs/fs_unlink.c
@@ -121,14 +121,6 @@ int nx_unlink(FAR const char *pathname)
           goto errout_with_inode;
         }
 
-      /* Verify parent-directory write permission before unlink. */
-
-      ret = inode_checkperm(desc.parent, W_OK);
-      if (ret < 0)
-        {
-          goto errout_with_inode;
-        }
-
       /* Notify the driver that it has been unlinked.  If there are no
        * open references to the driver instance, then the driver should
        * release all resources because it is no longer accessible.
diff --git a/include/nuttx/sched.h b/include/nuttx/sched.h
index 76630d3f101..44addd4d57e 100644
--- a/include/nuttx/sched.h
+++ b/include/nuttx/sched.h
@@ -118,7 +118,9 @@
 #define GROUP_FLAG_DELETED         (1 << 2)                      /* Bit 2: 
Group has been deleted but not yet freed */
 #define GROUP_FLAG_EXITING         (1 << 3)                      /* Bit 3: 
Group exit is in progress */
 #define GROUP_FLAG_FD_BACKTRACE    (1 << 4)                      /* Bit 4: 
Enable FD backtrace for the group */
-                                                                 /* Bits 5-7: 
Available */
+#define GROUP_FLAG_SECURE_EXEC     (1 << 5)                      /* Bit 5: 
Secure (setuid/setgid) executable */
+#define GROUP_FLAG_DUMPABLE        (1 << 6)                      /* Bit 6: 
Process may be traced / coredumped */
+                                                                 /* Bit 7: 
Available */
 
 /* Values for struct child_status_s ch_flags */
 
diff --git a/include/stdlib.h b/include/stdlib.h
index ca93bb4ca08..06a56128258 100644
--- a/include/stdlib.h
+++ b/include/stdlib.h
@@ -173,6 +173,7 @@ uint32_t  arc4random(void);
 
 FAR char **get_environ_ptr(void);
 FAR char *getenv(FAR const char *name);
+FAR char *secure_getenv(FAR const char *name);
 int       putenv(FAR const char *string);
 int       clearenv(void);
 int       setenv(FAR const char *name, FAR const char *value, int overwrite);
diff --git a/libs/libc/unistd/CMakeLists.txt b/libs/libc/unistd/CMakeLists.txt
index 3521d45f607..e57f69c07f1 100644
--- a/libs/libc/unistd/CMakeLists.txt
+++ b/libs/libc/unistd/CMakeLists.txt
@@ -89,7 +89,8 @@ if(NOT CONFIG_SCHED_USER_IDENTITY)
     lib_setreuid.c
     lib_setregid.c
     lib_getresuid.c
-    lib_getresgid.c)
+    lib_getresgid.c
+    lib_issetugid.c)
 endif()
 
 if(NOT CONFIG_DISABLE_ENVIRON)
diff --git a/libs/libc/unistd/Make.defs b/libs/libc/unistd/Make.defs
index 602887be4c3..65e1e7e5f04 100644
--- a/libs/libc/unistd/Make.defs
+++ b/libs/libc/unistd/Make.defs
@@ -40,6 +40,7 @@ ifneq ($(CONFIG_SCHED_USER_IDENTITY),y)
 CSRCS += lib_setuid.c lib_setgid.c lib_getuid.c lib_getgid.c
 CSRCS += lib_seteuid.c lib_setegid.c lib_geteuid.c lib_getegid.c
 CSRCS += lib_setreuid.c lib_setregid.c lib_getresuid.c lib_getresgid.c
+CSRCS += lib_issetugid.c
 endif
 
 ifneq ($(CONFIG_DISABLE_ENVIRON),y)
diff --git a/libs/libc/unistd/lib_issetugid.c b/libs/libc/unistd/lib_issetugid.c
new file mode 100644
index 00000000000..d99caa0c0bc
--- /dev/null
+++ b/libs/libc/unistd/lib_issetugid.c
@@ -0,0 +1,46 @@
+/****************************************************************************
+ * libs/libc/unistd/lib_issetugid.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>
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: issetugid
+ *
+ * Description:
+ *   Stub version always reports that the process is not executing a secure
+ *   set-user-ID or set-group-ID image.  When CONFIG_SCHED_USER_IDENTITY is
+ *   enabled, sched/group/group_issetugid.c provides the real version.
+ *
+ ****************************************************************************/
+
+int issetugid(void)
+{
+  return 0;
+}
diff --git a/sched/environ/CMakeLists.txt b/sched/environ/CMakeLists.txt
index 5e891a24ce4..dafb5d1fece 100644
--- a/sched/environ/CMakeLists.txt
+++ b/sched/environ/CMakeLists.txt
@@ -33,5 +33,6 @@ if(NOT CONFIG_DISABLE_ENVIRON)
             env_putenv.c
             env_setenv.c
             env_unsetenv.c
-            env_foreach.c)
+            env_foreach.c
+            env_secureexec.c)
 endif()
diff --git a/sched/environ/Make.defs b/sched/environ/Make.defs
index cdd62cf7288..28e3d187171 100644
--- a/sched/environ/Make.defs
+++ b/sched/environ/Make.defs
@@ -24,7 +24,7 @@ ifneq ($(CONFIG_DISABLE_ENVIRON),y)
 
 CSRCS += env_getenvironptr.c env_dup.c env_release.c env_findvar.c
 CSRCS += env_removevar.c env_clearenv.c env_getenv.c env_putenv.c
-CSRCS += env_setenv.c env_unsetenv.c env_foreach.c
+CSRCS += env_setenv.c env_unsetenv.c env_foreach.c env_secureexec.c
 
 # Include environ build support
 
diff --git a/sched/environ/env_secureexec.c b/sched/environ/env_secureexec.c
new file mode 100644
index 00000000000..b886bca1443
--- /dev/null
+++ b/sched/environ/env_secureexec.c
@@ -0,0 +1,126 @@
+/****************************************************************************
+ * sched/environ/env_secureexec.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 <stdbool.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "sched/sched.h"
+#include "environ/environ.h"
+
+/****************************************************************************
+ * Private Functions
+ ****************************************************************************/
+
+#ifdef CONFIG_SCHED_USER_IDENTITY
+
+/****************************************************************************
+ * Name: env_is_unsafe
+ *
+ * Description:
+ *   Return true if the "NAME=value" pair must not be inherited by a secure
+ *   (set-user-ID or set-group-ID) executable.
+ *
+ ****************************************************************************/
+
+static bool env_is_unsafe(FAR const char *pair)
+{
+  static FAR const char * const names[] =
+    {
+      "IFS=",
+      "CDPATH=",
+      "ENV=",
+      "BASH_ENV=",
+      "PATH=",
+      "HOSTALIASES=",
+      "LOCPATH=",
+      "GCONV_PATH=",
+      "TZDIR=",
+      "LOCALDOMAIN=",
+      "NLSPATH=",
+      NULL
+    };
+
+  FAR const char * const *name;
+
+  for (name = names; *name != NULL; name++)
+    {
+      if (strncmp(pair, *name, strlen(*name)) == 0)
+        {
+          return true;
+        }
+    }
+
+  return strncmp(pair, "LD_", 3) == 0 ||
+         strncmp(pair, "MALLOC_", 7) == 0;
+}
+
+#endif /* CONFIG_SCHED_USER_IDENTITY */
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: env_sanitize_secure
+ ****************************************************************************/
+
+#ifdef CONFIG_SCHED_USER_IDENTITY
+void env_sanitize_secure(FAR struct task_group_s *group)
+{
+  ssize_t index;
+
+  DEBUGASSERT(group != NULL);
+
+  nxrmutex_lock(&group->tg_mutex);
+
+  for (index = group->tg_envc - 1; index >= 0; )
+    {
+      if (env_is_unsafe(group->tg_envp[index]))
+        {
+          env_removevar(group, index);
+        }
+      else
+        {
+          index--;
+        }
+    }
+
+  nxrmutex_unlock(&group->tg_mutex);
+}
+#endif
+
+/****************************************************************************
+ * Name: secure_getenv
+ ****************************************************************************/
+
+FAR char *secure_getenv(FAR const char *name)
+{
+  return issetugid() ? NULL : getenv(name);
+}
diff --git a/sched/environ/environ.h b/sched/environ/environ.h
index 939b0f32d36..31ad20e83b8 100644
--- a/sched/environ/environ.h
+++ b/sched/environ/environ.h
@@ -104,6 +104,19 @@ int env_dup(FAR struct task_group_s *group, FAR char * 
const *envp);
 
 void env_release(FAR struct task_group_s *group);
 
+#ifdef CONFIG_SCHED_USER_IDENTITY
+/****************************************************************************
+ * Name: env_sanitize_secure
+ *
+ * Description:
+ *   Remove environment variables that must not be inherited by a secure
+ *   (set-user-ID or set-group-ID) executable.
+ *
+ ****************************************************************************/
+
+void env_sanitize_secure(FAR struct task_group_s *group);
+#endif
+
 /****************************************************************************
  * Name: env_findvar
  *
diff --git a/sched/group/CMakeLists.txt b/sched/group/CMakeLists.txt
index 736b50dcd58..42f21d504bc 100644
--- a/sched/group/CMakeLists.txt
+++ b/sched/group/CMakeLists.txt
@@ -56,7 +56,8 @@ if(CONFIG_SCHED_USER_IDENTITY)
     group_setreuid.c
     group_setregid.c
     group_getresuid.c
-    group_getresgid.c)
+    group_getresgid.c
+    group_issetugid.c)
 endif()
 
 if(CONFIG_SIG_SIGSTOP_ACTION)
diff --git a/sched/group/Make.defs b/sched/group/Make.defs
index 3b822fbf2f5..f505a0d80f5 100644
--- a/sched/group/Make.defs
+++ b/sched/group/Make.defs
@@ -42,6 +42,7 @@ ifeq ($(CONFIG_SCHED_USER_IDENTITY),y)
 CSRCS += group_setuid.c group_setgid.c group_getuid.c group_getgid.c
 CSRCS += group_seteuid.c group_setegid.c group_geteuid.c group_getegid.c
 CSRCS += group_setreuid.c group_setregid.c group_getresuid.c group_getresgid.c
+CSRCS += group_issetugid.c
 endif
 
 ifeq ($(CONFIG_SIG_SIGSTOP_ACTION),y)
diff --git a/sched/group/group_create.c b/sched/group/group_create.c
index 677d818b37c..c7b1af1fd46 100644
--- a/sched/group/group_create.c
+++ b/sched/group/group_create.c
@@ -171,6 +171,8 @@ int group_allocate(FAR struct tcb_s *tcb, uint8_t ttype)
   group->tg_flags |= GROUP_FLAG_FD_BACKTRACE;
 #endif
 
+  group->tg_flags |= GROUP_FLAG_DUMPABLE;
+
   /* Attach the group to the TCB */
 
   tcb->group = group;
diff --git a/sched/group/group_issetugid.c b/sched/group/group_issetugid.c
new file mode 100644
index 00000000000..53074a3cd01
--- /dev/null
+++ b/sched/group/group_issetugid.c
@@ -0,0 +1,53 @@
+/****************************************************************************
+ * sched/group/group_issetugid.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 <assert.h>
+
+#include "sched/sched.h"
+
+/****************************************************************************
+ * Public Functions
+ ****************************************************************************/
+
+/****************************************************************************
+ * Name: issetugid
+ *
+ * Description:
+ *   Return non-zero if the process is executing a set-user-ID or
+ *   set-group-ID program image.
+ *
+ ****************************************************************************/
+
+int issetugid(void)
+{
+  FAR struct tcb_s *rtcb          = this_task();
+  FAR struct task_group_s *rgroup = rtcb->group;
+
+  DEBUGASSERT(rgroup != NULL);
+  return (rgroup->tg_flags & GROUP_FLAG_SECURE_EXEC) != 0;
+}
diff --git a/sched/sched/sched.h b/sched/sched/sched.h
index 8c3aff1d426..62ed2f72ecd 100644
--- a/sched/sched/sched.h
+++ b/sched/sched/sched.h
@@ -307,6 +307,8 @@ extern volatile spinlock_t g_cpu_tasklistlock;
  * Public Function Prototypes
  ****************************************************************************/
 
+int issetugid(void);
+
 int nxthread_create(FAR const char *name, uint8_t ttype, int priority,
                     FAR void *stack_addr, int stack_size, main_t entry,
                     FAR char * const argv[], FAR char * const envp[]);
diff --git a/sched/task/task_prctl.c b/sched/task/task_prctl.c
index 17c015fa637..8b41979e89d 100644
--- a/sched/task/task_prctl.c
+++ b/sched/task/task_prctl.c
@@ -148,6 +148,46 @@ int prctl(int option, ...)
         goto errout;
 #endif
 
+      case PR_SET_DUMPABLE:
+      case PR_GET_DUMPABLE:
+#ifdef CONFIG_SCHED_USER_IDENTITY
+        {
+          FAR struct tcb_s *tcb = this_task();
+
+          if (tcb == NULL || tcb->group == NULL)
+            {
+              errcode = ESRCH;
+              goto errout;
+            }
+
+          if (option == PR_GET_DUMPABLE)
+            {
+              va_end(ap);
+              return (tcb->group->tg_flags & GROUP_FLAG_DUMPABLE) != 0;
+            }
+
+          if (va_arg(ap, int) != 0)
+            {
+              if (tcb->group->tg_euid != 0)
+                {
+                  errcode = EPERM;
+                  goto errout;
+                }
+
+              tcb->group->tg_flags |= GROUP_FLAG_DUMPABLE;
+            }
+          else
+            {
+              tcb->group->tg_flags &= ~GROUP_FLAG_DUMPABLE;
+            }
+        }
+        break;
+#else
+        serr("ERROR: Option not enabled: %d\n", option);
+        errcode = ENOSYS;
+        goto errout;
+#endif
+
       default:
         serr("ERROR: Unrecognized option: %d\n", option);
         errcode = EINVAL;

Reply via email to