Merge branch 'proc-cmdline'
Merge proc_cmdline simplifications. This re-writes the get_mm_cmdline() logic to be rather simpler than it used to be, and makes the semantics for "cmdline goes past the end of the original area" more natural. You _can_ use prctl(PR_SET_MM) to just point your command line somewhere else entirely, but the traditional model is to just edit things in place and that still needs to continue to work. At least this way the code makes some sense. * proc-cmdline: fs/proc: simplify and clarify get_mm_cmdline() function fs/proc: re-factor proc_pid_cmdline_read() a bit
This commit is contained in:
commit
3ca24ce9ff
219
fs/proc/base.c
219
fs/proc/base.c
|
@ -205,43 +205,16 @@ static int proc_root_link(struct dentry *dentry, struct path *path)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
|
static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
|
||||||
size_t _count, loff_t *pos)
|
size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
struct task_struct *tsk;
|
|
||||||
struct mm_struct *mm;
|
|
||||||
char *page;
|
|
||||||
unsigned long count = _count;
|
|
||||||
unsigned long arg_start, arg_end, env_start, env_end;
|
unsigned long arg_start, arg_end, env_start, env_end;
|
||||||
unsigned long len1, len2;
|
unsigned long pos, len;
|
||||||
char __user *buf0 = buf;
|
char *page;
|
||||||
struct {
|
|
||||||
unsigned long p;
|
|
||||||
unsigned long len;
|
|
||||||
} cmdline[2];
|
|
||||||
char c;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
BUG_ON(*pos < 0);
|
|
||||||
|
|
||||||
tsk = get_proc_task(file_inode(file));
|
|
||||||
if (!tsk)
|
|
||||||
return -ESRCH;
|
|
||||||
mm = get_task_mm(tsk);
|
|
||||||
put_task_struct(tsk);
|
|
||||||
if (!mm)
|
|
||||||
return 0;
|
|
||||||
/* Check if process spawned far enough to have cmdline. */
|
/* Check if process spawned far enough to have cmdline. */
|
||||||
if (!mm->env_end) {
|
if (!mm->env_end)
|
||||||
rv = 0;
|
return 0;
|
||||||
goto out_mmput;
|
|
||||||
}
|
|
||||||
|
|
||||||
page = (char *)__get_free_page(GFP_KERNEL);
|
|
||||||
if (!page) {
|
|
||||||
rv = -ENOMEM;
|
|
||||||
goto out_mmput;
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_lock(&mm->arg_lock);
|
spin_lock(&mm->arg_lock);
|
||||||
arg_start = mm->arg_start;
|
arg_start = mm->arg_start;
|
||||||
|
@ -250,97 +223,111 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
|
||||||
env_end = mm->env_end;
|
env_end = mm->env_end;
|
||||||
spin_unlock(&mm->arg_lock);
|
spin_unlock(&mm->arg_lock);
|
||||||
|
|
||||||
BUG_ON(arg_start > arg_end);
|
if (arg_start >= arg_end)
|
||||||
BUG_ON(env_start > env_end);
|
return 0;
|
||||||
|
|
||||||
len1 = arg_end - arg_start;
|
|
||||||
len2 = env_end - env_start;
|
|
||||||
|
|
||||||
/* Empty ARGV. */
|
|
||||||
if (len1 == 0)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Inherently racy -- command line shares address space
|
* We have traditionally allowed the user to re-write
|
||||||
* with code and data.
|
* the argument strings and overflow the end result
|
||||||
|
* into the environment section. But only do that if
|
||||||
|
* the environment area is contiguous to the arguments.
|
||||||
*/
|
*/
|
||||||
if (access_remote_vm(mm, arg_end - 1, &c, 1, FOLL_ANON) != 1)
|
if (env_start != arg_end || env_start >= env_end)
|
||||||
goto end;
|
env_start = env_end = arg_end;
|
||||||
|
|
||||||
cmdline[0].p = arg_start;
|
/* We're not going to care if "*ppos" has high bits set */
|
||||||
cmdline[0].len = len1;
|
pos = arg_start + *ppos;
|
||||||
if (c == '\0') {
|
|
||||||
/* Command line (set of strings) occupies whole ARGV. */
|
/* .. but we do check the result is in the proper range */
|
||||||
cmdline[1].len = 0;
|
if (pos < arg_start || pos >= env_end)
|
||||||
} else {
|
return 0;
|
||||||
/*
|
|
||||||
* Command line (1 string) occupies ARGV and
|
/* .. and we never go past env_end */
|
||||||
* extends into ENVP.
|
if (env_end - pos < count)
|
||||||
*/
|
count = env_end - pos;
|
||||||
cmdline[1].p = env_start;
|
|
||||||
cmdline[1].len = len2;
|
page = (char *)__get_free_page(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;
|
||||||
|
|
||||||
|
/* Don't walk past a NUL character once you hit arg_end */
|
||||||
|
if (pos + got >= arg_end) {
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we started before 'arg_end' but ended up
|
||||||
|
* at or after it, we start the NUL character
|
||||||
|
* check at arg_end-1 (where we expect the normal
|
||||||
|
* EOF to be).
|
||||||
|
*
|
||||||
|
* NOTE! This is smaller than 'got', because
|
||||||
|
* pos + got >= arg_end
|
||||||
|
*/
|
||||||
|
if (pos < arg_end)
|
||||||
|
n = arg_end - pos - 1;
|
||||||
|
|
||||||
|
/* Cut off at first NUL after 'n' */
|
||||||
|
got = n + strnlen(page+n, got-n);
|
||||||
|
if (!got)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
got -= copy_to_user(buf, page, got);
|
||||||
|
if (unlikely(!got)) {
|
||||||
|
if (!len)
|
||||||
|
len = -EFAULT;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos += got;
|
||||||
|
buf += got;
|
||||||
|
len += got;
|
||||||
|
count -= got;
|
||||||
}
|
}
|
||||||
|
|
||||||
{
|
|
||||||
loff_t pos1 = *pos;
|
|
||||||
unsigned int i;
|
|
||||||
|
|
||||||
i = 0;
|
|
||||||
while (i < 2 && pos1 >= cmdline[i].len) {
|
|
||||||
pos1 -= cmdline[i].len;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
while (i < 2) {
|
|
||||||
unsigned long p;
|
|
||||||
unsigned long len;
|
|
||||||
|
|
||||||
p = cmdline[i].p + pos1;
|
|
||||||
len = cmdline[i].len - pos1;
|
|
||||||
while (count > 0 && len > 0) {
|
|
||||||
unsigned int nr_read, nr_write;
|
|
||||||
|
|
||||||
nr_read = min3(count, len, PAGE_SIZE);
|
|
||||||
nr_read = access_remote_vm(mm, p, page, nr_read, FOLL_ANON);
|
|
||||||
if (nr_read == 0)
|
|
||||||
goto end;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Command line can be shorter than whole ARGV
|
|
||||||
* even if last "marker" byte says it is not.
|
|
||||||
*/
|
|
||||||
if (c == '\0')
|
|
||||||
nr_write = nr_read;
|
|
||||||
else
|
|
||||||
nr_write = strnlen(page, nr_read);
|
|
||||||
|
|
||||||
if (copy_to_user(buf, page, nr_write)) {
|
|
||||||
rv = -EFAULT;
|
|
||||||
goto out_free_page;
|
|
||||||
}
|
|
||||||
|
|
||||||
p += nr_write;
|
|
||||||
len -= nr_write;
|
|
||||||
buf += nr_write;
|
|
||||||
count -= nr_write;
|
|
||||||
|
|
||||||
if (nr_write < nr_read)
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only first chunk can be read partially. */
|
|
||||||
pos1 = 0;
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
end:
|
|
||||||
*pos += buf - buf0;
|
|
||||||
rv = buf - buf0;
|
|
||||||
out_free_page:
|
|
||||||
free_page((unsigned long)page);
|
free_page((unsigned long)page);
|
||||||
out_mmput:
|
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);
|
mmput(mm);
|
||||||
return rv;
|
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 = {
|
static const struct file_operations proc_pid_cmdline_ops = {
|
||||||
|
|
Loading…
Reference in New Issue