3990 lines · 95.9 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *  linux/fs/proc/base.c * *  Copyright (C) 1991, 1992 Linus Torvalds * *  proc base directory handling functions * *  1999, Al Viro. Rewritten. Now it covers the whole per-process part. *  Instead of using magical inumbers to determine the kind of object *  we allocate and fill in-core inodes upon lookup. They don't even *  go into icache. We cache the reference to task_struct upon lookup too. *  Eventually it should become a filesystem in its own. We don't use the *  rest of procfs anymore. * * *  Changelog: *  17-Jan-2005 *  Allan Bezerra *  Bruna Moreira <bruna.moreira@indt.org.br> *  Edjard Mota <edjard.mota@indt.org.br> *  Ilias Biris <ilias.biris@indt.org.br> *  Mauricio Lin <mauricio.lin@indt.org.br> * *  Embedded Linux Lab - 10LE Instituto Nokia de Tecnologia - INdT * *  A new process specific entry (smaps) included in /proc. It shows the *  size of rss for each memory area. The maps entry lacks information *  about physical memory size (rss) for each mapped file, i.e., *  rss information for executables and library files. *  This additional information is useful for any tools that need to know *  about physical memory consumption for a process specific library. * *  Changelog: *  21-Feb-2005 *  Embedded Linux Lab - 10LE Instituto Nokia de Tecnologia - INdT *  Pud inclusion in the page table walking. * *  ChangeLog: *  10-Mar-2005 *  10LE Instituto Nokia de Tecnologia - INdT: *  A better way to walks through the page table as suggested by Hugh Dickins. * *  Simo Piiroinen <simo.piiroinen@nokia.com>: *  Smaps information related to shared, private, clean and dirty pages. * *  Paul Mundt <paul.mundt@nokia.com>: *  Overall revision about smaps. */ #include <linux/uaccess.h> #include <linux/errno.h>#include <linux/time.h>#include <linux/proc_fs.h>#include <linux/stat.h>#include <linux/task_io_accounting_ops.h>#include <linux/init.h>#include <linux/capability.h>#include <linux/file.h>#include <linux/generic-radix-tree.h>#include <linux/string.h>#include <linux/seq_file.h>#include <linux/namei.h>#include <linux/mnt_namespace.h>#include <linux/mm.h>#include <linux/swap.h>#include <linux/rcupdate.h>#include <linux/kallsyms.h>#include <linux/stacktrace.h>#include <linux/resource.h>#include <linux/module.h>#include <linux/mount.h>#include <linux/security.h>#include <linux/ptrace.h>#include <linux/printk.h>#include <linux/cache.h>#include <linux/cgroup.h>#include <linux/cpuset.h>#include <linux/audit.h>#include <linux/poll.h>#include <linux/nsproxy.h>#include <linux/oom.h>#include <linux/elf.h>#include <linux/pid_namespace.h>#include <linux/user_namespace.h>#include <linux/fs_parser.h>#include <linux/fs_struct.h>#include <linux/slab.h>#include <linux/sched/autogroup.h>#include <linux/sched/mm.h>#include <linux/sched/coredump.h>#include <linux/sched/debug.h>#include <linux/sched/exec_state.h>#include <linux/sched/stat.h>#include <linux/posix-timers.h>#include <linux/time_namespace.h>#include <linux/resctrl.h>#include <linux/cn_proc.h>#include <linux/ksm.h>#include <uapi/linux/lsm.h>#include <trace/events/oom.h>#include "internal.h"#include "fd.h" #include "../../lib/kstrtox.h" /* NOTE: *	Implementing inode permission operations in /proc is almost *	certainly an error.  Permission checks need to happen during *	each system call not at open time.  The reason is that most of *	what we wish to check for permissions in /proc varies at runtime. * *	The classic example of a problem is opening file descriptors *	in /proc for a task before it execs a suid executable. */ static u8 nlink_tid __ro_after_init;static u8 nlink_tgid __ro_after_init; enum proc_mem_force {	PROC_MEM_FORCE_ALWAYS,	PROC_MEM_FORCE_PTRACE,	PROC_MEM_FORCE_NEVER}; static enum proc_mem_force proc_mem_force_override __ro_after_init =	IS_ENABLED(CONFIG_PROC_MEM_NO_FORCE) ? PROC_MEM_FORCE_NEVER :	IS_ENABLED(CONFIG_PROC_MEM_FORCE_PTRACE) ? PROC_MEM_FORCE_PTRACE :	PROC_MEM_FORCE_ALWAYS; static const struct constant_table proc_mem_force_table[] __initconst = {	{ "always", PROC_MEM_FORCE_ALWAYS },	{ "ptrace", PROC_MEM_FORCE_PTRACE },	{ "never", PROC_MEM_FORCE_NEVER },	{ }}; static int __init early_proc_mem_force_override(char *buf){	if (!buf)		return -EINVAL; 	/*	 * lookup_constant() defaults to proc_mem_force_override to preseve	 * the initial Kconfig choice in case an invalid param gets passed.	 */	proc_mem_force_override = lookup_constant(proc_mem_force_table,						  buf, proc_mem_force_override); 	return 0;}early_param("proc_mem.force_override", early_proc_mem_force_override); struct pid_entry {	const char *name;	unsigned int len;	umode_t mode;	const struct inode_operations *iop;	const struct file_operations *fop;	union proc_op op;}; #define NOD(NAME, MODE, IOP, FOP, OP) {			\	.name = (NAME),					\	.len  = sizeof(NAME) - 1,			\	.mode = MODE,					\	.iop  = IOP,					\	.fop  = FOP,					\	.op   = OP,					\} #define DIR(NAME, MODE, iops, fops)	\	NOD(NAME, (S_IFDIR|(MODE)), &iops, &fops, {} )#define LNK(NAME, get_link)					\	NOD(NAME, (S_IFLNK|S_IRWXUGO),				\		&proc_pid_link_inode_operations, NULL,		\		{ .proc_get_link = get_link } )#define REG(NAME, MODE, fops)				\	NOD(NAME, (S_IFREG|(MODE)), NULL, &fops, {})#define ONE(NAME, MODE, show)				\	NOD(NAME, (S_IFREG|(MODE)),			\		NULL, &proc_single_file_operations,	\		{ .proc_show = show } )#define ATTR(LSMID, NAME, MODE)				\	NOD(NAME, (S_IFREG|(MODE)),			\		NULL, &proc_pid_attr_operations,	\		{ .lsmid = LSMID }) /* * Count the number of hardlinks for the pid_entry table, excluding the . * and .. links. */static unsigned int __init pid_entry_nlink(const struct pid_entry *entries,	unsigned int n){	unsigned int i;	unsigned int count; 	count = 2;	for (i = 0; i < n; ++i) {		if (S_ISDIR(entries[i].mode))			++count;	} 	return count;} static int get_task_root(struct task_struct *task, struct path *root){	int result = -ENOENT; 	task_lock(task);	if (task->real_fs) {		get_fs_root(task->real_fs, root);		result = 0;	}	task_unlock(task);	return result;} static int proc_cwd_link(struct dentry *dentry, struct path *path,			 struct task_struct *task){	int result = -ENOENT; 	task_lock(task);	if (task->real_fs) {		get_fs_pwd(task->real_fs, path);		result = 0;	}	task_unlock(task);	return result;} static int proc_root_link(struct dentry *dentry, struct path *path,			  struct task_struct *task){	return get_task_root(task, path);} /* * If the user used setproctitle(), we just get the string from * user space at arg_start, and limit it to a maximum of one page. */static ssize_t get_mm_proctitle(struct mm_struct *mm, char __user *buf,				size_t count, unsigned long pos,				unsigned long arg_start){	char *page;	int ret, got; 	if (pos >= PAGE_SIZE)		return 0; 	page = kmalloc(PAGE_SIZE, GFP_KERNEL);	if (!page)		return -ENOMEM; 	ret = 0;	got = access_remote_vm(mm, arg_start, page, PAGE_SIZE, FOLL_ANON);	if (got > 0) {		int len = strnlen(page, got); 		/* Include the NUL character if it was found */		if (len < got)			len++; 		if (len > pos) {			len -= pos;			if (len > count)				len = count;			len -= copy_to_user(buf, page+pos, len);			if (!len)				len = -EFAULT;			ret = len;		}	}	kfree(page);	return ret;} static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,			      size_t count, loff_t *ppos){	unsigned long arg_start, arg_end, env_start, env_end;	unsigned long pos, len;	char *page, c; 	/* Check if process spawned far enough to have cmdline. */	if (!mm->env_end)		return 0; 	spin_lock(&mm->arg_lock);	arg_start = mm->arg_start;	arg_end = mm->arg_end;	env_start = mm->env_start;	env_end = mm->env_end;	spin_unlock(&mm->arg_lock); 	if (arg_start >= arg_end)		return 0; 	/*	 * We allow setproctitle() to overwrite the argument	 * strings, and overflow past the original end. But	 * only when it overflows into the environment area.	 */	if (env_start != arg_end || env_end < env_start)		env_start = env_end = arg_end;	len = env_end - arg_start; 	/* We're not going to care if "*ppos" has high bits set */	pos = *ppos;	if (pos >= len)		return 0;	if (count > len - pos)		count = len - pos;	if (!count)		return 0; 	/*	 * Magical special case: if the argv[] end byte is not	 * zero, the user has overwritten it with setproctitle(3).	 *	 * Possible future enhancement: do this only once when	 * pos is 0, and set a flag in the 'struct file'.	 */	if (access_remote_vm(mm, arg_end-1, &c, 1, FOLL_ANON) == 1 && c)		return get_mm_proctitle(mm, buf, count, pos, arg_start); 	/*	 * For the non-setproctitle() case we limit things strictly	 * to the [arg_start, arg_end[ range.	 */	pos += arg_start;	if (pos < arg_start || pos >= arg_end)		return 0;	if (count > arg_end - pos)		count = arg_end - pos; 	page = kmalloc(PAGE_SIZE, GFP_KERNEL);	if (!page)		return -ENOMEM; 	len = 0;	while (count) {		int got;		size_t size = min_t(size_t, PAGE_SIZE, count); 		got = access_remote_vm(mm, pos, page, size, FOLL_ANON);		if (got <= 0)			break;		got -= copy_to_user(buf, page, got);		if (unlikely(!got)) {			if (!len)				len = -EFAULT;			break;		}		pos += got;		buf += got;		len += got;		count -= got;	} 	kfree(page);	return len;} static ssize_t get_task_cmdline(struct task_struct *tsk, char __user *buf,				size_t count, loff_t *pos){	struct mm_struct *mm;	ssize_t ret; 	mm = get_task_mm(tsk);	if (!mm)		return 0; 	ret = get_mm_cmdline(mm, buf, count, pos);	mmput(mm);	return ret;} static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,				     size_t count, loff_t *pos){	struct task_struct *tsk;	ssize_t ret; 	BUG_ON(*pos < 0); 	tsk = get_proc_task(file_inode(file));	if (!tsk)		return -ESRCH;	ret = get_task_cmdline(tsk, buf, count, pos);	put_task_struct(tsk);	if (ret > 0)		*pos += ret;	return ret;} static const struct file_operations proc_pid_cmdline_ops = {	.read	= proc_pid_cmdline_read,	.llseek	= generic_file_llseek,}; #ifdef CONFIG_KALLSYMS/* * Provides a wchan file via kallsyms in a proper one-value-per-file format. * Returns the resolved symbol to user space. */static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,			  struct pid *pid, struct task_struct *task){	unsigned long wchan;	char symname[KSYM_NAME_LEN];	int err; 	err = down_read_killable(&task->signal->exec_update_lock);	if (err)		return err;	if (!ptrace_may_access(task, PTRACE_MODE_READ_FSCREDS))		goto print0; 	wchan = get_wchan(task);	if (wchan && !lookup_symbol_name(wchan, symname)) {		seq_puts(m, symname);		up_read(&task->signal->exec_update_lock);		return 0;	} print0:	seq_putc(m, '0');	up_read(&task->signal->exec_update_lock);	return 0;}#endif /* CONFIG_KALLSYMS */ static int lock_trace(struct task_struct *task){	int err = down_read_killable(&task->signal->exec_update_lock);	if (err)		return err;	if (!ptrace_may_access(task, PTRACE_MODE_ATTACH_FSCREDS)) {		up_read(&task->signal->exec_update_lock);		return -EPERM;	}	return 0;} static void unlock_trace(struct task_struct *task){	up_read(&task->signal->exec_update_lock);} #ifdef CONFIG_STACKTRACE #define MAX_STACK_TRACE_DEPTH	64 static int proc_pid_stack(struct seq_file *m, struct pid_namespace *ns,			  struct pid *pid, struct task_struct *task){	unsigned long *entries;	int err; 	/*	 * The ability to racily run the kernel stack unwinder on a running task	 * and then observe the unwinder output is scary; while it is useful for	 * debugging kernel issues, it can also allow an attacker to leak kernel	 * stack contents.	 * Doing this in a manner that is at least safe from races would require	 * some work to ensure that the remote task can not be scheduled; and	 * even then, this would still expose the unwinder as local attack	 * surface.	 * Therefore, this interface is restricted to root.	 */	if (!file_ns_capable(m->file, &init_user_ns, CAP_SYS_ADMIN))		return -EACCES; 	entries = kmalloc_array(MAX_STACK_TRACE_DEPTH, sizeof(*entries),				GFP_KERNEL);	if (!entries)		return -ENOMEM; 	err = lock_trace(task);	if (!err) {		unsigned int i, nr_entries; 		nr_entries = stack_trace_save_tsk(task, entries,						  MAX_STACK_TRACE_DEPTH, 0); 		for (i = 0; i < nr_entries; i++) {			seq_printf(m, "[<0>] %pB\n", (void *)entries[i]);		} 		unlock_trace(task);	}	kfree(entries); 

Lines 1–500 of 3990.