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 702fd85acce fs/vfs: enforce pseudoFS permissions on open()
702fd85acce is described below

commit 702fd85acce62845f3e981caa3b833269babbea3
Author: Abhishek Mishra <[email protected]>
AuthorDate: Sun May 17 01:04:43 2026 +0000

    fs/vfs: enforce pseudoFS permissions on open()
    
    Add inode_checkperm() and integrate it into file_vopen()
    to enforce UNIX-style read/write permission checks for
    pseudoFS inodes using effective uid/gid credentials.
    
    Skip permission enforcement for mountpoint inodes and
    allow kernel threads to bypass checks.
    
    Signed-off-by: Abhishek Mishra <[email protected]>
---
 fs/inode/fs_inode.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 fs/inode/inode.h    |  30 ++++++++++++
 fs/vfs/fs_open.c    |  52 +--------------------
 3 files changed, 161 insertions(+), 50 deletions(-)

diff --git a/fs/inode/fs_inode.c b/fs/inode/fs_inode.c
index b957a8ccecd..32dee8ff432 100644
--- a/fs/inode/fs_inode.c
+++ b/fs/inode/fs_inode.c
@@ -24,8 +24,13 @@
  * Included Files
  ****************************************************************************/
 
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+
 #include <nuttx/fs/fs.h>
 #include <nuttx/rwsem.h>
+#include <nuttx/sched.h>
 
 #include "inode/inode.h"
 
@@ -114,3 +119,127 @@ void inode_runlock(void)
 {
   up_read(&g_inode_lock);
 }
+
+/****************************************************************************
+ * Name: inode_checkperm
+ *
+ * Description:
+ *   Validate that 'inode' can be opened with the access described by
+ *   'oflags'.  Two sequential checks are performed:
+ *
+ *   1. Operation-support check (all inode types, unconditional):
+ *      Verifies the driver exposes the read/write entry points required by
+ *      'oflags'.  Returns -ENXIO when ops are NULL and -EACCES when the
+ *      required entry point is absent.  Pseudo-directory inodes
+ *      (INODE_IS_PSEUDODIR) are exempted from this step.
+ *
+ *   2. UNIX permission check (pseudo-filesystem inodes only):
+ *      Compares effective uid/gid against i_mode owner/group/other bits.
+ *      Mountpoint inodes and kernel threads are unconditionally exempted.
+ *      Requires CONFIG_PSEUDOFS_ATTRIBUTES and CONFIG_SCHED_USER_IDENTITY;
+ *      when either option is disabled this step is a no-op.
+ *
+ * Input Parameters:
+ *   inode  - The inode to check
+ *   oflags - Open flags (O_RDONLY / O_WRONLY / O_RDWR)
+ *
+ * Returned Value:
+ *   Zero (OK) on success.  Negated errno on failure:
+ *     -ENXIO   ops pointer is NULL
+ *     -EACCES  required operation not supported, or permission denied
+ *
+ ****************************************************************************/
+
+int inode_checkperm(FAR struct inode *inode, int oflags)
+{
+#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
+  FAR struct tcb_s *rtcb;
+  mode_t perm;
+  uid_t uid;
+  gid_t gid;
+#endif
+  FAR const struct file_operations *ops;
+
+  /* === Step 1: operation-support check === */
+
+  /* Pseudo-directories carry no ops and are always accessible */
+
+  if (INODE_IS_PSEUDODIR(inode))
+    {
+      return OK;
+    }
+
+  ops = inode->u.i_ops;
+  if (ops == NULL)
+    {
+      return -ENXIO;
+    }
+
+  if (((oflags & O_RDOK) != 0 &&
+       !ops->readv && !ops->read && !ops->ioctl) ||
+      ((oflags & O_WROK) != 0 &&
+       !ops->writev && !ops->write && !ops->ioctl))
+    {
+      return -EACCES;
+    }
+
+#if defined(CONFIG_PSEUDOFS_ATTRIBUTES) && defined(CONFIG_SCHED_USER_IDENTITY)
+
+  /* === Step 2: UNIX permission check (pseudo-filesystem inodes only) === */
+
+  /* Mountpoints delegate permission enforcement to the underlying
+   * filesystem
+   */
+
+  if (INODE_IS_MOUNTPT(inode))
+    {
+      return OK;
+    }
+
+  /* Kernel threads are always granted access */
+
+  rtcb = nxsched_self();
+  if ((rtcb->flags & TCB_FLAG_TTYPE_MASK) == TCB_FLAG_TTYPE_KERNEL)
+    {
+      return OK;
+    }
+
+  /* Use effective credentials */
+
+  DEBUGASSERT(rtcb->group != NULL);
+  uid = rtcb->group->tg_euid;
+  gid = rtcb->group->tg_egid;
+
+  /* Select the applicable permission-bit triplet */
+
+  if (uid == inode->i_owner)
+    {
+      perm = (inode->i_mode >> 6) & 7;
+    }
+  else if (gid == inode->i_group)
+    {
+      perm = (inode->i_mode >> 3) & 7;
+    }
+  else
+    {
+      perm = inode->i_mode & 7;
+    }
+
+  /* Bit 2 (value 4) = read permission */
+
+  if (((oflags & O_RDOK) != 0) && ((perm & 4) == 0))
+    {
+      return -EACCES;
+    }
+
+  /* Bit 1 (value 2) = write permission */
+
+  if (((oflags & O_WROK) != 0) && ((perm & 2) == 0))
+    {
+      return -EACCES;
+    }
+
+#endif /* CONFIG_PSEUDOFS_ATTRIBUTES && CONFIG_SCHED_USER_IDENTITY */
+
+  return OK;
+}
diff --git a/fs/inode/inode.h b/fs/inode/inode.h
index 638959ff8fd..8eccad2d367 100644
--- a/fs/inode/inode.h
+++ b/fs/inode/inode.h
@@ -418,6 +418,36 @@ void inode_addref(FAR struct inode *inode);
 
 void inode_release(FAR struct inode *inode);
 
+/****************************************************************************
+ * Name: inode_checkperm
+ *
+ * Description:
+ *   Validate that 'inode' can be opened with the access described by
+ *   'oflags'.  Two sequential checks are performed:
+ *
+ *   1. Operation-support check (all inode types):
+ *      Ensures the driver exposes the read/write entry points required by
+ *      'oflags'.  Pseudo-directory inodes are exempted.
+ *
+ *   2. UNIX permission check (pseudo-filesystem inodes only):
+ *      Compares effective uid/gid against i_mode owner/group/other bits.
+ *      Mountpoint inodes and kernel threads are unconditionally exempted.
+ *      Active only when CONFIG_PSEUDOFS_ATTRIBUTES and
+ *      CONFIG_SCHED_USER_IDENTITY are both enabled.
+ *
+ * Input Parameters:
+ *   inode  - The inode to check
+ *   oflags - Open flags (O_RDONLY / O_WRONLY / O_RDWR)
+ *
+ * Returned Value:
+ *   Zero (OK) on success.  Negated errno on failure:
+ *     -ENXIO   ops pointer is NULL
+ *     -EACCES  required operation not supported, or permission denied
+ *
+ ****************************************************************************/
+
+int inode_checkperm(FAR struct inode *inode, int oflags);
+
 /****************************************************************************
  * Name: foreach_inode
  *
diff --git a/fs/vfs/fs_open.c b/fs/vfs/fs_open.c
index 65b8a344b0c..4131d14b36e 100644
--- a/fs/vfs/fs_open.c
+++ b/fs/vfs/fs_open.c
@@ -48,54 +48,6 @@
  * Private Functions
  ****************************************************************************/
 
-/****************************************************************************
- * Name: inode_checkflags
- *
- * Description:
- *   Check if the access described by 'oflags' is supported on 'inode'
- *
- *   inode_checkflags() is an internal NuttX interface and should not be
- *   called from applications.
- *
- * Input Parameters:
- *   inode  - The inode to check
- *   oflags - open flags.
- *
- * Returned Value:
- *   Zero (OK) is returned on success.  On failure, a negated errno value is
- *   returned.
- *
- ****************************************************************************/
-
-static int inode_checkflags(FAR struct inode *inode, int oflags)
-{
-  FAR const struct file_operations *ops = inode->u.i_ops;
-
-  if (INODE_IS_PSEUDODIR(inode))
-    {
-      return OK;
-    }
-
-  if (ops == NULL)
-    {
-      return -ENXIO;
-    }
-
-  if (((oflags & O_RDOK) != 0 && !ops->readv && !ops->read && !ops->ioctl) ||
-      ((oflags & O_WROK) != 0 && !ops->writev && !ops->write && !ops->ioctl))
-    {
-      return -EACCES;
-    }
-  else
-    {
-      return OK;
-    }
-}
-
-/****************************************************************************
- * Name: file_vopen
- ****************************************************************************/
-
 /****************************************************************************
  * Name: file_vopen
  *
@@ -218,9 +170,9 @@ static int file_vopen(FAR struct file *filep, FAR const 
char *path,
     }
 #endif
 
-  /* Make sure that the inode supports the requested access */
+  /* Validate operation support and pseudo-filesystem permissions */
 
-  ret = inode_checkflags(inode, oflags);
+  ret = inode_checkperm(inode, oflags);
   if (ret < 0)
     {
       goto errout_with_inode;

Reply via email to