Re: [RFC PATCH] selinux: prevent truncation of status map

From: Stephen Smalley

Date: Thu Feb 12 2026 - 09:00:28 EST


On Sat, Jan 31, 2026 at 12:20 PM Christian Göttsche
<cgzones@xxxxxxxxxxxxxx> wrote:
>
> On Fri, 30 Jan 2026 at 22:08, Stephen Smalley
> <stephen.smalley.work@xxxxxxxxx> wrote:
> >
> > On Fri, Jan 30, 2026 at 12:11 PM Christian Göttsche
> > <cgoettsche@xxxxxxxxxxxxx> wrote:
> > >
> > > From: Christian Göttsche <cgzones@xxxxxxxxxxxxxx>
> > >
> > > 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@xxxxxxxxxxxxxx>
> >
> > 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.

/sys/fs/selinux/policy would be another one.

I would expect this to be an issue for other pseudo filesystems using
libfs, so would ask on linux-fsdevel and cc the VFS maintainers on
whether there is a general solution for this problem.

>
> >
> > > ---
> > > 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
> > >