Skip to content

Commit 3ca24ce

Browse files
committed
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
2 parents f72328d + 5ab8271 commit 3ca24ce

File tree

1 file changed

+99
-112
lines changed

1 file changed

+99
-112
lines changed

fs/proc/base.c

Lines changed: 99 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -205,43 +205,16 @@ static int proc_root_link(struct dentry *dentry, struct path *path)
205205
return result;
206206
}
207207

208-
static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
209-
size_t _count, loff_t *pos)
208+
static ssize_t get_mm_cmdline(struct mm_struct *mm, char __user *buf,
209+
size_t count, loff_t *ppos)
210210
{
211-
struct task_struct *tsk;
212-
struct mm_struct *mm;
213-
char *page;
214-
unsigned long count = _count;
215211
unsigned long arg_start, arg_end, env_start, env_end;
216-
unsigned long len1, len2;
217-
char __user *buf0 = buf;
218-
struct {
219-
unsigned long p;
220-
unsigned long len;
221-
} cmdline[2];
222-
char c;
223-
int rv;
224-
225-
BUG_ON(*pos < 0);
212+
unsigned long pos, len;
213+
char *page;
226214

227-
tsk = get_proc_task(file_inode(file));
228-
if (!tsk)
229-
return -ESRCH;
230-
mm = get_task_mm(tsk);
231-
put_task_struct(tsk);
232-
if (!mm)
233-
return 0;
234215
/* Check if process spawned far enough to have cmdline. */
235-
if (!mm->env_end) {
236-
rv = 0;
237-
goto out_mmput;
238-
}
239-
240-
page = (char *)__get_free_page(GFP_KERNEL);
241-
if (!page) {
242-
rv = -ENOMEM;
243-
goto out_mmput;
244-
}
216+
if (!mm->env_end)
217+
return 0;
245218

246219
spin_lock(&mm->arg_lock);
247220
arg_start = mm->arg_start;
@@ -250,97 +223,111 @@ static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
250223
env_end = mm->env_end;
251224
spin_unlock(&mm->arg_lock);
252225

253-
BUG_ON(arg_start > arg_end);
254-
BUG_ON(env_start > env_end);
255-
256-
len1 = arg_end - arg_start;
257-
len2 = env_end - env_start;
258-
259-
/* Empty ARGV. */
260-
if (len1 == 0)
261-
goto end;
226+
if (arg_start >= arg_end)
227+
return 0;
262228

263229
/*
264-
* Inherently racy -- command line shares address space
265-
* with code and data.
230+
* We have traditionally allowed the user to re-write
231+
* the argument strings and overflow the end result
232+
* into the environment section. But only do that if
233+
* the environment area is contiguous to the arguments.
266234
*/
267-
if (access_remote_vm(mm, arg_end - 1, &c, 1, FOLL_ANON) != 1)
268-
goto end;
269-
270-
cmdline[0].p = arg_start;
271-
cmdline[0].len = len1;
272-
if (c == '\0') {
273-
/* Command line (set of strings) occupies whole ARGV. */
274-
cmdline[1].len = 0;
275-
} else {
276-
/*
277-
* Command line (1 string) occupies ARGV and
278-
* extends into ENVP.
279-
*/
280-
cmdline[1].p = env_start;
281-
cmdline[1].len = len2;
282-
}
235+
if (env_start != arg_end || env_start >= env_end)
236+
env_start = env_end = arg_end;
283237

284-
{
285-
loff_t pos1 = *pos;
286-
unsigned int i;
238+
/* We're not going to care if "*ppos" has high bits set */
239+
pos = arg_start + *ppos;
240+
241+
/* .. but we do check the result is in the proper range */
242+
if (pos < arg_start || pos >= env_end)
243+
return 0;
287244

288-
i = 0;
289-
while (i < 2 && pos1 >= cmdline[i].len) {
290-
pos1 -= cmdline[i].len;
291-
i++;
245+
/* .. and we never go past env_end */
246+
if (env_end - pos < count)
247+
count = env_end - pos;
248+
249+
page = (char *)__get_free_page(GFP_KERNEL);
250+
if (!page)
251+
return -ENOMEM;
252+
253+
len = 0;
254+
while (count) {
255+
int got;
256+
size_t size = min_t(size_t, PAGE_SIZE, count);
257+
258+
got = access_remote_vm(mm, pos, page, size, FOLL_ANON);
259+
if (got <= 0)
260+
break;
261+
262+
/* Don't walk past a NUL character once you hit arg_end */
263+
if (pos + got >= arg_end) {
264+
int n = 0;
265+
266+
/*
267+
* If we started before 'arg_end' but ended up
268+
* at or after it, we start the NUL character
269+
* check at arg_end-1 (where we expect the normal
270+
* EOF to be).
271+
*
272+
* NOTE! This is smaller than 'got', because
273+
* pos + got >= arg_end
274+
*/
275+
if (pos < arg_end)
276+
n = arg_end - pos - 1;
277+
278+
/* Cut off at first NUL after 'n' */
279+
got = n + strnlen(page+n, got-n);
280+
if (!got)
281+
break;
292282
}
293-
while (i < 2) {
294-
unsigned long p;
295-
unsigned long len;
296-
297-
p = cmdline[i].p + pos1;
298-
len = cmdline[i].len - pos1;
299-
while (count > 0 && len > 0) {
300-
unsigned int nr_read, nr_write;
301-
302-
nr_read = min3(count, len, PAGE_SIZE);
303-
nr_read = access_remote_vm(mm, p, page, nr_read, FOLL_ANON);
304-
if (nr_read == 0)
305-
goto end;
306-
307-
/*
308-
* Command line can be shorter than whole ARGV
309-
* even if last "marker" byte says it is not.
310-
*/
311-
if (c == '\0')
312-
nr_write = nr_read;
313-
else
314-
nr_write = strnlen(page, nr_read);
315-
316-
if (copy_to_user(buf, page, nr_write)) {
317-
rv = -EFAULT;
318-
goto out_free_page;
319-
}
320-
321-
p += nr_write;
322-
len -= nr_write;
323-
buf += nr_write;
324-
count -= nr_write;
325-
326-
if (nr_write < nr_read)
327-
goto end;
328-
}
329283

330-
/* Only first chunk can be read partially. */
331-
pos1 = 0;
332-
i++;
284+
got -= copy_to_user(buf, page, got);
285+
if (unlikely(!got)) {
286+
if (!len)
287+
len = -EFAULT;
288+
break;
333289
}
290+
pos += got;
291+
buf += got;
292+
len += got;
293+
count -= got;
334294
}
335295

336-
end:
337-
*pos += buf - buf0;
338-
rv = buf - buf0;
339-
out_free_page:
340296
free_page((unsigned long)page);
341-
out_mmput:
297+
return len;
298+
}
299+
300+
static ssize_t get_task_cmdline(struct task_struct *tsk, char __user *buf,
301+
size_t count, loff_t *pos)
302+
{
303+
struct mm_struct *mm;
304+
ssize_t ret;
305+
306+
mm = get_task_mm(tsk);
307+
if (!mm)
308+
return 0;
309+
310+
ret = get_mm_cmdline(mm, buf, count, pos);
342311
mmput(mm);
343-
return rv;
312+
return ret;
313+
}
314+
315+
static ssize_t proc_pid_cmdline_read(struct file *file, char __user *buf,
316+
size_t count, loff_t *pos)
317+
{
318+
struct task_struct *tsk;
319+
ssize_t ret;
320+
321+
BUG_ON(*pos < 0);
322+
323+
tsk = get_proc_task(file_inode(file));
324+
if (!tsk)
325+
return -ESRCH;
326+
ret = get_task_cmdline(tsk, buf, count, pos);
327+
put_task_struct(tsk);
328+
if (ret > 0)
329+
*pos += ret;
330+
return ret;
344331
}
345332

346333
static const struct file_operations proc_pid_cmdline_ops = {

0 commit comments

Comments
 (0)