Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c
Original file line number Diff line number Diff line change
Expand Up @@ -3042,7 +3042,7 @@ static const struct file_operations amdgpu_driver_kms_fops = {
#ifdef CONFIG_PROC_FS
.show_fdinfo = drm_show_fdinfo,
#endif
.fop_flags = FOP_UNSIGNED_OFFSET,
.fop_flags = FOP_UNSIGNED_OFFSET | FOP_PERFMON_FDINFO,
};

int amdgpu_file_to_fpriv(struct file *filp, struct amdgpu_fpriv **fpriv)
Expand Down
18 changes: 18 additions & 0 deletions fs/proc/base.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@
#include <linux/user_namespace.h>
#include <linux/fs_parser.h>
#include <linux/fs_struct.h>
#include <linux/fdtable.h>
#include <linux/slab.h>
#include <linux/sched/autogroup.h>
#include <linux/sched/mm.h>
Expand Down Expand Up @@ -716,6 +717,23 @@ static bool proc_fd_access_allowed(struct inode *inode)
task = get_proc_task(inode);
if (task) {
allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);
if (!allowed && capable(CAP_PERFMON)) {
struct files_struct *files;

task_lock(task);
files = task->files;
if (files) {
struct file *file;

spin_lock(&files->file_lock);
file = files_lookup_fd_locked(files,
proc_fd(inode));
allowed = file && file->f_op->fop_flags &
FOP_PERFMON_FDINFO;
spin_unlock(&files->file_lock);
}
task_unlock(task);
}
put_task_struct(task);
}
return allowed;
Expand Down
28 changes: 27 additions & 1 deletion fs/proc/fd.c
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,35 @@ static int proc_fdinfo_permission(struct mnt_idmap *idmap, struct inode *inode,
int mask)
{
bool allowed = false;
struct task_struct *task = get_proc_task(inode);
struct task_struct *task;

task = get_proc_task(inode);
if (!task)
return -ESRCH;

allowed = ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS);

if (!allowed && capable(CAP_PERFMON)) {
struct files_struct *files;

if (S_ISDIR(inode->i_mode)) {
allowed = true;
} else {
task_lock(task);
files = task->files;
if (files) {
struct file *file;

spin_lock(&files->file_lock);
file = files_lookup_fd_locked(files, proc_fd(inode));
allowed = file && file->f_op->fop_flags &
FOP_PERFMON_FDINFO;
spin_unlock(&files->file_lock);
}
task_unlock(task);
}
}

put_task_struct(task);

if (!allowed)
Expand Down Expand Up @@ -338,6 +361,9 @@ int proc_fd_permission(struct mnt_idmap *idmap,
if (rv == 0)
return rv;

if (capable(CAP_PERFMON))
return 0;

rcu_read_lock();
p = pid_task(proc_pid(inode), PIDTYPE_PID);
if (p && same_thread_group(p, current))
Expand Down
2 changes: 2 additions & 0 deletions include/linux/fs.h
Original file line number Diff line number Diff line change
Expand Up @@ -1985,6 +1985,8 @@ struct file_operations {
#define FOP_ASYNC_LOCK ((__force fop_flags_t)(1 << 6))
/* File system supports uncached read/write buffered IO */
#define FOP_DONTCACHE ((__force fop_flags_t)(1 << 7))
/* fdinfo readable with CAP_SYS_PERFMON */
#define FOP_PERFMON_FDINFO ((__force fop_flags_t)(1 << 8))

/* Wrap a directory iterator that needs exclusive inode access */
int wrap_directory_iterator(struct file *, struct dir_context *,
Expand Down
Loading