selinux/stable-7.3 PR 20260919

-----BEGIN PGP SIGNATURE-----
 
 iQJIBAABCgAyFiEES0KozwfymdVUl37v6iDy2pc3iXMFAmqvPSwUHHBhdWxAcGF1
 bC1tb29yZS5jb20ACgkQ6iDy2pc3iXP1rBAAkK6sP+lzXynr/abxWio9UCvlN6NF
 jqMXtKeUeLNg/lJakXKhja/FGaK9w+Rm+zlXQQvVE7YAO5Db758JzCXMTHlK6etD
 T5d+GVdZj/oVcAvfGhov0guNHZqcfKTGWOXKtGV8RQWYL+f6IC1IIHnz9wNeWIfY
 emkfOTgAJpCkX9nXS5xTdQNUiPkUV80LFGqxXCMhxiumvyzqOhUteytfNh3UXXcs
 ldOWRBstUumudBe/MJR09REtQve3emHc7bZUAxSKwXxzdOPTf7UEy4Y7QXCN3Opn
 Dn6Dsrt6lnpIZyzUK/eOnZvQ639gDqTMW2XTmin0JIf2KGG/PhqnvOT07hOPl+X5
 wqXh2wCFn+tXVi5t+S2myMUVZK56GwfRH9kMCo6ca1Ui36iYYEeoJYt7z5tsE6P5
 YBdvKKJ/MPR1/uEwP/TB3UDK0o4OBjyujc6/Ka5iBoIHXKpx7MBLOo/Q0h/Wu2A0
 7HYvG/4lYdWp3p5iR8cdZ+ZgdTQDWi6t5BD3JWOfWi1gHCeGgZCHTCZ9mdSoFS18
 9GpXFXTR4yM5pfwxQEAu6jFH0esSB92HJBd/yS7sU8EIUQ+0XmOge+gVPifMrhWj
 yOHCR7hlAYE5ds51w3ZVOYI0slkYImqCHEdlCz1DiKzyLmQQmfJvshOIvBCl7uwn
 LRHu6Mo0cEBMAvc=
 =kiQE
 -----END PGP SIGNATURE-----

Merge tag 'selinux-pr-20260919' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux

Pull selinux fixes from Paul Moore:

 - Ensure that the cached SELinux access decisions are correct

 - Fix the SELinux overlayfs code to properly track the top-level/user
   information on multiple stacked overlayfs filesystems

 - Fix the SELinux overlayfs code to properly enforce mprotect() access
   control policy on all of the different layers in multiple stacked
   overlayfs filesystems

* tag 'selinux-pr-20260919' of git://git.kernel.org/pub/scm/linux/kernel/git/pcmoore/selinux:
  selinux: recheck intermediate backing files on mprotect()
  selinux: preserve user SID across nested backing files
  selinux: always fill AVC decision in avc_has_perm_noaudit()
This commit is contained in:
Linus Torvalds 2026-09-20 06:50:31 -07:00
commit 4a910e594a
3 changed files with 150 additions and 23 deletions

View File

@ -1149,8 +1149,11 @@ inline int avc_has_perm_noaudit(u32 ssid, u32 tsid,
u32 denied;
struct avc_node *node;
if (WARN_ON(!requested))
if (WARN_ON(!requested)) {
/* Provide a deny-all, audit-all decision to the caller. */
*avd = (struct av_decision){ .auditdeny = 0xffffffff };
return -EACCES;
}
rcu_read_lock();
node = avc_lookup(ssid, tsid, tclass);

View File

@ -1674,24 +1674,30 @@ static int cred_has_capability(const struct cred *cred,
return rc;
}
/* Check whether a task has a particular permission to an inode.
The 'adp' parameter is optional and allows other audit
data to be passed (e.g. the dentry). */
/*
* Check whether a SID has a particular permission to an inode. The 'adp'
* parameter is optional and allows other audit data to be passed (e.g. the
* dentry).
*/
static int inode_sid_has_perm(u32 sid, struct inode *inode, u32 perms,
struct common_audit_data *adp)
{
struct inode_security_struct *isec;
if (unlikely(IS_PRIVATE(inode)))
return 0;
isec = selinux_inode(inode);
return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
}
static int inode_has_perm(const struct cred *cred,
struct inode *inode,
u32 perms,
struct common_audit_data *adp)
{
struct inode_security_struct *isec;
u32 sid;
if (unlikely(IS_PRIVATE(inode)))
return 0;
sid = cred_sid(cred);
isec = selinux_inode(inode);
return avc_has_perm(sid, isec->sid, isec->sclass, perms, adp);
return inode_sid_has_perm(cred_sid(cred), inode, perms, adp);
}
/* Same as inode_has_perm, but pass explicit audit data containing
@ -3843,17 +3849,74 @@ static int selinux_file_alloc_security(struct file *file)
return 0;
}
static inline u32 selinux_file_user_sid(const struct file *file)
{
if (unlikely(file->f_mode & FMODE_BACKING))
return selinux_backing_file(file)->uf_sid;
return selinux_file(file)->sid;
}
static int selinux_backing_file_alloc(struct file *backing_file,
const struct file *user_file)
{
struct backing_file_security_struct *bfsec;
const struct backing_file_security_struct *ubfsec;
struct backing_file_security_layer *layer;
u32 i;
bfsec = selinux_backing_file(backing_file);
bfsec->uf_sid = selinux_file(user_file)->sid;
bfsec->uf_sid = selinux_file_user_sid(user_file);
if (!(user_file->f_mode & FMODE_BACKING))
return 0;
ubfsec = selinux_backing_file(user_file);
/* a wrapped count would make kmalloc_array() return ZERO_SIZE_PTR */
if (unlikely(ubfsec->layer_count == U32_MAX))
return -EOVERFLOW;
/*
* The final VMA only retains the lowest backing file, so record the
* whole chain here rather than in the mmap hook, where concurrent
* mappings would have to be serialized. Size it dynamically: erofs
* inode sharing adds a backing file without bumping s_stack_depth.
*/
bfsec->layers = kmalloc_array(ubfsec->layer_count + 1,
sizeof(*bfsec->layers), GFP_KERNEL);
if (!bfsec->layers)
return -ENOMEM;
for (i = 0; i < ubfsec->layer_count; i++) {
layer = &bfsec->layers[i];
*layer = ubfsec->layers[i];
path_get(&layer->path);
}
/* f_path, not file_user_path(): this layer, not the top-level file */
layer = &bfsec->layers[i];
layer->path = user_file->f_path;
layer->mounter_sid = cred_sid(user_file->f_cred);
layer->fd_sid = selinux_file(user_file)->sid;
path_get(&layer->path);
bfsec->layer_count = ubfsec->layer_count + 1;
return 0;
}
static void selinux_backing_file_free(struct file *backing_file)
{
struct backing_file_security_struct *bfsec;
/* security_backing_file_free() may be called twice after an error */
if (!backing_file_security(backing_file))
return;
bfsec = selinux_backing_file(backing_file);
while (bfsec->layer_count)
path_put(&bfsec->layers[--bfsec->layer_count].path);
kfree(bfsec->layers);
bfsec->layers = NULL;
}
/*
* Check whether a task has the ioctl permission and cmd
* operation to an inode.
@ -3971,6 +4034,53 @@ static int selinux_file_ioctl_compat(struct file *file, unsigned int cmd,
static int default_noexec __ro_after_init;
static u32 file_map_prot_to_av(unsigned long prot, bool shared)
{
u32 av = FILE__READ;
if (shared && (prot & PROT_WRITE))
av |= FILE__WRITE;
if (prot & PROT_EXEC)
av |= FILE__EXECUTE;
return av;
}
static int backing_mounters_has_perm(const struct file *file, u32 av)
{
const struct backing_file_security_struct *bfsec;
const struct backing_file_security_layer *layer;
struct common_audit_data ad;
struct inode *inode;
u32 i;
int rc;
if (WARN_ON_ONCE(!(file->f_mode & FMODE_BACKING)))
return -EIO;
bfsec = selinux_backing_file(file);
for (i = 0; i < bfsec->layer_count; i++) {
layer = &bfsec->layers[i];
inode = d_inode(layer->path.dentry);
ad.type = LSM_AUDIT_DATA_PATH;
ad.u.path = layer->path;
if (layer->mounter_sid != layer->fd_sid) {
rc = avc_has_perm(layer->mounter_sid, layer->fd_sid,
SECCLASS_FD, FD__USE, &ad);
if (rc)
return rc;
}
rc = inode_sid_has_perm(layer->mounter_sid, inode, av, &ad);
if (rc)
return rc;
}
return 0;
}
static int __file_map_prot_check(const struct file *file, unsigned long prot,
bool shared, bool mounter_check,
bool bf_user_file)
@ -4004,14 +4114,10 @@ static int __file_map_prot_check(const struct file *file, unsigned long prot,
if (file) {
const struct cred *cred = mounter_check ?
file->f_cred : current_cred();
/* "read" always possible, "write" only if shared */
u32 av = FILE__READ;
if (shared && prot_write)
av |= FILE__WRITE;
if (prot_exec)
av |= FILE__EXECUTE;
return __file_has_perm(cred, file, av, bf_user_file);
return __file_has_perm(cred, file,
file_map_prot_to_av(prot, shared),
bf_user_file);
}
return 0;
@ -4106,6 +4212,7 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
int rc;
const struct cred *cred = current_cred();
u32 sid = cred_sid(cred);
u32 av;
const struct file *file = vma->vm_file;
bool backing_file;
bool shared = vma->vm_flags & VM_SHARED;
@ -4149,6 +4256,10 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
if (rc)
return rc;
if (backing_file) {
rc = backing_mounters_has_perm(file,
FILE__EXECMOD);
if (rc)
return rc;
rc = file_has_perm(file->f_cred, file,
FILE__EXECMOD);
if (rc)
@ -4161,6 +4272,10 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
if (rc)
return rc;
if (backing_file) {
av = file_map_prot_to_av(prot, shared);
rc = backing_mounters_has_perm(file, av);
if (rc)
return rc;
rc = file_map_prot_check(file, prot, shared, true);
if (rc)
return rc;
@ -7619,6 +7734,7 @@ static struct security_hook_list selinux_hooks[] __ro_after_init = {
LSM_HOOK_INIT(file_permission, selinux_file_permission),
LSM_HOOK_INIT(file_alloc_security, selinux_file_alloc_security),
LSM_HOOK_INIT(backing_file_alloc, selinux_backing_file_alloc),
LSM_HOOK_INIT(backing_file_free, selinux_backing_file_free),
LSM_HOOK_INIT(file_ioctl, selinux_file_ioctl),
LSM_HOOK_INIT(file_ioctl_compat, selinux_file_ioctl_compat),
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),

View File

@ -86,8 +86,16 @@ struct file_security_struct {
u32 pseqno; /* Policy seqno at the time of file open */
};
struct backing_file_security_layer {
struct path path; /* this layer's real path */
u32 mounter_sid; /* SID of the mounter that opened it */
u32 fd_sid; /* SID of its open file description */
};
struct backing_file_security_struct {
u32 uf_sid; /* associated user file fsec->sid */
u32 uf_sid; /* top-level user file fsec->sid */
u32 layer_count; /* number of intermediate backing files */
struct backing_file_security_layer *layers;
};
struct superblock_security_struct {