2.6.27.59-stable review patch. If anyone has any objections, please let us know.
------------------ From: Oleg Nesterov <[email protected]> commit 3c77f845722158206a7209c45ccddc264d19319c upstream. Brad Spengler published a local memory-allocation DoS that evades the OOM-killer (though not the virtual memory RLIMIT): http://www.grsecurity.net/~spender/64bit_dos.c execve()->copy_strings() can allocate a lot of memory, but this is not visible to oom-killer, nobody can see the nascent bprm->mm and take it into account. With this patch get_arg_page() increments current's MM_ANONPAGES counter every time we allocate the new page for argv/envp. When do_execve() succeds or fails, we change this counter back. Technically this is not 100% correct, we can't know if the new page is swapped out and turn MM_ANONPAGES into MM_SWAPENTS, but I don't think this really matters and everything becomes correct once exec changes ->mm or fails. Reported-by: Brad Spengler <[email protected]> Reviewed-and-discussed-by: KOSAKI Motohiro <[email protected]> Signed-off-by: Oleg Nesterov <[email protected]> Signed-off-by: Linus Torvalds <[email protected]> Cc: Moritz Muehlenhoff <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]> --- fs/exec.c | 28 ++++++++++++++++++++++++++-- include/linux/binfmts.h | 1 + 2 files changed, 27 insertions(+), 2 deletions(-) Index: longterm-2.6.27/fs/exec.c =================================================================== --- longterm-2.6.27.orig/fs/exec.c 2011-02-09 22:45:33.000000000 +0100 +++ longterm-2.6.27/fs/exec.c 2011-04-25 17:22:50.655279537 +0200 @@ -168,6 +168,21 @@ #ifdef CONFIG_MMU +static void acct_arg_size(struct linux_binprm *bprm, unsigned long pages) +{ + struct mm_struct *mm = current->mm; + long diff = (long)(pages - bprm->vma_pages); + + if (!mm || !diff) + return; + + bprm->vma_pages = pages; + + down_write(&mm->mmap_sem); + mm->total_vm += diff; + up_write(&mm->mmap_sem); +} + static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, int write) { @@ -190,6 +205,8 @@ unsigned long size = bprm->vma->vm_end - bprm->vma->vm_start; struct rlimit *rlim; + acct_arg_size(bprm, size / PAGE_SIZE); + /* * We've historically supported up to 32 pages (ARG_MAX) * of argument strings even with small stacks @@ -291,6 +308,10 @@ #else +static inline void acct_arg_size(struct linux_binprm *bprm, unsigned long pages) +{ +} + static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, int write) { @@ -995,6 +1016,7 @@ /* * Release all of the old mmap stuff */ + acct_arg_size(bprm, 0); retval = exec_mmap(bprm->mm); if (retval) goto out; @@ -1378,8 +1400,10 @@ security_bprm_free(bprm); out_mm: - if (bprm->mm) + if (bprm->mm) { + acct_arg_size(bprm, 0); mmput (bprm->mm); + } out_file: if (bprm->file) { Index: longterm-2.6.27/include/linux/binfmts.h =================================================================== --- longterm-2.6.27.orig/include/linux/binfmts.h 2011-01-23 10:52:34.000000000 +0100 +++ longterm-2.6.27/include/linux/binfmts.h 2011-04-25 17:22:24.856278432 +0200 @@ -28,6 +28,7 @@ char buf[BINPRM_BUF_SIZE]; #ifdef CONFIG_MMU struct vm_area_struct *vma; + unsigned long vma_pages; #else # define MAX_ARG_PAGES 32 struct page *page[MAX_ARG_PAGES]; _______________________________________________ stable mailing list [email protected] http://linux.kernel.org/mailman/listinfo/stable
