summaryrefslogtreecommitdiffstats
path: root/fs/proc/base.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/proc/base.c')
-rw-r--r--fs/proc/base.c302
1 files changed, 216 insertions, 86 deletions
diff --git a/fs/proc/base.c b/fs/proc/base.c
index 989af5e55d1..3c41149dea8 100644
--- a/fs/proc/base.c
+++ b/fs/proc/base.c
@@ -61,9 +61,9 @@
#include <linux/namei.h>
#include <linux/mnt_namespace.h>
#include <linux/mm.h>
-#include <linux/smp_lock.h>
#include <linux/rcupdate.h>
#include <linux/kallsyms.h>
+#include <linux/module.h>
#include <linux/mount.h>
#include <linux/security.h>
#include <linux/ptrace.h>
@@ -90,8 +90,8 @@
#define PROC_NUMBUF 13
struct pid_entry {
- int len;
char *name;
+ int len;
mode_t mode;
const struct inode_operations *iop;
const struct file_operations *fop;
@@ -99,8 +99,8 @@ struct pid_entry {
};
#define NOD(NAME, MODE, IOP, FOP, OP) { \
- .len = sizeof(NAME) - 1, \
.name = (NAME), \
+ .len = sizeof(NAME) - 1, \
.mode = MODE, \
.iop = IOP, \
.fop = FOP, \
@@ -123,6 +123,9 @@ struct pid_entry {
NULL, &proc_info_file_operations, \
{ .proc_read = &proc_##OTYPE } )
+int maps_protect;
+EXPORT_SYMBOL(maps_protect);
+
static struct fs_struct *get_fs_struct(struct task_struct *task)
{
struct fs_struct *fs;
@@ -275,17 +278,15 @@ static int proc_pid_auxv(struct task_struct *task, char *buffer)
*/
static int proc_pid_wchan(struct task_struct *task, char *buffer)
{
- char *modname;
- const char *sym_name;
- unsigned long wchan, size, offset;
- char namebuf[KSYM_NAME_LEN+1];
+ unsigned long wchan;
+ char symname[KSYM_NAME_LEN+1];
wchan = get_wchan(task);
- sym_name = kallsyms_lookup(wchan, &size, &offset, &modname, namebuf);
- if (sym_name)
- return sprintf(buffer, "%s", sym_name);
- return sprintf(buffer, "%lu", wchan);
+ if (lookup_symbol_name(wchan, symname) < 0)
+ return sprintf(buffer, "%lu", wchan);
+ else
+ return sprintf(buffer, "%s", symname);
}
#endif /* CONFIG_KALLSYMS */
@@ -310,7 +311,9 @@ static int proc_oom_score(struct task_struct *task, char *buffer)
struct timespec uptime;
do_posix_clock_monotonic_gettime(&uptime);
+ read_lock(&tasklist_lock);
points = badness(task, uptime.tv_sec);
+ read_unlock(&tasklist_lock);
return sprintf(buffer, "%lu\n", points);
}
@@ -344,11 +347,8 @@ static int proc_setattr(struct dentry *dentry, struct iattr *attr)
return -EPERM;
error = inode_change_ok(inode, attr);
- if (!error) {
- error = security_inode_setattr(dentry, attr);
- if (!error)
- error = inode_setattr(inode, attr);
- }
+ if (!error)
+ error = inode_setattr(inode, attr);
return error;
}
@@ -660,7 +660,6 @@ static ssize_t oom_adjust_read(struct file *file, char __user *buf,
char buffer[PROC_NUMBUF];
size_t len;
int oom_adjust;
- loff_t __ppos = *ppos;
if (!task)
return -ESRCH;
@@ -668,14 +667,8 @@ static ssize_t oom_adjust_read(struct file *file, char __user *buf,
put_task_struct(task);
len = snprintf(buffer, sizeof(buffer), "%i\n", oom_adjust);
- if (__ppos >= len)
- return 0;
- if (count > len-__ppos)
- count = len-__ppos;
- if (copy_to_user(buf, buffer + __ppos, count))
- return -EFAULT;
- *ppos = __ppos + count;
- return count;
+
+ return simple_read_from_buffer(buf, count, ppos, buffer, len);
}
static ssize_t oom_adjust_write(struct file *file, const char __user *buf,
@@ -715,6 +708,40 @@ static const struct file_operations proc_oom_adjust_operations = {
.write = oom_adjust_write,
};
+static ssize_t clear_refs_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct task_struct *task;
+ char buffer[PROC_NUMBUF], *end;
+ struct mm_struct *mm;
+
+ memset(buffer, 0, sizeof(buffer));
+ if (count > sizeof(buffer) - 1)
+ count = sizeof(buffer) - 1;
+ if (copy_from_user(buffer, buf, count))
+ return -EFAULT;
+ if (!simple_strtol(buffer, &end, 0))
+ return -EINVAL;
+ if (*end == '\n')
+ end++;
+ task = get_proc_task(file->f_path.dentry->d_inode);
+ if (!task)
+ return -ESRCH;
+ mm = get_task_mm(task);
+ if (mm) {
+ clear_refs_smap(mm);
+ mmput(mm);
+ }
+ put_task_struct(task);
+ if (end - buffer == 0)
+ return -EIO;
+ return end - buffer;
+}
+
+static struct file_operations proc_clear_refs_operations = {
+ .write = clear_refs_write,
+};
+
#ifdef CONFIG_AUDITSYSCALL
#define TMPBUFLEN 21
static ssize_t proc_loginuid_read(struct file * file, char __user * buf,
@@ -789,7 +816,6 @@ static ssize_t seccomp_read(struct file *file, char __user *buf,
{
struct task_struct *tsk = get_proc_task(file->f_dentry->d_inode);
char __buf[20];
- loff_t __ppos = *ppos;
size_t len;
if (!tsk)
@@ -797,14 +823,8 @@ static ssize_t seccomp_read(struct file *file, char __user *buf,
/* no need to print the trailing zero, so use only len */
len = sprintf(__buf, "%u\n", tsk->seccomp.mode);
put_task_struct(tsk);
- if (__ppos >= len)
- return 0;
- if (count > len - __ppos)
- count = len - __ppos;
- if (copy_to_user(buf, __buf + __ppos, count))
- return -EFAULT;
- *ppos = __ppos + count;
- return count;
+
+ return simple_read_from_buffer(buf, count, ppos, __buf, len);
}
static ssize_t seccomp_write(struct file *file, const char __user *buf,
@@ -863,7 +883,6 @@ static ssize_t proc_fault_inject_read(struct file * file, char __user * buf,
char buffer[PROC_NUMBUF];
size_t len;
int make_it_fail;
- loff_t __ppos = *ppos;
if (!task)
return -ESRCH;
@@ -871,14 +890,8 @@ static ssize_t proc_fault_inject_read(struct file * file, char __user * buf,
put_task_struct(task);
len = snprintf(buffer, sizeof(buffer), "%i\n", make_it_fail);
- if (__ppos >= len)
- return 0;
- if (count > len-__ppos)
- count = len-__ppos;
- if (copy_to_user(buf, buffer + __ppos, count))
- return -EFAULT;
- *ppos = __ppos + count;
- return count;
+
+ return simple_read_from_buffer(buf, count, ppos, buffer, len);
}
static ssize_t proc_fault_inject_write(struct file * file,
@@ -941,7 +954,7 @@ static int do_proc_readlink(struct dentry *dentry, struct vfsmount *mnt,
if (!tmp)
return -ENOMEM;
-
+
inode = dentry->d_inode;
path = d_path(dentry, mnt, tmp, PAGE_SIZE);
len = PTR_ERR(path);
@@ -1121,7 +1134,8 @@ static struct dentry_operations pid_dentry_operations =
/* Lookups */
-typedef struct dentry *instantiate_t(struct inode *, struct dentry *, struct task_struct *, void *);
+typedef struct dentry *instantiate_t(struct inode *, struct dentry *,
+ struct task_struct *, const void *);
/*
* Fill a directory entry.
@@ -1137,7 +1151,7 @@ typedef struct dentry *instantiate_t(struct inode *, struct dentry *, struct tas
*/
static int proc_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
char *name, int len,
- instantiate_t instantiate, struct task_struct *task, void *ptr)
+ instantiate_t instantiate, struct task_struct *task, const void *ptr)
{
struct dentry *child, *dir = filp->f_path.dentry;
struct inode *inode;
@@ -1199,7 +1213,10 @@ out:
return ~0U;
}
-static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsmount **mnt)
+#define PROC_FDINFO_MAX 64
+
+static int proc_fd_info(struct inode *inode, struct dentry **dentry,
+ struct vfsmount **mnt, char *info)
{
struct task_struct *task = get_proc_task(inode);
struct files_struct *files = NULL;
@@ -1218,8 +1235,16 @@ static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsm
spin_lock(&files->file_lock);
file = fcheck_files(files, fd);
if (file) {
- *mnt = mntget(file->f_path.mnt);
- *dentry = dget(file->f_path.dentry);
+ if (mnt)
+ *mnt = mntget(file->f_path.mnt);
+ if (dentry)
+ *dentry = dget(file->f_path.dentry);
+ if (info)
+ snprintf(info, PROC_FDINFO_MAX,
+ "pos:\t%lli\n"
+ "flags:\t0%o\n",
+ (long long) file->f_pos,
+ file->f_flags);
spin_unlock(&files->file_lock);
put_files_struct(files);
return 0;
@@ -1230,6 +1255,12 @@ static int proc_fd_link(struct inode *inode, struct dentry **dentry, struct vfsm
return -ENOENT;
}
+static int proc_fd_link(struct inode *inode, struct dentry **dentry,
+ struct vfsmount **mnt)
+{
+ return proc_fd_info(inode, dentry, mnt, NULL);
+}
+
static int tid_fd_revalidate(struct dentry *dentry, struct nameidata *nd)
{
struct inode *inode = dentry->d_inode;
@@ -1272,9 +1303,9 @@ static struct dentry_operations tid_fd_dentry_operations =
};
static struct dentry *proc_fd_instantiate(struct inode *dir,
- struct dentry *dentry, struct task_struct *task, void *ptr)
+ struct dentry *dentry, struct task_struct *task, const void *ptr)
{
- unsigned fd = *(unsigned *)ptr;
+ unsigned fd = *(const unsigned *)ptr;
struct file *file;
struct files_struct *files;
struct inode *inode;
@@ -1325,7 +1356,9 @@ out_iput:
goto out;
}
-static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry, struct nameidata *nd)
+static struct dentry *proc_lookupfd_common(struct inode *dir,
+ struct dentry *dentry,
+ instantiate_t instantiate)
{
struct task_struct *task = get_proc_task(dir);
unsigned fd = name_to_int(dentry);
@@ -1336,23 +1369,15 @@ static struct dentry *proc_lookupfd(struct inode * dir, struct dentry * dentry,
if (fd == ~0U)
goto out;
- result = proc_fd_instantiate(dir, dentry, task, &fd);
+ result = instantiate(dir, dentry, task, &fd);
out:
put_task_struct(task);
out_no_task:
return result;
}
-static int proc_fd_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
- struct task_struct *task, int fd)
-{
- char name[PROC_NUMBUF];
- int len = snprintf(name, sizeof(name), "%d", fd);
- return proc_fill_cache(filp, dirent, filldir, name, len,
- proc_fd_instantiate, task, &fd);
-}
-
-static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir)
+static int proc_readfd_common(struct file * filp, void * dirent,
+ filldir_t filldir, instantiate_t instantiate)
{
struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
@@ -1388,12 +1413,17 @@ static int proc_readfd(struct file * filp, void * dirent, filldir_t filldir)
for (fd = filp->f_pos-2;
fd < fdt->max_fds;
fd++, filp->f_pos++) {
+ char name[PROC_NUMBUF];
+ int len;
if (!fcheck_files(files, fd))
continue;
rcu_read_unlock();
- if (proc_fd_fill_cache(filp, dirent, filldir, p, fd) < 0) {
+ len = snprintf(name, sizeof(name), "%d", fd);
+ if (proc_fill_cache(filp, dirent, filldir,
+ name, len, instantiate,
+ p, &fd) < 0) {
rcu_read_lock();
break;
}
@@ -1408,23 +1438,119 @@ out_no_task:
return retval;
}
+static struct dentry *proc_lookupfd(struct inode *dir, struct dentry *dentry,
+ struct nameidata *nd)
+{
+ return proc_lookupfd_common(dir, dentry, proc_fd_instantiate);
+}
+
+static int proc_readfd(struct file *filp, void *dirent, filldir_t filldir)
+{
+ return proc_readfd_common(filp, dirent, filldir, proc_fd_instantiate);
+}
+
+static ssize_t proc_fdinfo_read(struct file *file, char __user *buf,
+ size_t len, loff_t *ppos)
+{
+ char tmp[PROC_FDINFO_MAX];
+ int err = proc_fd_info(file->f_path.dentry->d_inode, NULL, NULL, tmp);
+ if (!err)
+ err = simple_read_from_buffer(buf, len, ppos, tmp, strlen(tmp));
+ return err;
+}
+
+static const struct file_operations proc_fdinfo_file_operations = {
+ .open = nonseekable_open,
+ .read = proc_fdinfo_read,
+};
+
static const struct file_operations proc_fd_operations = {
.read = generic_read_dir,
.readdir = proc_readfd,
};
/*
+ * /proc/pid/fd needs a special permission handler so that a process can still
+ * access /proc/self/fd after it has executed a setuid().
+ */
+static int proc_fd_permission(struct inode *inode, int mask,
+ struct nameidata *nd)
+{
+ int rv;
+
+ rv = generic_permission(inode, mask, NULL);
+ if (rv == 0)
+ return 0;
+ if (task_pid(current) == proc_pid(inode))
+ rv = 0;
+ return rv;
+}
+
+/*
* proc directories can do almost nothing..
*/
static const struct inode_operations proc_fd_inode_operations = {
.lookup = proc_lookupfd,
+ .permission = proc_fd_permission,
.setattr = proc_setattr,
};
+static struct dentry *proc_fdinfo_instantiate(struct inode *dir,
+ struct dentry *dentry, struct task_struct *task, const void *ptr)
+{
+ unsigned fd = *(unsigned *)ptr;
+ struct inode *inode;
+ struct proc_inode *ei;
+ struct dentry *error = ERR_PTR(-ENOENT);
+
+ inode = proc_pid_make_inode(dir->i_sb, task);
+ if (!inode)
+ goto out;
+ ei = PROC_I(inode);
+ ei->fd = fd;
+ inode->i_mode = S_IFREG | S_IRUSR;
+ inode->i_fop = &proc_fdinfo_file_operations;
+ dentry->d_op = &tid_fd_dentry_operations;
+ d_add(dentry, inode);
+ /* Close the race of the process dying before we return the dentry */
+ if (tid_fd_revalidate(dentry, NULL))
+ error = NULL;
+
+ out:
+ return error;
+}
+
+static struct dentry *proc_lookupfdinfo(struct inode *dir,
+ struct dentry *dentry,
+ struct nameidata *nd)
+{
+ return proc_lookupfd_common(dir, dentry, proc_fdinfo_instantiate);
+}
+
+static int proc_readfdinfo(struct file *filp, void *dirent, filldir_t filldir)
+{
+ return proc_readfd_common(filp, dirent, filldir,
+ proc_fdinfo_instantiate);
+}
+
+static const struct file_operations proc_fdinfo_operations = {
+ .read = generic_read_dir,
+ .readdir = proc_readfdinfo,
+};
+
+/*
+ * proc directories can do almost nothing..
+ */
+static const struct inode_operations proc_fdinfo_inode_operations = {
+ .lookup = proc_lookupfdinfo,
+ .setattr = proc_setattr,
+};
+
+
static struct dentry *proc_pident_instantiate(struct inode *dir,
- struct dentry *dentry, struct task_struct *task, void *ptr)
+ struct dentry *dentry, struct task_struct *task, const void *ptr)
{
- struct pid_entry *p = ptr;
+ const struct pid_entry *p = ptr;
struct inode *inode;
struct proc_inode *ei;
struct dentry *error = ERR_PTR(-EINVAL);
@@ -1453,13 +1579,13 @@ out:
static struct dentry *proc_pident_lookup(struct inode *dir,
struct dentry *dentry,
- struct pid_entry *ents,
+ const struct pid_entry *ents,
unsigned int nents)
{
struct inode *inode;
struct dentry *error;
struct task_struct *task = get_proc_task(dir);
- struct pid_entry *p, *last;
+ const struct pid_entry *p, *last;
error = ERR_PTR(-ENOENT);
inode = NULL;
@@ -1488,8 +1614,8 @@ out_no_task:
return error;
}
-static int proc_pident_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
- struct task_struct *task, struct pid_entry *p)
+static int proc_pident_fill_cache(struct file *filp, void *dirent,
+ filldir_t filldir, struct task_struct *task, const struct pid_entry *p)
{
return proc_fill_cache(filp, dirent, filldir, p->name, p->len,
proc_pident_instantiate, task, p);
@@ -1497,14 +1623,14 @@ static int proc_pident_fill_cache(struct file *filp, void *dirent, filldir_t fil
static int proc_pident_readdir(struct file *filp,
void *dirent, filldir_t filldir,
- struct pid_entry *ents, unsigned int nents)
+ const struct pid_entry *ents, unsigned int nents)
{
int i;
int pid;
struct dentry *dentry = filp->f_path.dentry;
struct inode *inode = dentry->d_inode;
struct task_struct *task = get_proc_task(inode);
- struct pid_entry *p, *last;
+ const struct pid_entry *p, *last;
ino_t ino;
int ret;
@@ -1619,7 +1745,7 @@ static const struct file_operations proc_pid_attr_operations = {
.write = proc_pid_attr_write,
};
-static struct pid_entry attr_dir_stuff[] = {
+static const struct pid_entry attr_dir_stuff[] = {
REG("current", S_IRUGO|S_IWUGO, pid_attr),
REG("prev", S_IRUGO, pid_attr),
REG("exec", S_IRUGO|S_IWUGO, pid_attr),
@@ -1685,7 +1811,7 @@ static const struct inode_operations proc_self_inode_operations = {
* that properly belong to the /proc filesystem, as they describe
* describe something that is process related.
*/
-static struct pid_entry proc_base_stuff[] = {
+static const struct pid_entry proc_base_stuff[] = {
NOD("self", S_IFLNK|S_IRWXUGO,
&proc_self_inode_operations, NULL, {}),
};
@@ -1714,9 +1840,9 @@ static struct dentry_operations proc_base_dentry_operations =
};
static struct dentry *proc_base_instantiate(struct inode *dir,
- struct dentry *dentry, struct task_struct *task, void *ptr)
+ struct dentry *dentry, struct task_struct *task, const void *ptr)
{
- struct pid_entry *p = ptr;
+ const struct pid_entry *p = ptr;
struct inode *inode;
struct proc_inode *ei;
struct dentry *error = ERR_PTR(-EINVAL);
@@ -1764,7 +1890,7 @@ static struct dentry *proc_base_lookup(struct inode *dir, struct dentry *dentry)
{
struct dentry *error;
struct task_struct *task = get_proc_task(dir);
- struct pid_entry *p, *last;
+ const struct pid_entry *p, *last;
error = ERR_PTR(-ENOENT);
@@ -1790,8 +1916,8 @@ out_no_task:
return error;
}
-static int proc_base_fill_cache(struct file *filp, void *dirent, filldir_t filldir,
- struct task_struct *task, struct pid_entry *p)
+static int proc_base_fill_cache(struct file *filp, void *dirent,
+ filldir_t filldir, struct task_struct *task, const struct pid_entry *p)
{
return proc_fill_cache(filp, dirent, filldir, p->name, p->len,
proc_base_instantiate, task, p);
@@ -1828,9 +1954,10 @@ static int proc_pid_io_accounting(struct task_struct *task, char *buffer)
static const struct file_operations proc_task_operations;
static const struct inode_operations proc_task_inode_operations;
-static struct pid_entry tgid_base_stuff[] = {
+static const struct pid_entry tgid_base_stuff[] = {
DIR("task", S_IRUGO|S_IXUGO, task),
DIR("fd", S_IRUSR|S_IXUSR, fd),
+ DIR("fdinfo", S_IRUSR|S_IXUSR, fdinfo),
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
@@ -1851,6 +1978,7 @@ static struct pid_entry tgid_base_stuff[] = {
REG("mounts", S_IRUGO, mounts),
REG("mountstats", S_IRUSR, mountstats),
#ifdef CONFIG_MMU
+ REG("clear_refs", S_IWUSR, clear_refs),
REG("smaps", S_IRUGO, smaps),
#endif
#ifdef CONFIG_SECURITY
@@ -1970,7 +2098,7 @@ out:
static struct dentry *proc_pid_instantiate(struct inode *dir,
struct dentry * dentry,
- struct task_struct *task, void *ptr)
+ struct task_struct *task, const void *ptr)
{
struct dentry *error = ERR_PTR(-ENOENT);
struct inode *inode;
@@ -1983,7 +2111,7 @@ static struct dentry *proc_pid_instantiate(struct inode *dir,
inode->i_op = &proc_tgid_base_inode_operations;
inode->i_fop = &proc_tgid_base_operations;
inode->i_flags|=S_IMMUTABLE;
- inode->i_nlink = 4;
+ inode->i_nlink = 5;
#ifdef CONFIG_SECURITY
inode->i_nlink += 1;
#endif
@@ -2085,7 +2213,7 @@ int proc_pid_readdir(struct file * filp, void * dirent, filldir_t filldir)
goto out_no_task;
for (; nr < ARRAY_SIZE(proc_base_stuff); filp->f_pos++, nr++) {
- struct pid_entry *p = &proc_base_stuff[nr];
+ const struct pid_entry *p = &proc_base_stuff[nr];
if (proc_base_fill_cache(filp, dirent, filldir, reaper, p) < 0)
goto out;
}
@@ -2111,8 +2239,9 @@ out_no_task:
/*
* Tasks
*/
-static struct pid_entry tid_base_stuff[] = {
+static const struct pid_entry tid_base_stuff[] = {
DIR("fd", S_IRUSR|S_IXUSR, fd),
+ DIR("fdinfo", S_IRUSR|S_IXUSR, fdinfo),
INF("environ", S_IRUSR, pid_environ),
INF("auxv", S_IRUSR, pid_auxv),
INF("status", S_IRUGO, pid_status),
@@ -2132,6 +2261,7 @@ static struct pid_entry tid_base_stuff[] = {
LNK("exe", exe),
REG("mounts", S_IRUGO, mounts),
#ifdef CONFIG_MMU
+ REG("clear_refs", S_IWUSR, clear_refs),
REG("smaps", S_IRUGO, smaps),
#endif
#ifdef CONFIG_SECURITY
@@ -2180,7 +2310,7 @@ static const struct inode_operations proc_tid_base_inode_operations = {
};
static struct dentry *proc_task_instantiate(struct inode *dir,
- struct dentry *dentry, struct task_struct *task, void *ptr)
+ struct dentry *dentry, struct task_struct *task, const void *ptr)
{
struct dentry *error = ERR_PTR(-ENOENT);
struct inode *inode;
@@ -2192,7 +2322,7 @@ static struct dentry *proc_task_instantiate(struct inode *dir,
inode->i_op = &proc_tid_base_inode_operations;
inode->i_fop = &proc_tid_base_operations;
inode->i_flags|=S_IMMUTABLE;
- inode->i_nlink = 3;
+ inode->i_nlink = 4;
#ifdef CONFIG_SECURITY
inode->i_nlink += 1;
#endif