security/selinux/selinuxfs.c | 43 ++++++++++++++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-)
From: Christian Göttsche <cgzones@googlemail.com>
Currently the SELinux status map can be truncated, given the necessary
permissions, leading to foreign user space processes getting a bus error
(SIGBUS) while concurrently making use of the status map.
For example systemd can be killed that way, see [1].
Override the setattr inode handler and check for O_TRUNC in the open
handler to prevent truncations.
Link [1]: https://github.com/systemd/systemd/issues/37349
Closes: https://github.com/SELinuxProject/selinux/issues/475
Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
---
security/selinux/selinuxfs.c | 43 ++++++++++++++++++++++++++++++++++--
1 file changed, 41 insertions(+), 2 deletions(-)
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 896acad1f5f7..df079a35a02d 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -214,10 +214,30 @@ static const struct file_operations sel_handle_unknown_ops = {
.llseek = generic_file_llseek,
};
+static int sel_setattr_handle_status(struct mnt_idmap *idmap,
+ struct dentry *dentry,
+ struct iattr *iattr)
+{
+ /* Prevent truncation to avoid raising SIGBUS */
+ if (iattr->ia_valid & ATTR_SIZE)
+ return -EINVAL;
+
+ return simple_setattr(idmap, dentry, iattr);
+}
+
+static const struct inode_operations sel_handle_status_iops = {
+ .setattr = sel_setattr_handle_status,
+};
+
static int sel_open_handle_status(struct inode *inode, struct file *filp)
{
- struct page *status = selinux_kernel_status_page();
+ struct page *status;
+ /* Prevent truncation to avoid raising SIGBUS */
+ if (filp->f_flags & O_TRUNC)
+ return -EINVAL;
+
+ status = selinux_kernel_status_page();
if (!status)
return -ENOMEM;
@@ -1980,7 +2000,6 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
[SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
[SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO},
[SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
- [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO},
[SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
[SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops,
S_IWUGO},
@@ -1995,6 +2014,26 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
if (ret)
goto err;
+ /* Create "status" separately to assign a custom inode_operations */
+ {
+ ret = -ENOMEM;
+
+ dentry = d_alloc_name(sb->s_root, "status");
+ if (!dentry)
+ goto err;
+ inode = new_inode(sb);
+ if (!inode) {
+ dput(dentry);
+ goto err;
+ }
+ inode->i_mode = S_IFREG | 0444;
+ simple_inode_init_ts(inode);
+ inode->i_fop = &sel_handle_status_ops;
+ inode->i_op = &sel_handle_status_iops;
+ inode->i_ino = SEL_STATUS;
+ d_add(dentry, inode);
+ }
+
fsi = sb->s_fs_info;
fsi->bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &fsi->last_ino);
if (IS_ERR(fsi->bool_dir)) {
--
2.51.0
On Fri, Jan 30, 2026 at 12:11 PM Christian Göttsche
<cgoettsche@seltendoof.de> wrote:
>
> From: Christian Göttsche <cgzones@googlemail.com>
>
> Currently the SELinux status map can be truncated, given the necessary
> permissions, leading to foreign user space processes getting a bus error
> (SIGBUS) while concurrently making use of the status map.
> For example systemd can be killed that way, see [1].
>
> Override the setattr inode handler and check for O_TRUNC in the open
> handler to prevent truncations.
>
> Link [1]: https://github.com/systemd/systemd/issues/37349
> Closes: https://github.com/SELinuxProject/selinux/issues/475
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
This issue can't be limited to just the status node, so if we care
about preventing this, we ought to do it for them all.
When would we want to allow truncation of _any_ selinuxfs file?
> ---
> security/selinux/selinuxfs.c | 43 ++++++++++++++++++++++++++++++++++--
> 1 file changed, 41 insertions(+), 2 deletions(-)
>
> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> index 896acad1f5f7..df079a35a02d 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -214,10 +214,30 @@ static const struct file_operations sel_handle_unknown_ops = {
> .llseek = generic_file_llseek,
> };
>
> +static int sel_setattr_handle_status(struct mnt_idmap *idmap,
> + struct dentry *dentry,
> + struct iattr *iattr)
> +{
> + /* Prevent truncation to avoid raising SIGBUS */
> + if (iattr->ia_valid & ATTR_SIZE)
> + return -EINVAL;
> +
> + return simple_setattr(idmap, dentry, iattr);
> +}
> +
> +static const struct inode_operations sel_handle_status_iops = {
> + .setattr = sel_setattr_handle_status,
> +};
> +
> static int sel_open_handle_status(struct inode *inode, struct file *filp)
> {
> - struct page *status = selinux_kernel_status_page();
> + struct page *status;
>
> + /* Prevent truncation to avoid raising SIGBUS */
> + if (filp->f_flags & O_TRUNC)
> + return -EINVAL;
> +
> + status = selinux_kernel_status_page();
> if (!status)
> return -ENOMEM;
>
> @@ -1980,7 +2000,6 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
> [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
> [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO},
> [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
> - [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO},
> [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
> [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops,
> S_IWUGO},
> @@ -1995,6 +2014,26 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
> if (ret)
> goto err;
>
> + /* Create "status" separately to assign a custom inode_operations */
> + {
> + ret = -ENOMEM;
> +
> + dentry = d_alloc_name(sb->s_root, "status");
> + if (!dentry)
> + goto err;
> + inode = new_inode(sb);
> + if (!inode) {
> + dput(dentry);
> + goto err;
> + }
> + inode->i_mode = S_IFREG | 0444;
> + simple_inode_init_ts(inode);
> + inode->i_fop = &sel_handle_status_ops;
> + inode->i_op = &sel_handle_status_iops;
> + inode->i_ino = SEL_STATUS;
> + d_add(dentry, inode);
> + }
> +
> fsi = sb->s_fs_info;
> fsi->bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &fsi->last_ino);
> if (IS_ERR(fsi->bool_dir)) {
> --
> 2.51.0
>
On Fri, 30 Jan 2026 at 22:08, Stephen Smalley
<stephen.smalley.work@gmail.com> wrote:
>
> On Fri, Jan 30, 2026 at 12:11 PM Christian Göttsche
> <cgoettsche@seltendoof.de> wrote:
> >
> > From: Christian Göttsche <cgzones@googlemail.com>
> >
> > Currently the SELinux status map can be truncated, given the necessary
> > permissions, leading to foreign user space processes getting a bus error
> > (SIGBUS) while concurrently making use of the status map.
> > For example systemd can be killed that way, see [1].
> >
> > Override the setattr inode handler and check for O_TRUNC in the open
> > handler to prevent truncations.
> >
> > Link [1]: https://github.com/systemd/systemd/issues/37349
> > Closes: https://github.com/SELinuxProject/selinux/issues/475
> > Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
>
> This issue can't be limited to just the status node, so if we care
> about preventing this, we ought to do it for them all.
> When would we want to allow truncation of _any_ selinuxfs file?
Probably not, but - at least to my quick overview - the status map is
the only file-object backed up by real memory and the only file
commonly mapped by userspace.
>
> > ---
> > security/selinux/selinuxfs.c | 43 ++++++++++++++++++++++++++++++++++--
> > 1 file changed, 41 insertions(+), 2 deletions(-)
> >
> > diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> > index 896acad1f5f7..df079a35a02d 100644
> > --- a/security/selinux/selinuxfs.c
> > +++ b/security/selinux/selinuxfs.c
> > @@ -214,10 +214,30 @@ static const struct file_operations sel_handle_unknown_ops = {
> > .llseek = generic_file_llseek,
> > };
> >
> > +static int sel_setattr_handle_status(struct mnt_idmap *idmap,
> > + struct dentry *dentry,
> > + struct iattr *iattr)
> > +{
> > + /* Prevent truncation to avoid raising SIGBUS */
> > + if (iattr->ia_valid & ATTR_SIZE)
> > + return -EINVAL;
> > +
> > + return simple_setattr(idmap, dentry, iattr);
> > +}
> > +
> > +static const struct inode_operations sel_handle_status_iops = {
> > + .setattr = sel_setattr_handle_status,
> > +};
> > +
> > static int sel_open_handle_status(struct inode *inode, struct file *filp)
> > {
> > - struct page *status = selinux_kernel_status_page();
> > + struct page *status;
> >
> > + /* Prevent truncation to avoid raising SIGBUS */
> > + if (filp->f_flags & O_TRUNC)
> > + return -EINVAL;
> > +
> > + status = selinux_kernel_status_page();
> > if (!status)
> > return -ENOMEM;
> >
> > @@ -1980,7 +2000,6 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
> > [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
> > [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO},
> > [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
> > - [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO},
> > [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
> > [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops,
> > S_IWUGO},
> > @@ -1995,6 +2014,26 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
> > if (ret)
> > goto err;
> >
> > + /* Create "status" separately to assign a custom inode_operations */
> > + {
> > + ret = -ENOMEM;
> > +
> > + dentry = d_alloc_name(sb->s_root, "status");
> > + if (!dentry)
> > + goto err;
> > + inode = new_inode(sb);
> > + if (!inode) {
> > + dput(dentry);
> > + goto err;
> > + }
> > + inode->i_mode = S_IFREG | 0444;
> > + simple_inode_init_ts(inode);
> > + inode->i_fop = &sel_handle_status_ops;
> > + inode->i_op = &sel_handle_status_iops;
> > + inode->i_ino = SEL_STATUS;
> > + d_add(dentry, inode);
> > + }
> > +
> > fsi = sb->s_fs_info;
> > fsi->bool_dir = sel_make_dir(sb->s_root, BOOL_DIR_NAME, &fsi->last_ino);
> > if (IS_ERR(fsi->bool_dir)) {
> > --
> > 2.51.0
> >
On Fri, Jan 30, 2026 at 4:08 PM Stephen Smalley <stephen.smalley.work@gmail.com> wrote: > On Fri, Jan 30, 2026 at 12:11 PM Christian Göttsche > <cgoettsche@seltendoof.de> wrote: > > > > From: Christian Göttsche <cgzones@googlemail.com> > > > > Currently the SELinux status map can be truncated, given the necessary > > permissions, leading to foreign user space processes getting a bus error > > (SIGBUS) while concurrently making use of the status map. > > For example systemd can be killed that way, see [1]. > > > > Override the setattr inode handler and check for O_TRUNC in the open > > handler to prevent truncations. > > > > Link [1]: https://github.com/systemd/systemd/issues/37349 > > Closes: https://github.com/SELinuxProject/selinux/issues/475 > > Signed-off-by: Christian Göttsche <cgzones@googlemail.com> > > This issue can't be limited to just the status node, so if we care > about preventing this, we ought to do it for them all. > When would we want to allow truncation of _any_ selinuxfs file? Fair point. -- paul-moore.com
On Jan 30, 2026 =?UTF-8?q?Christian=20G=C3=B6ttsche?= <cgoettsche@seltendoof.de> wrote:
>
> Currently the SELinux status map can be truncated, given the necessary
> permissions, leading to foreign user space processes getting a bus error
> (SIGBUS) while concurrently making use of the status map.
> For example systemd can be killed that way, see [1].
>
> Override the setattr inode handler and check for O_TRUNC in the open
> handler to prevent truncations.
>
> Link [1]: https://github.com/systemd/systemd/issues/37349
>
> Closes: https://github.com/SELinuxProject/selinux/issues/475
> Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> ---
> security/selinux/selinuxfs.c | 43 ++++++++++++++++++++++++++++++++++--
> 1 file changed, 41 insertions(+), 2 deletions(-)
>
> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> index 896acad1f5f7..df079a35a02d 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -214,10 +214,30 @@ static const struct file_operations sel_handle_unknown_ops = {
> .llseek = generic_file_llseek,
> };
>
> +static int sel_setattr_handle_status(struct mnt_idmap *idmap,
> + struct dentry *dentry,
> + struct iattr *iattr)
> +{
> + /* Prevent truncation to avoid raising SIGBUS */
> + if (iattr->ia_valid & ATTR_SIZE)
> + return -EINVAL;
Do we want this as -EINVAL or -EPERM? However, see my comments below
about how to handle the ATTR_SIZE case.
> + return simple_setattr(idmap, dentry, iattr);
> +}
> +
> +static const struct inode_operations sel_handle_status_iops = {
> + .setattr = sel_setattr_handle_status,
> +};
> +
> static int sel_open_handle_status(struct inode *inode, struct file *filp)
> {
> - struct page *status = selinux_kernel_status_page();
> + struct page *status;
>
> + /* Prevent truncation to avoid raising SIGBUS */
> + if (filp->f_flags & O_TRUNC)
> + return -EINVAL;
Same as above, should this be -EPERM?
> + status = selinux_kernel_status_page();
> if (!status)
> return -ENOMEM;
>
> @@ -1980,7 +2000,6 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
> [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
> [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO},
> [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
> - [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO},
> [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
> [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops,
> S_IWUGO},
> @@ -1995,6 +2014,26 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
> if (ret)
> goto err;
>
> + /* Create "status" separately to assign a custom inode_operations */
> + {
> + ret = -ENOMEM;
> +
> + dentry = d_alloc_name(sb->s_root, "status");
> + if (!dentry)
> + goto err;
> + inode = new_inode(sb);
> + if (!inode) {
> + dput(dentry);
> + goto err;
> + }
> + inode->i_mode = S_IFREG | 0444;
> + simple_inode_init_ts(inode);
> + inode->i_fop = &sel_handle_status_ops;
> + inode->i_op = &sel_handle_status_iops;
> + inode->i_ino = SEL_STATUS;
> + d_add(dentry, inode);
> + }
I worry a little about duplicating and open coding the per-file loop from
simple_fill_super(), I can see things slowly getting out of sync and bad
things happening. Unfortunately, I don't see anything in libfs that would
allow us to supply our own inode_operations either.
What do you think about using selinux_inode_setattr() as shown below,
would this work?
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index feda34b18d83..8e4374f22a18 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -106,6 +106,7 @@
#include "netlabel.h"
#include "audit.h"
#include "avc_ss.h"
+#include "selinuxfs.h"
#define SELINUX_INODE_INIT_XATTRS 1
@@ -3291,6 +3292,12 @@ static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
return 0;
}
+ if (inode->i_sb->s_magic == SELINUX_MAGIC) {
+ int rc = selinuxfs_inode_setattr(idmap, inode, iattr);
+ if (rc)
+ return rc;
+ }
+
if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
return dentry_has_perm(cred, dentry, FILE__SETATTR);
diff --git a/security/selinux/include/selinuxfs.h b/security/selinux/include/selinuxfs.h
new file mode 100644
index 000000000000..f5e6da02833b
--- /dev/null
+++ b/security/selinux/include/selinuxfs.h
@@ -0,0 +1,2 @@
+int selinuxfs_inode_setattr(struct mnt_idmap *idmap, struct inode *inode,
+ struct iattr *iattr);
diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
index 896acad1f5f7..5af5e1dee743 100644
--- a/security/selinux/selinuxfs.c
+++ b/security/selinux/selinuxfs.c
@@ -43,6 +43,7 @@
#include "objsec.h"
#include "conditional.h"
#include "ima.h"
+#include "selinuxfs.h"
enum sel_inos {
SEL_ROOT_INO = 2,
@@ -1954,6 +1955,20 @@ static struct dentry *sel_make_swapover_dir(struct super_block *sb,
return dentry; // borrowed
}
+int selinuxfs_inode_setattr(struct mnt_idmap *idmap, struct inode *inode,
+ struct iattr *iattr)
+{
+ switch (inode->i_ino) {
+ case SEL_STATUS:
+ if (iattr->ia_valid & ATTR_SIZE)
+ return -EPERM;
+ default:
+ break;
+ }
+
+ return 0;
+}
--
paul-moore.com
On Fri, 30 Jan 2026 at 21:46, Paul Moore <paul@paul-moore.com> wrote:
>
> On Jan 30, 2026 =?UTF-8?q?Christian=20G=C3=B6ttsche?= <cgoettsche@seltendoof.de> wrote:
> >
> > Currently the SELinux status map can be truncated, given the necessary
> > permissions, leading to foreign user space processes getting a bus error
> > (SIGBUS) while concurrently making use of the status map.
> > For example systemd can be killed that way, see [1].
> >
> > Override the setattr inode handler and check for O_TRUNC in the open
> > handler to prevent truncations.
> >
> > Link [1]: https://github.com/systemd/systemd/issues/37349
> >
> > Closes: https://github.com/SELinuxProject/selinux/issues/475
> > Signed-off-by: Christian Göttsche <cgzones@googlemail.com>
> > ---
> > security/selinux/selinuxfs.c | 43 ++++++++++++++++++++++++++++++++++--
> > 1 file changed, 41 insertions(+), 2 deletions(-)
> >
> > diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> > index 896acad1f5f7..df079a35a02d 100644
> > --- a/security/selinux/selinuxfs.c
> > +++ b/security/selinux/selinuxfs.c
> > @@ -214,10 +214,30 @@ static const struct file_operations sel_handle_unknown_ops = {
> > .llseek = generic_file_llseek,
> > };
> >
> > +static int sel_setattr_handle_status(struct mnt_idmap *idmap,
> > + struct dentry *dentry,
> > + struct iattr *iattr)
> > +{
> > + /* Prevent truncation to avoid raising SIGBUS */
> > + if (iattr->ia_valid & ATTR_SIZE)
> > + return -EINVAL;
>
> Do we want this as -EINVAL or -EPERM? However, see my comments below
> about how to handle the ATTR_SIZE case.
I did not choose EPERM because it is not a matter of missing
permissions of the caller.
The status page should just not support truncation and EINVAL seemed
more natural than ENOTSUP.
>
> > + return simple_setattr(idmap, dentry, iattr);
> > +}
> > +
> > +static const struct inode_operations sel_handle_status_iops = {
> > + .setattr = sel_setattr_handle_status,
> > +};
> > +
> > static int sel_open_handle_status(struct inode *inode, struct file *filp)
> > {
> > - struct page *status = selinux_kernel_status_page();
> > + struct page *status;
> >
> > + /* Prevent truncation to avoid raising SIGBUS */
> > + if (filp->f_flags & O_TRUNC)
> > + return -EINVAL;
>
> Same as above, should this be -EPERM?
>
> > + status = selinux_kernel_status_page();
> > if (!status)
> > return -ENOMEM;
> >
> > @@ -1980,7 +2000,6 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
> > [SEL_CHECKREQPROT] = {"checkreqprot", &sel_checkreqprot_ops, S_IRUGO|S_IWUSR},
> > [SEL_REJECT_UNKNOWN] = {"reject_unknown", &sel_handle_unknown_ops, S_IRUGO},
> > [SEL_DENY_UNKNOWN] = {"deny_unknown", &sel_handle_unknown_ops, S_IRUGO},
> > - [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO},
> > [SEL_POLICY] = {"policy", &sel_policy_ops, S_IRUGO},
> > [SEL_VALIDATE_TRANS] = {"validatetrans", &sel_transition_ops,
> > S_IWUGO},
> > @@ -1995,6 +2014,26 @@ static int sel_fill_super(struct super_block *sb, struct fs_context *fc)
> > if (ret)
> > goto err;
> >
> > + /* Create "status" separately to assign a custom inode_operations */
> > + {
> > + ret = -ENOMEM;
> > +
> > + dentry = d_alloc_name(sb->s_root, "status");
> > + if (!dentry)
> > + goto err;
> > + inode = new_inode(sb);
> > + if (!inode) {
> > + dput(dentry);
> > + goto err;
> > + }
> > + inode->i_mode = S_IFREG | 0444;
> > + simple_inode_init_ts(inode);
> > + inode->i_fop = &sel_handle_status_ops;
> > + inode->i_op = &sel_handle_status_iops;
> > + inode->i_ino = SEL_STATUS;
> > + d_add(dentry, inode);
> > + }
>
> I worry a little about duplicating and open coding the per-file loop from
> simple_fill_super(), I can see things slowly getting out of sync and bad
> things happening. Unfortunately, I don't see anything in libfs that would
> allow us to supply our own inode_operations either.
We could ask the FS people if they would be fine with the following patch:
diff --git a/fs/libfs.c b/fs/libfs.c
index 9264523be85c..76f7fec136cb 100644
--- a/fs/libfs.c
+++ b/fs/libfs.c
@@ -1089,6 +1089,7 @@ int simple_fill_super(struct super_block *s,
unsigned long magic,
}
inode->i_mode = S_IFREG | files->mode;
simple_inode_init_ts(inode);
+ inode->i_op = files->iops;
inode->i_fop = files->ops;
inode->i_ino = i;
d_make_persistent(dentry, inode);
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 04ceeca12a0d..9f1a9f0a9b48 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -3225,7 +3225,7 @@ extern const struct file_operations simple_dir_operations;
extern const struct inode_operations simple_dir_inode_operations;
extern void make_empty_dir_inode(struct inode *inode);
extern bool is_empty_dir_inode(struct inode *inode);
-struct tree_descr { const char *name; const struct file_operations
*ops; int mode; };
+struct tree_descr { const char *name; const struct file_operations
*ops; int mode; const struct inode_operations *iops; };
struct dentry *d_alloc_name(struct dentry *, const char *);
extern int simple_fill_super(struct super_block *, unsigned long,
const struct tree_descr *);
and then adding the hook would just be
- [SEL_STATUS] = {"status", &sel_handle_status_ops, S_IRUGO},
+ [SEL_STATUS] = {"status", &sel_handle_status_ops,
S_IRUGO, &sel_handle_status_iops},
>
> What do you think about using selinux_inode_setattr() as shown below,
> would this work?
>
> diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
> index feda34b18d83..8e4374f22a18 100644
> --- a/security/selinux/hooks.c
> +++ b/security/selinux/hooks.c
> @@ -106,6 +106,7 @@
> #include "netlabel.h"
> #include "audit.h"
> #include "avc_ss.h"
> +#include "selinuxfs.h"
>
> #define SELINUX_INODE_INIT_XATTRS 1
>
> @@ -3291,6 +3292,12 @@ static int selinux_inode_setattr(struct mnt_idmap *idmap, struct dentry *dentry,
> return 0;
> }
>
> + if (inode->i_sb->s_magic == SELINUX_MAGIC) {
> + int rc = selinuxfs_inode_setattr(idmap, inode, iattr);
> + if (rc)
> + return rc;
> + }
> +
I don't like that approach, since up to now selinux_inode_setattr() is
only for SELinux permission checking and this would add some behavior
modification into it.
> if (ia_valid & (ATTR_MODE | ATTR_UID | ATTR_GID |
> ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET))
> return dentry_has_perm(cred, dentry, FILE__SETATTR);
> diff --git a/security/selinux/include/selinuxfs.h b/security/selinux/include/selinuxfs.h
> new file mode 100644
> index 000000000000..f5e6da02833b
> --- /dev/null
> +++ b/security/selinux/include/selinuxfs.h
> @@ -0,0 +1,2 @@
> +int selinuxfs_inode_setattr(struct mnt_idmap *idmap, struct inode *inode,
> + struct iattr *iattr);
> diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c
> index 896acad1f5f7..5af5e1dee743 100644
> --- a/security/selinux/selinuxfs.c
> +++ b/security/selinux/selinuxfs.c
> @@ -43,6 +43,7 @@
> #include "objsec.h"
> #include "conditional.h"
> #include "ima.h"
> +#include "selinuxfs.h"
>
> enum sel_inos {
> SEL_ROOT_INO = 2,
> @@ -1954,6 +1955,20 @@ static struct dentry *sel_make_swapover_dir(struct super_block *sb,
> return dentry; // borrowed
> }
>
> +int selinuxfs_inode_setattr(struct mnt_idmap *idmap, struct inode *inode,
> + struct iattr *iattr)
> +{
> + switch (inode->i_ino) {
> + case SEL_STATUS:
> + if (iattr->ia_valid & ATTR_SIZE)
> + return -EPERM;
> + default:
> + break;
> + }
> +
> + return 0;
> +}
>
> --
> paul-moore.com
© 2016 - 2026 Red Hat, Inc.