Skip to content

Commit 655c16a

Browse files
oleg-nesterovtorvalds
authored andcommitted
exec: separate MM_ANONPAGES and RLIMIT_STACK accounting
get_arg_page() checks bprm->rlim_stack.rlim_cur and re-calculates the "extra" size for argv/envp pointers every time, this is a bit ugly and even not strictly correct: acct_arg_size() must not account this size. Remove all the rlimit code in get_arg_page(). Instead, add bprm->argmin calculated once at the start of __do_execve_file() and change copy_strings to check bprm->p >= bprm->argmin. The patch adds the new helper, prepare_arg_pages() which initializes bprm->argc/envc and bprm->argmin. [[email protected]: fix !CONFIG_MMU version of get_arg_page()] Link: http://lkml.kernel.org/r/[email protected] [[email protected]: use max_t] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Oleg Nesterov <[email protected]> Acked-by: Kees Cook <[email protected]> Tested-by: Guenter Roeck <[email protected]> Cc: "Eric W. Biederman" <[email protected]> Cc: Michal Hocko <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 8099b04 commit 655c16a

File tree

2 files changed

+53
-53
lines changed

2 files changed

+53
-53
lines changed

fs/exec.c

Lines changed: 52 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -218,55 +218,10 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
218218
if (ret <= 0)
219219
return NULL;
220220

221-
if (write) {
222-
unsigned long size = bprm->vma->vm_end - bprm->vma->vm_start;
223-
unsigned long ptr_size, limit;
224-
225-
/*
226-
* Since the stack will hold pointers to the strings, we
227-
* must account for them as well.
228-
*
229-
* The size calculation is the entire vma while each arg page is
230-
* built, so each time we get here it's calculating how far it
231-
* is currently (rather than each call being just the newly
232-
* added size from the arg page). As a result, we need to
233-
* always add the entire size of the pointers, so that on the
234-
* last call to get_arg_page() we'll actually have the entire
235-
* correct size.
236-
*/
237-
ptr_size = (bprm->argc + bprm->envc) * sizeof(void *);
238-
if (ptr_size > ULONG_MAX - size)
239-
goto fail;
240-
size += ptr_size;
241-
242-
acct_arg_size(bprm, size / PAGE_SIZE);
243-
244-
/*
245-
* We've historically supported up to 32 pages (ARG_MAX)
246-
* of argument strings even with small stacks
247-
*/
248-
if (size <= ARG_MAX)
249-
return page;
250-
251-
/*
252-
* Limit to 1/4 of the max stack size or 3/4 of _STK_LIM
253-
* (whichever is smaller) for the argv+env strings.
254-
* This ensures that:
255-
* - the remaining binfmt code will not run out of stack space,
256-
* - the program will have a reasonable amount of stack left
257-
* to work from.
258-
*/
259-
limit = _STK_LIM / 4 * 3;
260-
limit = min(limit, bprm->rlim_stack.rlim_cur / 4);
261-
if (size > limit)
262-
goto fail;
263-
}
221+
if (write)
222+
acct_arg_size(bprm, vma_pages(bprm->vma));
264223

265224
return page;
266-
267-
fail:
268-
put_page(page);
269-
return NULL;
270225
}
271226

272227
static void put_arg_page(struct page *page)
@@ -492,6 +447,50 @@ static int count(struct user_arg_ptr argv, int max)
492447
return i;
493448
}
494449

450+
static int prepare_arg_pages(struct linux_binprm *bprm,
451+
struct user_arg_ptr argv, struct user_arg_ptr envp)
452+
{
453+
unsigned long limit, ptr_size;
454+
455+
bprm->argc = count(argv, MAX_ARG_STRINGS);
456+
if (bprm->argc < 0)
457+
return bprm->argc;
458+
459+
bprm->envc = count(envp, MAX_ARG_STRINGS);
460+
if (bprm->envc < 0)
461+
return bprm->envc;
462+
463+
/*
464+
* Limit to 1/4 of the max stack size or 3/4 of _STK_LIM
465+
* (whichever is smaller) for the argv+env strings.
466+
* This ensures that:
467+
* - the remaining binfmt code will not run out of stack space,
468+
* - the program will have a reasonable amount of stack left
469+
* to work from.
470+
*/
471+
limit = _STK_LIM / 4 * 3;
472+
limit = min(limit, bprm->rlim_stack.rlim_cur / 4);
473+
/*
474+
* We've historically supported up to 32 pages (ARG_MAX)
475+
* of argument strings even with small stacks
476+
*/
477+
limit = max_t(unsigned long, limit, ARG_MAX);
478+
/*
479+
* We must account for the size of all the argv and envp pointers to
480+
* the argv and envp strings, since they will also take up space in
481+
* the stack. They aren't stored until much later when we can't
482+
* signal to the parent that the child has run out of stack space.
483+
* Instead, calculate it here so it's possible to fail gracefully.
484+
*/
485+
ptr_size = (bprm->argc + bprm->envc) * sizeof(void *);
486+
if (limit <= ptr_size)
487+
return -E2BIG;
488+
limit -= ptr_size;
489+
490+
bprm->argmin = bprm->p - limit;
491+
return 0;
492+
}
493+
495494
/*
496495
* 'copy_strings()' copies argument/environment strings from the old
497496
* processes's memory to the new process's stack. The call to get_user_pages()
@@ -527,6 +526,10 @@ static int copy_strings(int argc, struct user_arg_ptr argv,
527526
pos = bprm->p;
528527
str += len;
529528
bprm->p -= len;
529+
#ifdef CONFIG_MMU
530+
if (bprm->p < bprm->argmin)
531+
goto out;
532+
#endif
530533

531534
while (len > 0) {
532535
int offset, bytes_to_copy;
@@ -1789,12 +1792,8 @@ static int __do_execve_file(int fd, struct filename *filename,
17891792
if (retval)
17901793
goto out_unmark;
17911794

1792-
bprm->argc = count(argv, MAX_ARG_STRINGS);
1793-
if ((retval = bprm->argc) < 0)
1794-
goto out;
1795-
1796-
bprm->envc = count(envp, MAX_ARG_STRINGS);
1797-
if ((retval = bprm->envc) < 0)
1795+
retval = prepare_arg_pages(bprm, argv, envp);
1796+
if (retval < 0)
17981797
goto out;
17991798

18001799
retval = prepare_binprm(bprm);

include/linux/binfmts.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ struct linux_binprm {
2525
#endif
2626
struct mm_struct *mm;
2727
unsigned long p; /* current top of mem */
28+
unsigned long argmin; /* rlimit marker for copy_strings() */
2829
unsigned int
2930
/*
3031
* True after the bprm_set_creds hook has been called once

0 commit comments

Comments
 (0)