Import of kernel-4.18.0-553.137.1.el8_10
This commit is contained in:
parent
f79607ac92
commit
52184f2a5c
@ -12,7 +12,7 @@ RHEL_MINOR = 10
|
||||
#
|
||||
# Use this spot to avoid future merge conflicts.
|
||||
# Do not trim this comment.
|
||||
RHEL_RELEASE = 553.136.1
|
||||
RHEL_RELEASE = 553.137.1
|
||||
|
||||
#
|
||||
# ZSTREAM
|
||||
|
||||
@ -101,7 +101,8 @@ static void show_faulting_vma(unsigned long address, char *buf)
|
||||
if (vma && (vma->vm_start <= address)) {
|
||||
struct file *file = vma->vm_file;
|
||||
if (file) {
|
||||
nm = file_path(file, buf, PAGE_SIZE - 1);
|
||||
/* XXX: can we use %pD below and get rid of buf? */
|
||||
nm = d_path(file_user_path(file), buf, PAGE_SIZE - 1);
|
||||
inode = file_inode(vma->vm_file);
|
||||
dev = inode->i_sb->s_dev;
|
||||
ino = inode->i_ino;
|
||||
|
||||
139
fs/file_table.c
139
fs/file_table.c
@ -41,18 +41,64 @@ static struct kmem_cache *filp_cachep __read_mostly;
|
||||
|
||||
static struct percpu_counter nr_files __cacheline_aligned_in_smp;
|
||||
|
||||
/* Container for backing file with optional user path */
|
||||
struct backing_file {
|
||||
struct file file;
|
||||
struct path user_path;
|
||||
#ifdef CONFIG_SECURITY
|
||||
void *security;
|
||||
#endif
|
||||
};
|
||||
|
||||
#define backing_file(f) container_of(f, struct backing_file, file)
|
||||
|
||||
struct path *backing_file_user_path(const struct file *f)
|
||||
{
|
||||
return &backing_file(f)->user_path;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(backing_file_user_path);
|
||||
|
||||
void backing_file_set_user_path(struct file *f, const struct path *path)
|
||||
{
|
||||
backing_file(f)->user_path = *path;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(backing_file_set_user_path);
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
void *backing_file_security(const struct file *f)
|
||||
{
|
||||
return backing_file(f)->security;
|
||||
}
|
||||
|
||||
void backing_file_set_security(struct file *f, void *security)
|
||||
{
|
||||
backing_file(f)->security = security;
|
||||
}
|
||||
#endif /* CONFIG_SECURITY */
|
||||
|
||||
static void file_free_rcu(struct rcu_head *head)
|
||||
{
|
||||
struct file *f = container_of(head, struct file, f_u.fu_rcuhead);
|
||||
|
||||
put_cred(f->f_cred);
|
||||
kmem_cache_free(filp_cachep, f);
|
||||
if (unlikely(f->f_mode & FMODE_BACKING))
|
||||
kfree(backing_file(f));
|
||||
else
|
||||
kmem_cache_free(filp_cachep, f);
|
||||
}
|
||||
|
||||
static inline void backing_file_free(struct backing_file *ff)
|
||||
{
|
||||
security_backing_file_free(&ff->file);
|
||||
path_put(&ff->user_path);
|
||||
}
|
||||
|
||||
static inline void file_free(struct file *f)
|
||||
{
|
||||
security_file_free(f);
|
||||
if (!(f->f_mode & FMODE_NOACCOUNT))
|
||||
if (unlikely(f->f_mode & FMODE_BACKING))
|
||||
backing_file_free(backing_file(f));
|
||||
if (likely(!(f->f_mode & FMODE_NOACCOUNT)))
|
||||
percpu_counter_dec(&nr_files);
|
||||
call_rcu(&f->f_u.fu_rcuhead, file_free_rcu);
|
||||
}
|
||||
@ -92,20 +138,15 @@ int proc_nr_files(struct ctl_table *table, int write,
|
||||
}
|
||||
#endif
|
||||
|
||||
static struct file *__alloc_file(int flags, const struct cred *cred)
|
||||
static int init_file(struct file *f, int flags, const struct cred *cred)
|
||||
{
|
||||
struct file *f;
|
||||
int error;
|
||||
|
||||
f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
|
||||
if (unlikely(!f))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
f->f_cred = get_cred(cred);
|
||||
error = security_file_alloc(f);
|
||||
if (unlikely(error)) {
|
||||
file_free_rcu(&f->f_u.fu_rcuhead);
|
||||
return ERR_PTR(error);
|
||||
put_cred(f->f_cred);
|
||||
return error;
|
||||
}
|
||||
|
||||
atomic_long_set(&f->f_count, 1);
|
||||
@ -117,7 +158,7 @@ static struct file *__alloc_file(int flags, const struct cred *cred)
|
||||
f->f_mode = OPEN_FMODE(flags);
|
||||
/* f->f_version: 0 */
|
||||
|
||||
return f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Find an unused file structure and return a pointer to it.
|
||||
@ -134,6 +175,7 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
|
||||
{
|
||||
static long old_max;
|
||||
struct file *f;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* Privileged users can go above max_files
|
||||
@ -147,9 +189,17 @@ struct file *alloc_empty_file(int flags, const struct cred *cred)
|
||||
goto over;
|
||||
}
|
||||
|
||||
f = __alloc_file(flags, cred);
|
||||
if (!IS_ERR(f))
|
||||
percpu_counter_inc(&nr_files);
|
||||
f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
|
||||
if (unlikely(!f))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
error = init_file(f, flags, cred);
|
||||
if (unlikely(error)) {
|
||||
kmem_cache_free(filp_cachep, f);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
percpu_counter_inc(&nr_files);
|
||||
|
||||
return f;
|
||||
|
||||
@ -165,18 +215,71 @@ over:
|
||||
/*
|
||||
* Variant of alloc_empty_file() that doesn't check and modify nr_files.
|
||||
*
|
||||
* Should not be used unless there's a very good reason to do so.
|
||||
* This is only for kernel internal use, and the allocate file must not be
|
||||
* installed into file tables or such.
|
||||
*/
|
||||
struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred)
|
||||
{
|
||||
struct file *f = __alloc_file(flags, cred);
|
||||
struct file *f;
|
||||
int error;
|
||||
|
||||
if (!IS_ERR(f))
|
||||
f->f_mode |= FMODE_NOACCOUNT;
|
||||
f = kmem_cache_zalloc(filp_cachep, GFP_KERNEL);
|
||||
if (unlikely(!f))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
error = init_file(f, flags, cred);
|
||||
if (unlikely(error)) {
|
||||
kmem_cache_free(filp_cachep, f);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
f->f_mode |= FMODE_NOACCOUNT;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static int init_backing_file(struct backing_file *ff,
|
||||
const struct file *user_file)
|
||||
{
|
||||
memset(&ff->user_path, 0, sizeof(ff->user_path));
|
||||
backing_file_set_security(&ff->file, NULL);
|
||||
return security_backing_file_alloc(&ff->file, user_file);
|
||||
}
|
||||
|
||||
/*
|
||||
* Variant of alloc_empty_file() that allocates a backing_file container
|
||||
* and doesn't check and modify nr_files.
|
||||
*
|
||||
* This is only for kernel internal use, and the allocate file must not be
|
||||
* installed into file tables or such.
|
||||
*/
|
||||
struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
|
||||
const struct file *user_file)
|
||||
{
|
||||
struct backing_file *ff;
|
||||
int error;
|
||||
|
||||
ff = kzalloc(sizeof(struct backing_file), GFP_KERNEL);
|
||||
if (unlikely(!ff))
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
error = init_file(&ff->file, flags, cred);
|
||||
if (unlikely(error)) {
|
||||
kfree(ff);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
/* The f_mode flags must be set before fput(). */
|
||||
ff->file.f_mode |= FMODE_BACKING | FMODE_NOACCOUNT;
|
||||
error = init_backing_file(ff, user_file);
|
||||
if (unlikely(error)) {
|
||||
fput(&ff->file);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
return &ff->file;
|
||||
}
|
||||
|
||||
/**
|
||||
* alloc_file - allocate and initialize a 'struct file'
|
||||
*
|
||||
|
||||
@ -116,16 +116,26 @@ extern void chroot_fs_refs(const struct path *, const struct path *);
|
||||
/*
|
||||
* file_table.c
|
||||
*/
|
||||
extern struct file *alloc_empty_file(int, const struct cred *);
|
||||
extern struct file *alloc_empty_file_noaccount(int, const struct cred *);
|
||||
struct file *alloc_empty_file(int flags, const struct cred *cred);
|
||||
struct file *alloc_empty_file_noaccount(int flags, const struct cred *cred);
|
||||
struct file *alloc_empty_backing_file(int flags, const struct cred *cred,
|
||||
const struct file *user_file);
|
||||
void backing_file_set_user_path(struct file *f, const struct path *path);
|
||||
|
||||
static inline void file_put_write_access(struct file *file)
|
||||
{
|
||||
put_write_access(file->f_inode);
|
||||
__mnt_drop_write(file->f_path.mnt);
|
||||
if (unlikely(file->f_mode & FMODE_BACKING))
|
||||
__mnt_drop_write(backing_file_user_path(file)->mnt);
|
||||
}
|
||||
|
||||
static inline void put_file_access(struct file *file)
|
||||
{
|
||||
if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
|
||||
i_readcount_dec(file->f_inode);
|
||||
} else if (file->f_mode & FMODE_WRITER) {
|
||||
put_write_access(file->f_inode);
|
||||
__mnt_drop_write(file->f_path.mnt);
|
||||
file_put_write_access(file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
76
fs/open.c
76
fs/open.c
@ -745,6 +745,30 @@ SYSCALL_DEFINE3(fchown, unsigned int, fd, uid_t, user, gid_t, group)
|
||||
return ksys_fchown(fd, user, group);
|
||||
}
|
||||
|
||||
static inline int file_get_write_access(struct file *f)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = get_write_access(f->f_inode);
|
||||
if (unlikely(error))
|
||||
return error;
|
||||
error = __mnt_want_write(f->f_path.mnt);
|
||||
if (unlikely(error))
|
||||
goto cleanup_inode;
|
||||
if (unlikely(f->f_mode & FMODE_BACKING)) {
|
||||
error = __mnt_want_write(backing_file_user_path(f)->mnt);
|
||||
if (unlikely(error))
|
||||
goto cleanup_mnt;
|
||||
}
|
||||
return 0;
|
||||
|
||||
cleanup_mnt:
|
||||
__mnt_drop_write(f->f_path.mnt);
|
||||
cleanup_inode:
|
||||
put_write_access(f->f_inode);
|
||||
return error;
|
||||
}
|
||||
|
||||
static int do_dentry_open(struct file *f,
|
||||
struct inode *inode,
|
||||
int (*open)(struct inode *, struct file *))
|
||||
@ -768,14 +792,9 @@ static int do_dentry_open(struct file *f,
|
||||
if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
|
||||
i_readcount_inc(inode);
|
||||
} else if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {
|
||||
error = get_write_access(inode);
|
||||
error = file_get_write_access(f);
|
||||
if (unlikely(error))
|
||||
goto cleanup_file;
|
||||
error = __mnt_want_write(f->f_path.mnt);
|
||||
if (unlikely(error)) {
|
||||
put_write_access(inode);
|
||||
goto cleanup_file;
|
||||
}
|
||||
f->f_mode |= FMODE_WRITER;
|
||||
}
|
||||
|
||||
@ -946,23 +965,44 @@ struct file *dentry_open(const struct path *path, int flags,
|
||||
}
|
||||
EXPORT_SYMBOL(dentry_open);
|
||||
|
||||
struct file *open_with_fake_path(const struct path *path, int flags,
|
||||
struct inode *inode, const struct cred *cred)
|
||||
/**
|
||||
* backing_file_open - open a backing file for kernel internal use
|
||||
* @user_path: path that the user reuqested to open
|
||||
* @flags: open flags
|
||||
* @path: path of the backing file
|
||||
* @cred: credentials for open
|
||||
*
|
||||
* Open a backing file for a stackable filesystem (e.g., overlayfs).
|
||||
* @user_path may be on the stackable filesystem and @real_path on the
|
||||
* underlying filesystem. In this case, we want to be able to return the
|
||||
* @user_path of the stackable filesystem. This is done by embedding the
|
||||
* returned file into a container structure that also stores the stacked
|
||||
* file's path, which can be retrieved using backing_file_user_path().
|
||||
*/
|
||||
struct file *backing_file_open(const struct file *user_file, int flags,
|
||||
const struct path *real_path,
|
||||
const struct cred *cred)
|
||||
{
|
||||
struct file *f = alloc_empty_file_noaccount(flags, cred);
|
||||
if (!IS_ERR(f)) {
|
||||
int error;
|
||||
const struct path *user_path = &user_file->f_path;
|
||||
struct file *f;
|
||||
int error;
|
||||
|
||||
f->f_path = *path;
|
||||
error = do_dentry_open(f, inode, NULL);
|
||||
if (error) {
|
||||
fput(f);
|
||||
f = ERR_PTR(error);
|
||||
}
|
||||
f = alloc_empty_backing_file(flags, cred, user_file);
|
||||
if (IS_ERR(f))
|
||||
return f;
|
||||
|
||||
path_get(user_path);
|
||||
backing_file_set_user_path(f, user_path);
|
||||
f->f_path = *real_path;
|
||||
error = do_dentry_open(f, d_inode(real_path->dentry), NULL);
|
||||
if (error) {
|
||||
fput(f);
|
||||
f = ERR_PTR(error);
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
EXPORT_SYMBOL(open_with_fake_path);
|
||||
EXPORT_SYMBOL_GPL(backing_file_open);
|
||||
|
||||
#define WILL_CREATE(flags) (flags & (O_CREAT | __O_TMPFILE))
|
||||
#define O_PATH_FLAGS (O_DIRECTORY | O_NOFOLLOW | O_PATH | O_CLOEXEC)
|
||||
|
||||
@ -41,8 +41,9 @@ static char ovl_whatisit(struct inode *inode, struct inode *realinode)
|
||||
#define OVL_OPEN_FLAGS (O_NOATIME | FMODE_NONOTIFY)
|
||||
|
||||
static struct file *ovl_open_realfile(const struct file *file,
|
||||
struct inode *realinode)
|
||||
struct path *realpath)
|
||||
{
|
||||
struct inode *realinode = d_inode(realpath->dentry);
|
||||
struct inode *inode = file_inode(file);
|
||||
struct file *realfile;
|
||||
const struct cred *old_cred;
|
||||
@ -61,8 +62,8 @@ static struct file *ovl_open_realfile(const struct file *file,
|
||||
if (!inode_owner_or_capable(realinode))
|
||||
flags &= ~O_NOATIME;
|
||||
|
||||
realfile = open_with_fake_path(&file->f_path, flags, realinode,
|
||||
current_cred());
|
||||
realfile = backing_file_open(file,
|
||||
flags, realpath, current_cred());
|
||||
}
|
||||
revert_creds(old_cred);
|
||||
|
||||
@ -107,21 +108,21 @@ static int ovl_change_flags(struct file *file, unsigned int flags)
|
||||
static int ovl_real_fdget_meta(const struct file *file, struct fd *real,
|
||||
bool allow_meta)
|
||||
{
|
||||
struct inode *inode = file_inode(file);
|
||||
struct inode *realinode;
|
||||
struct dentry *dentry = file_dentry(file);
|
||||
struct path realpath;
|
||||
|
||||
real->flags = 0;
|
||||
real->file = file->private_data;
|
||||
|
||||
if (allow_meta)
|
||||
realinode = ovl_inode_real(inode);
|
||||
ovl_path_real(dentry, &realpath);
|
||||
else
|
||||
realinode = ovl_inode_realdata(inode);
|
||||
ovl_path_realdata(dentry, &realpath);
|
||||
|
||||
/* Has it been copied up since we'd opened it? */
|
||||
if (unlikely(file_inode(real->file) != realinode)) {
|
||||
if (unlikely(file_inode(real->file) != d_inode(realpath.dentry))) {
|
||||
real->flags = FDPUT_FPUT;
|
||||
real->file = ovl_open_realfile(file, realinode);
|
||||
real->file = ovl_open_realfile(file, &realpath);
|
||||
|
||||
return PTR_ERR_OR_ZERO(real->file);
|
||||
}
|
||||
@ -147,17 +148,20 @@ static int ovl_real_fdget(const struct file *file, struct fd *real)
|
||||
|
||||
static int ovl_open(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct dentry *dentry = file_dentry(file);
|
||||
struct file *realfile;
|
||||
struct path realpath;
|
||||
int err;
|
||||
|
||||
err = ovl_maybe_copy_up(file_dentry(file), file->f_flags);
|
||||
err = ovl_maybe_copy_up(dentry, file->f_flags);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* No longer need these flags, so don't pass them on to underlying fs */
|
||||
file->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
|
||||
|
||||
realfile = ovl_open_realfile(file, ovl_inode_realdata(inode));
|
||||
ovl_path_realdata(dentry, &realpath);
|
||||
realfile = ovl_open_realfile(file, &realpath);
|
||||
if (IS_ERR(realfile))
|
||||
return PTR_ERR(realfile);
|
||||
|
||||
@ -492,6 +496,11 @@ static int ovl_mmap(struct file *file, struct vm_area_struct *vma)
|
||||
vma_set_file(vma, realfile);
|
||||
|
||||
old_cred = ovl_override_creds(file_inode(file)->i_sb);
|
||||
ret = security_mmap_backing_file(vma, realfile, file);
|
||||
if (ret) {
|
||||
revert_creds(old_cred);
|
||||
return ret;
|
||||
}
|
||||
ret = call_mmap(vma->vm_file, vma);
|
||||
revert_creds(old_cred);
|
||||
ovl_file_accessed(file);
|
||||
|
||||
@ -270,6 +270,7 @@ void ovl_path_upper(struct dentry *dentry, struct path *path);
|
||||
void ovl_path_lower(struct dentry *dentry, struct path *path);
|
||||
void ovl_path_lowerdata(struct dentry *dentry, struct path *path);
|
||||
enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path);
|
||||
enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path);
|
||||
struct dentry *ovl_dentry_upper(struct dentry *dentry);
|
||||
struct dentry *ovl_dentry_lower(struct dentry *dentry);
|
||||
struct dentry *ovl_dentry_lowerdata(struct dentry *dentry);
|
||||
|
||||
@ -84,14 +84,22 @@ static struct dentry *ovl_d_real(struct dentry *dentry,
|
||||
{
|
||||
struct dentry *real = NULL, *lower;
|
||||
|
||||
/* It's an overlay file */
|
||||
/*
|
||||
* vfs is only expected to call d_real() with NULL from d_real_inode()
|
||||
* and with overlay inode from file_dentry() on an overlay file.
|
||||
*
|
||||
* TODO: remove @inode argument from d_real() API, remove code in this
|
||||
* function that deals with non-NULL @inode and remove d_real() call
|
||||
* from file_dentry().
|
||||
*/
|
||||
if (inode && d_inode(dentry) == inode)
|
||||
return dentry;
|
||||
else if (inode)
|
||||
goto bug;
|
||||
|
||||
if (!d_is_reg(dentry)) {
|
||||
if (!inode || inode == d_inode(dentry))
|
||||
return dentry;
|
||||
goto bug;
|
||||
/* d_real_inode() is only relevant for regular files */
|
||||
return dentry;
|
||||
}
|
||||
|
||||
real = ovl_dentry_upper(dentry);
|
||||
|
||||
@ -196,6 +196,20 @@ enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path)
|
||||
return type;
|
||||
}
|
||||
|
||||
enum ovl_path_type ovl_path_realdata(struct dentry *dentry, struct path *path)
|
||||
{
|
||||
enum ovl_path_type type = ovl_path_type(dentry);
|
||||
|
||||
WARN_ON_ONCE(d_is_dir(dentry));
|
||||
|
||||
if (!OVL_TYPE_UPPER(type) || OVL_TYPE_MERGE(type))
|
||||
ovl_path_lowerdata(dentry, path);
|
||||
else
|
||||
ovl_path_upper(dentry, path);
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
struct dentry *ovl_dentry_upper(struct dentry *dentry)
|
||||
{
|
||||
return ovl_upperdentry_dereference(OVL_I(d_inode(dentry)));
|
||||
|
||||
@ -2151,7 +2151,7 @@ static int map_files_get_link(struct dentry *dentry, struct path *path)
|
||||
rc = -ENOENT;
|
||||
vma = find_exact_vma(mm, vm_start, vm_end);
|
||||
if (vma && vma->vm_file) {
|
||||
*path = vma->vm_file->f_path;
|
||||
*path = *file_user_path(vma->vm_file);
|
||||
path_get(path);
|
||||
rc = 0;
|
||||
}
|
||||
|
||||
@ -64,7 +64,7 @@ static int nommu_region_show(struct seq_file *m, struct vm_region *region)
|
||||
|
||||
if (file) {
|
||||
seq_pad(m, ' ');
|
||||
seq_file_path(m, file, "");
|
||||
seq_path(m, file_user_path(file), "");
|
||||
}
|
||||
|
||||
seq_putc(m, '\n');
|
||||
|
||||
@ -321,7 +321,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma)
|
||||
*/
|
||||
if (file) {
|
||||
seq_pad(m, ' ');
|
||||
seq_file_path(m, file, "\n");
|
||||
seq_path(m, file_user_path(file), "\n");
|
||||
goto done;
|
||||
}
|
||||
|
||||
@ -1802,7 +1802,7 @@ static int show_numa_map(struct seq_file *m, void *v)
|
||||
|
||||
if (file) {
|
||||
seq_puts(m, " file=");
|
||||
seq_file_path(m, file, "\n\t= ");
|
||||
seq_path(m, file_user_path(file), "\n\t= ");
|
||||
} else if (vma->vm_start <= mm->brk && vma->vm_end >= mm->start_brk) {
|
||||
seq_puts(m, " heap");
|
||||
} else if (is_stack(vma)) {
|
||||
|
||||
@ -175,7 +175,7 @@ static int nommu_vma_show(struct seq_file *m, struct vm_area_struct *vma)
|
||||
|
||||
if (file) {
|
||||
seq_pad(m, ' ');
|
||||
seq_file_path(m, file, "");
|
||||
seq_path(m, file_user_path(file), "");
|
||||
} else if (mm && is_stack(vma)) {
|
||||
seq_pad(m, ' ');
|
||||
seq_printf(m, "[stack]");
|
||||
|
||||
@ -158,6 +158,9 @@ typedef int (dio_iodone_t)(struct kiocb *iocb, loff_t offset,
|
||||
|
||||
#define FMODE_OPENED ((__force fmode_t)0x80000)
|
||||
|
||||
/* File is embedded in backing_file object */
|
||||
#define FMODE_BACKING ((__force fmode_t)0x2000000)
|
||||
|
||||
/* File was opened by fanotify and shouldn't generate fanotify events */
|
||||
#define FMODE_NONOTIFY ((__force fmode_t)0x4000000)
|
||||
|
||||
@ -2613,9 +2616,42 @@ extern struct file *file_open_name(struct filename *, int, umode_t);
|
||||
extern struct file *filp_open(const char *, int, umode_t);
|
||||
extern struct file *file_open_root(struct dentry *, struct vfsmount *,
|
||||
const char *, int, umode_t);
|
||||
extern struct file * dentry_open(const struct path *, int, const struct cred *);
|
||||
extern struct file * open_with_fake_path(const struct path *, int,
|
||||
struct inode*, const struct cred *);
|
||||
struct file *dentry_open(const struct path *path, int flags,
|
||||
const struct cred *creds);
|
||||
struct file *backing_file_open(const struct file *user_file, int flags,
|
||||
const struct path *real_path,
|
||||
const struct cred *cred);
|
||||
struct path *backing_file_user_path(const struct file *f);
|
||||
|
||||
#ifdef CONFIG_SECURITY
|
||||
void *backing_file_security(const struct file *f);
|
||||
void backing_file_set_security(struct file *f, void *security);
|
||||
#else
|
||||
static inline void *backing_file_security(const struct file *f)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void backing_file_set_security(struct file *f, void *security)
|
||||
{
|
||||
}
|
||||
#endif /* CONFIG_SECURITY */
|
||||
|
||||
/*
|
||||
* file_user_path - get the path to display for memory mapped file
|
||||
*
|
||||
* When mmapping a file on a stackable filesystem (e.g., overlayfs), the file
|
||||
* stored in ->vm_file is a backing file whose f_inode is on the underlying
|
||||
* filesystem. When the mapped file path is displayed to user (e.g. via
|
||||
* /proc/<pid>/maps), this helper should be used to get the path to display
|
||||
* to the user, which is the path of the fd that user has requested to map.
|
||||
*/
|
||||
static inline const struct path *file_user_path(const struct file *f)
|
||||
{
|
||||
if (unlikely(f->f_mode & FMODE_BACKING))
|
||||
return backing_file_user_path(f);
|
||||
return &f->f_path;
|
||||
}
|
||||
|
||||
static inline struct file *file_clone_open(struct file *file)
|
||||
{
|
||||
return dentry_open(&file->f_path, file->f_flags, file->f_cred);
|
||||
|
||||
@ -90,7 +90,7 @@ struct common_audit_data {
|
||||
#endif
|
||||
char *kmod_name;
|
||||
struct lsm_ioctlop_audit *op;
|
||||
struct file *file;
|
||||
const struct file *file;
|
||||
struct lsm_ibpkey_audit *ibpkey;
|
||||
struct lsm_ibendport_audit *ibendport;
|
||||
} u;
|
||||
|
||||
@ -155,11 +155,16 @@ LSM_HOOK(int, 0, kernfs_init_security, struct kernfs_node *kn_dir,
|
||||
LSM_HOOK(int, 0, file_permission, struct file *file, int mask)
|
||||
LSM_HOOK(int, 0, file_alloc_security, struct file *file)
|
||||
LSM_HOOK(void, LSM_RET_VOID, file_free_security, struct file *file)
|
||||
LSM_HOOK(int, 0, backing_file_alloc, struct file *backing_file,
|
||||
const struct file *user_file)
|
||||
LSM_HOOK(void, LSM_RET_VOID, backing_file_free, struct file *backing_file)
|
||||
LSM_HOOK(int, 0, file_ioctl, struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
LSM_HOOK(int, 0, mmap_addr, unsigned long addr)
|
||||
LSM_HOOK(int, 0, mmap_file, struct file *file, unsigned long reqprot,
|
||||
unsigned long prot, unsigned long flags)
|
||||
LSM_HOOK(int, 0, mmap_backing_file, struct vm_area_struct *vma,
|
||||
struct file *backing_file, struct file *user_file)
|
||||
LSM_HOOK(int, 0, file_mprotect, struct vm_area_struct *vma,
|
||||
unsigned long reqprot, unsigned long prot)
|
||||
LSM_HOOK(int, 0, file_lock, struct file *file, unsigned int cmd)
|
||||
|
||||
@ -1501,6 +1501,7 @@ struct security_hook_list {
|
||||
struct lsm_blob_sizes {
|
||||
int lbs_cred;
|
||||
int lbs_file;
|
||||
int lbs_backing_file;
|
||||
int lbs_inode;
|
||||
int lbs_superblock;
|
||||
int lbs_ipc;
|
||||
|
||||
@ -307,9 +307,15 @@ int security_kernfs_init_security(struct kernfs_node *kn_dir,
|
||||
int security_file_permission(struct file *file, int mask);
|
||||
int security_file_alloc(struct file *file);
|
||||
void security_file_free(struct file *file);
|
||||
int security_backing_file_alloc(struct file *backing_file,
|
||||
const struct file *user_file);
|
||||
void security_backing_file_free(struct file *backing_file);
|
||||
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
|
||||
int security_mmap_file(struct file *file, unsigned long prot,
|
||||
unsigned long flags);
|
||||
int security_mmap_backing_file(struct vm_area_struct *vma,
|
||||
struct file *backing_file,
|
||||
struct file *user_file);
|
||||
int security_mmap_addr(unsigned long addr);
|
||||
int security_file_mprotect(struct vm_area_struct *vma, unsigned long reqprot,
|
||||
unsigned long prot);
|
||||
@ -848,6 +854,15 @@ static inline int security_file_alloc(struct file *file)
|
||||
static inline void security_file_free(struct file *file)
|
||||
{ }
|
||||
|
||||
static inline int security_backing_file_alloc(struct file *backing_file,
|
||||
const struct file *user_file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void security_backing_file_free(struct file *backing_file)
|
||||
{ }
|
||||
|
||||
static inline int security_file_ioctl(struct file *file, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
@ -860,6 +875,13 @@ static inline int security_mmap_file(struct file *file, unsigned long prot,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_mmap_backing_file(struct vm_area_struct *vma,
|
||||
struct file *backing_file,
|
||||
struct file *user_file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int security_mmap_addr(unsigned long addr)
|
||||
{
|
||||
return cap_mmap_addr(addr);
|
||||
|
||||
@ -8410,7 +8410,7 @@ static void perf_event_mmap_event(struct perf_mmap_event *mmap_event)
|
||||
* need to add enough zero bytes after the string to handle
|
||||
* the 64bit alignment we do later.
|
||||
*/
|
||||
name = file_path(file, buf, PATH_MAX - sizeof(u64));
|
||||
name = d_path(file_user_path(file), buf, PATH_MAX - sizeof(u64));
|
||||
if (IS_ERR(name)) {
|
||||
name = "//toolong";
|
||||
goto cpy_name;
|
||||
|
||||
@ -418,7 +418,7 @@ static int seq_print_user_ip(struct trace_seq *s, struct mm_struct *mm,
|
||||
vmstart = vma->vm_start;
|
||||
}
|
||||
if (file) {
|
||||
ret = trace_seq_path(s, &file->f_path);
|
||||
ret = trace_seq_path(s, file_user_path(file));
|
||||
if (ret)
|
||||
trace_seq_printf(s, "[+0x%lx]",
|
||||
ip - vmstart);
|
||||
|
||||
@ -45,6 +45,7 @@ struct security_hook_heads security_hook_heads __lsm_ro_after_init;
|
||||
static BLOCKING_NOTIFIER_HEAD(blocking_lsm_notifier_chain);
|
||||
|
||||
static struct kmem_cache *lsm_file_cache;
|
||||
static struct kmem_cache *lsm_backing_file_cache;
|
||||
static struct kmem_cache *lsm_inode_cache;
|
||||
|
||||
char *lsm_names;
|
||||
@ -166,6 +167,8 @@ static void __init lsm_set_blob_sizes(struct lsm_blob_sizes *needed)
|
||||
|
||||
lsm_set_blob_size(&needed->lbs_cred, &blob_sizes.lbs_cred);
|
||||
lsm_set_blob_size(&needed->lbs_file, &blob_sizes.lbs_file);
|
||||
lsm_set_blob_size(&needed->lbs_backing_file,
|
||||
&blob_sizes.lbs_backing_file);
|
||||
/*
|
||||
* The inode blob gets an rcu_head in addition to
|
||||
* what the modules might need.
|
||||
@ -304,13 +307,14 @@ static void __init ordered_lsm_init(void)
|
||||
for (lsm = ordered_lsms; *lsm; lsm++)
|
||||
prepare_lsm(*lsm);
|
||||
|
||||
init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
|
||||
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
|
||||
init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
|
||||
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
|
||||
init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
|
||||
init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
|
||||
init_debug("task blob size = %d\n", blob_sizes.lbs_task);
|
||||
init_debug("cred blob size = %d\n", blob_sizes.lbs_cred);
|
||||
init_debug("file blob size = %d\n", blob_sizes.lbs_file);
|
||||
init_debug("backing_file blob size = %d\n", blob_sizes.lbs_backing_file);
|
||||
init_debug("inode blob size = %d\n", blob_sizes.lbs_inode);
|
||||
init_debug("ipc blob size = %d\n", blob_sizes.lbs_ipc);
|
||||
init_debug("msg_msg blob size = %d\n", blob_sizes.lbs_msg_msg);
|
||||
init_debug("superblock blob size = %d\n", blob_sizes.lbs_superblock);
|
||||
init_debug("task blob size = %d\n", blob_sizes.lbs_task);
|
||||
|
||||
/*
|
||||
* Create any kmem_caches needed for blobs
|
||||
@ -319,6 +323,11 @@ static void __init ordered_lsm_init(void)
|
||||
lsm_file_cache = kmem_cache_create("lsm_file_cache",
|
||||
blob_sizes.lbs_file, 0,
|
||||
SLAB_PANIC, NULL);
|
||||
if (blob_sizes.lbs_backing_file)
|
||||
lsm_backing_file_cache = kmem_cache_create(
|
||||
"lsm_backing_file_cache",
|
||||
blob_sizes.lbs_backing_file,
|
||||
0, SLAB_PANIC, NULL);
|
||||
if (blob_sizes.lbs_inode)
|
||||
lsm_inode_cache = kmem_cache_create("lsm_inode_cache",
|
||||
blob_sizes.lbs_inode, 0,
|
||||
@ -456,6 +465,30 @@ int unregister_blocking_lsm_notifier(struct notifier_block *nb)
|
||||
}
|
||||
EXPORT_SYMBOL(unregister_blocking_lsm_notifier);
|
||||
|
||||
/**
|
||||
* lsm_backing_file_alloc - allocate a composite backing file blob
|
||||
* @backing_file: the backing file
|
||||
*
|
||||
* Allocate the backing file blob for all the modules.
|
||||
*
|
||||
* Returns 0, or -ENOMEM if memory can't be allocated.
|
||||
*/
|
||||
static int lsm_backing_file_alloc(struct file *backing_file)
|
||||
{
|
||||
void *blob;
|
||||
|
||||
if (!lsm_backing_file_cache) {
|
||||
backing_file_set_security(backing_file, NULL);
|
||||
return 0;
|
||||
}
|
||||
|
||||
blob = kmem_cache_zalloc(lsm_backing_file_cache, GFP_KERNEL);
|
||||
backing_file_set_security(backing_file, blob);
|
||||
if (!blob)
|
||||
return -ENOMEM;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* lsm_cred_alloc - allocate a composite cred blob
|
||||
* @cred: the cred that needs a blob
|
||||
@ -1462,6 +1495,57 @@ void security_file_free(struct file *file)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* security_backing_file_alloc() - Allocate and setup a backing file blob
|
||||
* @backing_file: the backing file
|
||||
* @user_file: the associated user visible file
|
||||
*
|
||||
* Allocate a backing file LSM blob and perform any necessary initialization of
|
||||
* the LSM blob. There will be some operations where the LSM will not have
|
||||
* access to @user_file after this point, so any important state associated
|
||||
* with @user_file that is important to the LSM should be captured in the
|
||||
* backing file's LSM blob.
|
||||
*
|
||||
* LSM's should avoid taking a reference to @user_file in this hook as it will
|
||||
* result in problems later when the system attempts to drop/put the file
|
||||
* references due to a circular dependency.
|
||||
*
|
||||
* Return: Return 0 if the hook is successful, negative values otherwise.
|
||||
*/
|
||||
int security_backing_file_alloc(struct file *backing_file,
|
||||
const struct file *user_file)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = lsm_backing_file_alloc(backing_file);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = call_int_hook(backing_file_alloc, 0, backing_file, user_file);
|
||||
if (unlikely(rc))
|
||||
security_backing_file_free(backing_file);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* security_backing_file_free() - Free a backing file blob
|
||||
* @backing_file: the backing file
|
||||
*
|
||||
* Free any LSM state associate with a backing file's LSM blob, including the
|
||||
* blob itself.
|
||||
*/
|
||||
void security_backing_file_free(struct file *backing_file)
|
||||
{
|
||||
void *blob = backing_file_security(backing_file);
|
||||
|
||||
call_void_hook(backing_file_free, backing_file);
|
||||
|
||||
if (blob) {
|
||||
backing_file_set_security(backing_file, NULL);
|
||||
kmem_cache_free(lsm_backing_file_cache, blob);
|
||||
}
|
||||
}
|
||||
|
||||
int security_file_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return call_int_hook(file_ioctl, 0, file, cmd, arg);
|
||||
@ -1512,6 +1596,32 @@ int security_mmap_file(struct file *file, unsigned long prot,
|
||||
return ima_file_mmap(file, prot);
|
||||
}
|
||||
|
||||
/**
|
||||
* security_mmap_backing_file - Check if mmap'ing a backing file is allowed
|
||||
* @vma: the vm_area_struct for the mmap'd region
|
||||
* @backing_file: the backing file being mmap'd
|
||||
* @user_file: the user file being mmap'd
|
||||
*
|
||||
* Check permissions for a mmap operation on a stacked filesystem. This hook
|
||||
* is called after the security_mmap_file() and is responsible for authorizing
|
||||
* the mmap on @backing_file. It is important to note that the mmap operation
|
||||
* on @user_file has already been authorized and the @vma->vm_file has been
|
||||
* set to @backing_file.
|
||||
*
|
||||
* Return: Returns 0 if permission is granted.
|
||||
*/
|
||||
int security_mmap_backing_file(struct vm_area_struct *vma,
|
||||
struct file *backing_file,
|
||||
struct file *user_file)
|
||||
{
|
||||
/* recommended by the stackable filesystem devs */
|
||||
if (WARN_ON_ONCE(!(backing_file->f_mode & FMODE_BACKING)))
|
||||
return -EIO;
|
||||
|
||||
return call_int_hook(mmap_backing_file, 0, vma, backing_file, user_file);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(security_mmap_backing_file);
|
||||
|
||||
int security_mmap_addr(unsigned long addr)
|
||||
{
|
||||
return call_int_hook(mmap_addr, 0, addr);
|
||||
|
||||
@ -1756,9 +1756,67 @@ static inline int file_path_has_perm(const struct cred *cred,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
static int bpf_fd_pass(struct file *file, u32 sid);
|
||||
static int bpf_fd_pass(const struct file *file, u32 sid);
|
||||
#endif
|
||||
|
||||
static int __file_has_perm(const struct cred *cred, const struct file *file,
|
||||
u32 av, bool bf_user_file)
|
||||
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
struct inode *inode;
|
||||
u32 ssid = cred_sid(cred);
|
||||
u32 tsid_fd;
|
||||
int rc;
|
||||
|
||||
if (bf_user_file) {
|
||||
struct backing_file_security_struct *bfsec;
|
||||
const struct path *path;
|
||||
|
||||
if (WARN_ON(!(file->f_mode & FMODE_BACKING)))
|
||||
return -EIO;
|
||||
|
||||
bfsec = selinux_backing_file(file);
|
||||
path = backing_file_user_path(file);
|
||||
tsid_fd = bfsec->uf_sid;
|
||||
inode = d_inode(path->dentry);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_PATH;
|
||||
ad.u.path = *path;
|
||||
} else {
|
||||
struct file_security_struct *fsec = selinux_file(file);
|
||||
|
||||
tsid_fd = fsec->sid;
|
||||
inode = file_inode(file);
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_FILE;
|
||||
ad.u.file = file;
|
||||
}
|
||||
|
||||
if (ssid != tsid_fd) {
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
ssid, tsid_fd,
|
||||
SECCLASS_FD,
|
||||
FD__USE,
|
||||
&ad);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
/* regardless of backing vs user file, use the underlying file here */
|
||||
rc = bpf_fd_pass(file, ssid);
|
||||
if (rc)
|
||||
return rc;
|
||||
#endif
|
||||
|
||||
/* av is zero if only checking access to the descriptor. */
|
||||
if (av)
|
||||
return inode_has_perm(cred, inode, av, &ad);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Check whether a task can use an open file descriptor to
|
||||
access an inode in a given way. Check access to the
|
||||
descriptor itself, and then use dentry_has_perm to
|
||||
@ -1767,42 +1825,10 @@ static int bpf_fd_pass(struct file *file, u32 sid);
|
||||
has the same SID as the process. If av is zero, then
|
||||
access to the file is not checked, e.g. for cases
|
||||
where only the descriptor is affected like seek. */
|
||||
static int file_has_perm(const struct cred *cred,
|
||||
struct file *file,
|
||||
u32 av)
|
||||
static inline int file_has_perm(const struct cred *cred,
|
||||
const struct file *file, u32 av)
|
||||
{
|
||||
struct file_security_struct *fsec = selinux_file(file);
|
||||
struct inode *inode = file_inode(file);
|
||||
struct common_audit_data ad;
|
||||
u32 sid = cred_sid(cred);
|
||||
int rc;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_FILE;
|
||||
ad.u.file = file;
|
||||
|
||||
if (sid != fsec->sid) {
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
sid, fsec->sid,
|
||||
SECCLASS_FD,
|
||||
FD__USE,
|
||||
&ad);
|
||||
if (rc)
|
||||
goto out;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_BPF_SYSCALL
|
||||
rc = bpf_fd_pass(file, cred_sid(cred));
|
||||
if (rc)
|
||||
return rc;
|
||||
#endif
|
||||
|
||||
/* av is zero if only checking access to the descriptor. */
|
||||
rc = 0;
|
||||
if (av)
|
||||
rc = inode_has_perm(cred, inode, av, &ad);
|
||||
|
||||
out:
|
||||
return rc;
|
||||
return __file_has_perm(cred, file, av, false);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3583,6 +3609,17 @@ static int selinux_file_alloc_security(struct file *file)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int selinux_backing_file_alloc(struct file *backing_file,
|
||||
const struct file *user_file)
|
||||
{
|
||||
struct backing_file_security_struct *bfsec;
|
||||
|
||||
bfsec = selinux_backing_file(backing_file);
|
||||
bfsec->uf_sid = selinux_file(user_file)->sid;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check whether a task has the ioctl permission and cmd
|
||||
* operation to an inode.
|
||||
@ -3675,43 +3712,56 @@ static int selinux_file_ioctl(struct file *file, unsigned int cmd,
|
||||
|
||||
static int default_noexec __ro_after_init;
|
||||
|
||||
static int file_map_prot_check(struct file *file, unsigned long prot, int shared)
|
||||
static int __file_map_prot_check(const struct cred *cred,
|
||||
const struct file *file, unsigned long prot,
|
||||
bool shared, bool mounter, bool bf_user_file)
|
||||
{
|
||||
const struct cred *cred = current_cred();
|
||||
u32 sid = cred_sid(cred);
|
||||
int rc = 0;
|
||||
struct inode *inode = NULL;
|
||||
bool prot_exec = prot & PROT_EXEC;
|
||||
bool prot_write = prot & PROT_WRITE;
|
||||
|
||||
if (file) {
|
||||
if (bf_user_file)
|
||||
inode = d_inode(backing_file_user_path(file)->dentry);
|
||||
else
|
||||
inode = file_inode(file);
|
||||
}
|
||||
|
||||
if (default_noexec && prot_exec &&
|
||||
(!file || IS_PRIVATE(inode) || (!shared && prot_write)) && !mounter) {
|
||||
int rc;
|
||||
u32 sid = cred_sid(cred);
|
||||
|
||||
if (default_noexec &&
|
||||
(prot & PROT_EXEC) && (!file || IS_PRIVATE(file_inode(file)) ||
|
||||
(!shared && (prot & PROT_WRITE)))) {
|
||||
/*
|
||||
* We are making executable an anonymous mapping or a
|
||||
* private file mapping that will also be writable.
|
||||
* This has an additional check.
|
||||
* We are making executable an anonymous mapping or a private
|
||||
* file mapping that will also be writable.
|
||||
*/
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
sid, sid, SECCLASS_PROCESS,
|
||||
PROCESS__EXECMEM, NULL);
|
||||
sid, sid, SECCLASS_PROCESS, PROCESS__EXECMEM,
|
||||
NULL);
|
||||
if (rc)
|
||||
goto error;
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (file) {
|
||||
/* read access is always possible with a mapping */
|
||||
/* "read" always possible, "write" only if shared */
|
||||
u32 av = FILE__READ;
|
||||
|
||||
/* write access only matters if the mapping is shared */
|
||||
if (shared && (prot & PROT_WRITE))
|
||||
if (shared && prot_write)
|
||||
av |= FILE__WRITE;
|
||||
|
||||
if (prot & PROT_EXEC)
|
||||
if (prot_exec)
|
||||
av |= FILE__EXECUTE;
|
||||
|
||||
return file_has_perm(cred, file, av);
|
||||
return __file_has_perm(cred, file, av, bf_user_file);
|
||||
}
|
||||
|
||||
error:
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int file_map_prot_check(const struct cred *cred,
|
||||
const struct file *file,
|
||||
unsigned long prot, bool shared, bool mounter)
|
||||
{
|
||||
return __file_map_prot_check(cred, file, prot, shared, mounter, false);
|
||||
}
|
||||
|
||||
static int selinux_mmap_addr(unsigned long addr)
|
||||
@ -3728,17 +3778,17 @@ static int selinux_mmap_addr(unsigned long addr)
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int selinux_mmap_file(struct file *file, unsigned long reqprot,
|
||||
unsigned long prot, unsigned long flags)
|
||||
static int selinux_mmap_file_common(const struct cred *cred, struct file *file,
|
||||
unsigned long reqprot, unsigned long prot,
|
||||
bool shared, bool mounter)
|
||||
{
|
||||
struct common_audit_data ad;
|
||||
int rc;
|
||||
|
||||
if (file) {
|
||||
int rc;
|
||||
struct common_audit_data ad;
|
||||
|
||||
ad.type = LSM_AUDIT_DATA_FILE;
|
||||
ad.u.file = file;
|
||||
rc = inode_has_perm(current_cred(), file_inode(file),
|
||||
FILE__MAP, &ad);
|
||||
rc = inode_has_perm(cred, file_inode(file), FILE__MAP, &ad);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
@ -3746,36 +3796,87 @@ static int selinux_mmap_file(struct file *file, unsigned long reqprot,
|
||||
if (selinux_state.checkreqprot)
|
||||
prot = reqprot;
|
||||
|
||||
return file_map_prot_check(file, prot,
|
||||
(flags & MAP_TYPE) == MAP_SHARED);
|
||||
return file_map_prot_check(cred, file, prot, shared, mounter);
|
||||
}
|
||||
|
||||
static int selinux_mmap_file(struct file *file, unsigned long reqprot,
|
||||
unsigned long prot, unsigned long flags)
|
||||
{
|
||||
return selinux_mmap_file_common(current_cred(), file, reqprot, prot,
|
||||
(flags & MAP_TYPE) == MAP_SHARED,
|
||||
false);
|
||||
}
|
||||
|
||||
/**
|
||||
* selinux_mmap_backing_file - Check mmap permissions on a backing file
|
||||
* @vma: memory region
|
||||
* @backing_file: stacked filesystem backing file
|
||||
* @user_file: user visible file
|
||||
*
|
||||
* This is called after selinux_mmap_file() on stacked filesystems, and it
|
||||
* is this function's responsibility to verify access to @backing_file and
|
||||
* setup the SELinux state for possible later use in the mprotect() code path.
|
||||
*
|
||||
* By the time this function is called, mmap() access to @user_file has already
|
||||
* been authorized and @vma->vm_file has been set to point to @backing_file.
|
||||
*
|
||||
* Return zero on success, negative values otherwise.
|
||||
*/
|
||||
static int selinux_mmap_backing_file(struct vm_area_struct *vma,
|
||||
struct file *backing_file,
|
||||
struct file *user_file __always_unused)
|
||||
{
|
||||
unsigned long prot = 0;
|
||||
|
||||
/* translate vma->vm_flags perms into PROT perms */
|
||||
if (vma->vm_flags & VM_READ)
|
||||
prot |= PROT_READ;
|
||||
if (vma->vm_flags & VM_WRITE)
|
||||
prot |= PROT_WRITE;
|
||||
if (vma->vm_flags & VM_EXEC)
|
||||
prot |= PROT_EXEC;
|
||||
|
||||
return selinux_mmap_file_common(backing_file->f_cred, backing_file,
|
||||
prot, prot, vma->vm_flags & VM_SHARED,
|
||||
true);
|
||||
}
|
||||
|
||||
static int selinux_file_mprotect(struct vm_area_struct *vma,
|
||||
unsigned long reqprot,
|
||||
unsigned long prot)
|
||||
{
|
||||
int rc;
|
||||
const struct cred *cred = current_cred();
|
||||
u32 sid = cred_sid(cred);
|
||||
const struct file *file = vma->vm_file;
|
||||
bool backing_file;
|
||||
bool shared = vma->vm_flags & VM_SHARED;
|
||||
|
||||
/* check if we need to trigger the "backing files are awful" mode */
|
||||
backing_file = file && (file->f_mode & FMODE_BACKING);
|
||||
|
||||
if (selinux_state.checkreqprot)
|
||||
prot = reqprot;
|
||||
|
||||
if (default_noexec &&
|
||||
(prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
|
||||
int rc = 0;
|
||||
if (vma->vm_start >= vma->vm_mm->start_brk &&
|
||||
vma->vm_end <= vma->vm_mm->brk) {
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
sid, sid, SECCLASS_PROCESS,
|
||||
PROCESS__EXECHEAP, NULL);
|
||||
} else if (!vma->vm_file &&
|
||||
if (rc)
|
||||
return rc;
|
||||
} else if (!file &&
|
||||
((vma->vm_start <= vma->vm_mm->start_stack &&
|
||||
vma->vm_end >= vma->vm_mm->start_stack) ||
|
||||
vma_is_stack_for_current(vma))) {
|
||||
rc = avc_has_perm(&selinux_state,
|
||||
sid, sid, SECCLASS_PROCESS,
|
||||
PROCESS__EXECSTACK, NULL);
|
||||
} else if (vma->vm_file && vma->anon_vma) {
|
||||
if (rc)
|
||||
return rc;
|
||||
} else if (file && vma->anon_vma) {
|
||||
/*
|
||||
* We are making executable a file mapping that has
|
||||
* had some COW done. Since pages might have been
|
||||
@ -3783,13 +3884,29 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
|
||||
* modified content. This typically should only
|
||||
* occur for text relocations.
|
||||
*/
|
||||
rc = file_has_perm(cred, vma->vm_file, FILE__EXECMOD);
|
||||
rc = __file_has_perm(cred, file, FILE__EXECMOD,
|
||||
backing_file);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (backing_file) {
|
||||
rc = file_has_perm(file->f_cred, file,
|
||||
FILE__EXECMOD);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rc = __file_map_prot_check(cred, file, prot, shared, false, backing_file);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (backing_file) {
|
||||
rc = file_map_prot_check(file->f_cred, file, prot, shared, true);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int selinux_file_lock(struct file *file, unsigned int cmd)
|
||||
@ -6745,7 +6862,7 @@ static u32 bpf_map_fmode_to_av(fmode_t fmode)
|
||||
* access the bpf object and that's why we have to add this additional check in
|
||||
* selinux_file_receive and selinux_binder_transfer_files.
|
||||
*/
|
||||
static int bpf_fd_pass(struct file *file, u32 sid)
|
||||
static int bpf_fd_pass(const struct file *file, u32 sid)
|
||||
{
|
||||
struct bpf_security_struct *bpfsec;
|
||||
struct bpf_prog *prog;
|
||||
@ -6842,6 +6959,7 @@ static void selinux_bpf_prog_free(struct bpf_prog_aux *aux)
|
||||
struct lsm_blob_sizes selinux_blob_sizes __lsm_ro_after_init = {
|
||||
.lbs_cred = sizeof(struct task_security_struct),
|
||||
.lbs_file = sizeof(struct file_security_struct),
|
||||
.lbs_backing_file = sizeof(struct backing_file_security_struct),
|
||||
.lbs_inode = sizeof(struct inode_security_struct),
|
||||
.lbs_ipc = sizeof(struct ipc_security_struct),
|
||||
.lbs_msg_msg = sizeof(struct msg_security_struct),
|
||||
@ -6994,8 +7112,10 @@ static struct security_hook_list selinux_hooks[] __lsm_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(file_ioctl, selinux_file_ioctl),
|
||||
LSM_HOOK_INIT(mmap_file, selinux_mmap_file),
|
||||
LSM_HOOK_INIT(mmap_backing_file, selinux_mmap_backing_file),
|
||||
LSM_HOOK_INIT(mmap_addr, selinux_mmap_addr),
|
||||
LSM_HOOK_INIT(file_mprotect, selinux_file_mprotect),
|
||||
LSM_HOOK_INIT(file_lock, selinux_file_lock),
|
||||
|
||||
@ -63,6 +63,10 @@ struct file_security_struct {
|
||||
u32 pseqno; /* Policy seqno at the time of file open */
|
||||
};
|
||||
|
||||
struct backing_file_security_struct {
|
||||
u32 uf_sid; /* associated user file fsec->sid */
|
||||
};
|
||||
|
||||
struct superblock_security_struct {
|
||||
u32 sid; /* SID of file system superblock */
|
||||
u32 def_sid; /* default SID for labeling */
|
||||
@ -161,6 +165,13 @@ static inline struct file_security_struct *selinux_file(const struct file *file)
|
||||
return file->f_security + selinux_blob_sizes.lbs_file;
|
||||
}
|
||||
|
||||
static inline struct backing_file_security_struct *
|
||||
selinux_backing_file(const struct file *backing_file)
|
||||
{
|
||||
void *blob = backing_file_security(backing_file);
|
||||
return blob + selinux_blob_sizes.lbs_backing_file;
|
||||
}
|
||||
|
||||
static inline struct inode_security_struct *selinux_inode(
|
||||
const struct inode *inode)
|
||||
{
|
||||
|
||||
Loading…
Reference in New Issue
Block a user