This patch is preparation, it adds a couple of helpers to read data and to get the cached permission checks during that ->read(). Currently INF entries share the same code, they do not implement specific ->open(), only ->read() coupled with callback calls. Doing permission checks during ->open() will not work and will only disturb the INF entries that do not need permission checks. Yes not all the INF entries need checks, the ones that need protections are listed below: /proc/<pid>/wchan /proc/<pid>/syscall /proc/<pid>/{auxv,io} (will be handled in next patches). So to have proper permission checks convert this INF entries to REG entries and use their open() and read() handlers to implement these checks. To achieve this we add the following helpers: * proc_read_from_buffer() a wrapper around simple_read_from_buffer(), it makes sure that count <= PROC_BLOCK_SIZE (3*1024) * pid_entry_read(): it will get a free page and pass it to the specific /proc/<pid>/$entry handler (callback). The handler is of the same types of the previous INF handlers, it will only receive an extra "permitted" argument that contains the cached permission check that was performed during ->open(). The handler is of type: typedef int (*proc_read_fn_t)(char *page, struct task_struct *task, int permitted); So the converted code for example: /proc/pid/wchan will be: Before: static int proc_pid_wchan(struct task_struct *task, char *buffer) After: static int proc_pid_wchan(char *buffer, struct task_struct *task, int permitted) The extra "permitted" can be used by the handler to allow/deny reads. And the whole read() of /proc/pid/wchan will be: static ssize_t wchan_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { ssize_t length; unsigned long page = 0UL; length = pid_entry_read(file, &page, proc_pid_wchan); if (length >= 0) { length = proc_read_from_buffer(buf, count, ppos, (char *)page, length); free_page(page); } return length; } Signed-off-by: Djalal Harouni <tixxdz@xxxxxxxxxx> --- fs/proc/base.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++------ fs/proc/internal.h | 3 +++ 2 files changed, 50 insertions(+), 6 deletions(-) diff --git a/fs/proc/base.c b/fs/proc/base.c index e442784..efe2a11 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -139,6 +139,50 @@ struct pid_entry { NULL, &proc_single_file_operations, \ { .proc_show = show } ) +/* 4K page size but our output routines use some slack for overruns */ +#define PROC_BLOCK_SIZE (3*1024) + +static ssize_t proc_read_from_buffer(void __user *to, size_t count, + loff_t *ppos, const void *from, + size_t available) +{ + if (count > PROC_BLOCK_SIZE) + count = PROC_BLOCK_SIZE; + + return simple_read_from_buffer(to, count, ppos, from, available); +} + +static ssize_t pid_entry_read(struct file *file, unsigned long *page, + proc_read_fn_t proc_read) +{ + unsigned long addr; + ssize_t length = -ESRCH; + struct task_struct *task; + struct inode *inode = file_inode(file); + int permitted = (unsigned long)(void *)file->private_data; + + task = get_proc_task(inode); + if (!task) + goto out_no_task; + + length = -ENOMEM; + addr = __get_free_page(GFP_TEMPORARY); + if (!addr) + goto out; + + length = proc_read((char *)addr, task, permitted); + if (length < 0) { + free_page(addr); + goto out; + } + + *page = addr; +out: + put_task_struct(task); +out_no_task: + return length; +} + /* * Count the number of hardlinks for the pid_entry table, excluding the . * and .. links. @@ -598,8 +642,6 @@ static const struct inode_operations proc_def_inode_operations = { .setattr = proc_setattr, }; -#define PROC_BLOCK_SIZE (3*1024) /* 4K page size but our output routines use some slack for overruns */ - static ssize_t proc_info_read(struct file * file, char __user * buf, size_t count, loff_t *ppos) { @@ -612,9 +654,6 @@ static ssize_t proc_info_read(struct file * file, char __user * buf, if (!task) goto out_no_task; - if (count > PROC_BLOCK_SIZE) - count = PROC_BLOCK_SIZE; - length = -ENOMEM; if (!(page = __get_free_page(GFP_TEMPORARY))) goto out; @@ -622,7 +661,9 @@ static ssize_t proc_info_read(struct file * file, char __user * buf, length = PROC_I(inode)->op.proc_read(task, (char*)page); if (length >= 0) - length = simple_read_from_buffer(buf, count, ppos, (char *)page, length); + length = proc_read_from_buffer(buf, count, ppos, + (char *)page, length); + free_page(page); out: put_task_struct(task); diff --git a/fs/proc/internal.h b/fs/proc/internal.h index 4f828fa..f5c452c 100644 --- a/fs/proc/internal.h +++ b/fs/proc/internal.h @@ -78,6 +78,9 @@ struct proc_inode { struct inode vfs_inode; }; +typedef int (*proc_read_fn_t)(char *page, + struct task_struct *task, int permitted); + /* * General functions */ -- 1.7.11.7 -- To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html