Skip to content

Commit 094e94d

Browse files
tweksteenpcmoore
authored andcommitted
memfd,selinux: call security_inode_init_security_anon()
Prior to this change, no security hooks were called at the creation of a memfd file. It means that, for SELinux as an example, it will receive the default type of the filesystem that backs the in-memory inode. In most cases, that would be tmpfs, but if MFD_HUGETLB is passed, it will be hugetlbfs. Both can be considered implementation details of memfd. It also means that it is not possible to differentiate between a file coming from memfd_create and a file coming from a standard tmpfs mount point. Additionally, no permission is validated at creation, which differs from the similar memfd_secret syscall. Call security_inode_init_security_anon during creation. This ensures that the file is setup similarly to other anonymous inodes. On SELinux, it means that the file will receive the security context of its task. The ability to limit fexecve on memfd has been of interest to avoid potential pitfalls where /proc/self/exe or similar would be executed [1][2]. Reuse the "execute_no_trans" and "entrypoint" access vectors, similarly to the file class. These access vectors may not make sense for the existing "anon_inode" class. Therefore, define and assign a new class "memfd_file" to support such access vectors. Guard these changes behind a new policy capability named "memfd_class". [1] https://crbug.com/1305267 [2] https://lore.kernel.org/lkml/20221215001205.51969-1-jeffxu@google.com/ Signed-off-by: Thiébaud Weksteen <tweek@google.com> Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com> Tested-by: Stephen Smalley <stephen.smalley.work@gmail.com> Acked-by: Hugh Dickins <hughd@google.com> [PM: subj tweak] Signed-off-by: Paul Moore <paul@paul-moore.com>
1 parent 211ddde commit 094e94d

7 files changed

Lines changed: 44 additions & 7 deletions

File tree

include/linux/memfd.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
#include <linux/file.h>
66

7+
#define MEMFD_ANON_NAME "[memfd]"
8+
79
#ifdef CONFIG_MEMFD_CREATE
810
extern long memfd_fcntl(struct file *file, unsigned int cmd, unsigned int arg);
911
struct folio *memfd_alloc_folio(struct file *memfd, pgoff_t idx);

mm/memfd.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,8 @@ static struct file *alloc_file(const char *name, unsigned int flags)
433433
{
434434
unsigned int *file_seals;
435435
struct file *file;
436+
struct inode *inode;
437+
int err = 0;
436438

437439
if (flags & MFD_HUGETLB) {
438440
file = hugetlb_file_setup(name, 0, VM_NORESERVE,
@@ -444,12 +446,20 @@ static struct file *alloc_file(const char *name, unsigned int flags)
444446
}
445447
if (IS_ERR(file))
446448
return file;
449+
450+
inode = file_inode(file);
451+
err = security_inode_init_security_anon(inode,
452+
&QSTR(MEMFD_ANON_NAME), NULL);
453+
if (err) {
454+
fput(file);
455+
file = ERR_PTR(err);
456+
return file;
457+
}
458+
447459
file->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
448460
file->f_flags |= O_LARGEFILE;
449461

450462
if (flags & MFD_NOEXEC_SEAL) {
451-
struct inode *inode = file_inode(file);
452-
453463
inode->i_mode &= ~0111;
454464
file_seals = memfd_file_seals_ptr(file);
455465
if (file_seals) {

security/selinux/hooks.c

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
#include <linux/fanotify.h>
9494
#include <linux/io_uring/cmd.h>
9595
#include <uapi/linux/lsm.h>
96+
#include <linux/memfd.h>
9697

9798
#include "avc.h"
9899
#include "objsec.h"
@@ -2319,6 +2320,10 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
23192320
new_tsec = selinux_cred(bprm->cred);
23202321
isec = inode_security(inode);
23212322

2323+
if (WARN_ON(isec->sclass != SECCLASS_FILE &&
2324+
isec->sclass != SECCLASS_MEMFD_FILE))
2325+
return -EACCES;
2326+
23222327
/* Default to the current task SID. */
23232328
new_tsec->sid = old_tsec->sid;
23242329
new_tsec->osid = old_tsec->sid;
@@ -2371,8 +2376,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
23712376
ad.u.file = bprm->file;
23722377

23732378
if (new_tsec->sid == old_tsec->sid) {
2374-
rc = avc_has_perm(old_tsec->sid, isec->sid,
2375-
SECCLASS_FILE, FILE__EXECUTE_NO_TRANS, &ad);
2379+
rc = avc_has_perm(old_tsec->sid, isec->sid, isec->sclass,
2380+
FILE__EXECUTE_NO_TRANS, &ad);
23762381
if (rc)
23772382
return rc;
23782383
} else {
@@ -2382,8 +2387,8 @@ static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
23822387
if (rc)
23832388
return rc;
23842389

2385-
rc = avc_has_perm(new_tsec->sid, isec->sid,
2386-
SECCLASS_FILE, FILE__ENTRYPOINT, &ad);
2390+
rc = avc_has_perm(new_tsec->sid, isec->sid, isec->sclass,
2391+
FILE__ENTRYPOINT, &ad);
23872392
if (rc)
23882393
return rc;
23892394

@@ -2978,10 +2983,18 @@ static int selinux_inode_init_security_anon(struct inode *inode,
29782983
struct common_audit_data ad;
29792984
struct inode_security_struct *isec;
29802985
int rc;
2986+
bool is_memfd = false;
29812987

29822988
if (unlikely(!selinux_initialized()))
29832989
return 0;
29842990

2991+
if (name != NULL && name->name != NULL &&
2992+
!strcmp(name->name, MEMFD_ANON_NAME)) {
2993+
if (!selinux_policycap_memfd_class())
2994+
return 0;
2995+
is_memfd = true;
2996+
}
2997+
29852998
isec = selinux_inode(inode);
29862999

29873000
/*
@@ -3001,7 +3014,10 @@ static int selinux_inode_init_security_anon(struct inode *inode,
30013014
isec->sclass = context_isec->sclass;
30023015
isec->sid = context_isec->sid;
30033016
} else {
3004-
isec->sclass = SECCLASS_ANON_INODE;
3017+
if (is_memfd)
3018+
isec->sclass = SECCLASS_MEMFD_FILE;
3019+
else
3020+
isec->sclass = SECCLASS_ANON_INODE;
30053021
rc = security_transition_sid(
30063022
sid, sid,
30073023
isec->sclass, name, &isec->sid);

security/selinux/include/classmap.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,8 @@ const struct security_class_mapping secclass_map[] = {
179179
{ "anon_inode", { COMMON_FILE_PERMS, NULL } },
180180
{ "io_uring", { "override_creds", "sqpoll", "cmd", "allowed", NULL } },
181181
{ "user_namespace", { "create", NULL } },
182+
{ "memfd_file",
183+
{ COMMON_FILE_PERMS, "execute_no_trans", "entrypoint", NULL } },
182184
/* last one */ { NULL, {} }
183185
};
184186

security/selinux/include/policycap.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ enum {
1818
POLICYDB_CAP_NETIF_WILDCARD,
1919
POLICYDB_CAP_GENFS_SECLABEL_WILDCARD,
2020
POLICYDB_CAP_FUNCTIONFS_SECLABEL,
21+
POLICYDB_CAP_MEMFD_CLASS,
2122
__POLICYDB_CAP_MAX
2223
};
2324
#define POLICYDB_CAP_MAX (__POLICYDB_CAP_MAX - 1)

security/selinux/include/policycap_names.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ const char *const selinux_policycap_names[__POLICYDB_CAP_MAX] = {
2121
"netif_wildcard",
2222
"genfs_seclabel_wildcard",
2323
"functionfs_seclabel",
24+
"memfd_class",
2425
};
2526
/* clang-format on */
2627

security/selinux/include/security.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,11 @@ static inline bool selinux_policycap_functionfs_seclabel(void)
209209
selinux_state.policycap[POLICYDB_CAP_FUNCTIONFS_SECLABEL]);
210210
}
211211

212+
static inline bool selinux_policycap_memfd_class(void)
213+
{
214+
return READ_ONCE(selinux_state.policycap[POLICYDB_CAP_MEMFD_CLASS]);
215+
}
216+
212217
struct selinux_policy_convert_data;
213218

214219
struct selinux_load_state {

0 commit comments

Comments
 (0)