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;