lkml.org 
[lkml]   [2010]   [Nov]   [30]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH 1/2] exec: make argv/envp memory visible to oom-killer
    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 <spender@grsecurity.net>
    By-discussion-with: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
    Signed-off-by: Oleg Nesterov <oleg@redhat.com>
    ---

    include/linux/binfmts.h | 1 +
    fs/exec.c | 32 ++++++++++++++++++++++++++++++--
    2 files changed, 31 insertions(+), 2 deletions(-)

    --- K/include/linux/binfmts.h~acct_exec_mem 2010-11-30 18:27:15.000000000 +0100
    +++ K/include/linux/binfmts.h 2010-11-30 18:28:54.000000000 +0100
    @@ -29,6 +29,7 @@ struct linux_binprm{
    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];
    --- K/fs/exec.c~acct_exec_mem 2010-11-30 18:27:15.000000000 +0100
    +++ K/fs/exec.c 2010-11-30 18:28:54.000000000 +0100
    @@ -164,6 +164,25 @@ out:

    #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;
    +
    +#ifdef SPLIT_RSS_COUNTING
    + add_mm_counter(mm, MM_ANONPAGES, diff);
    +#else
    + spin_lock(&mm->page_table_lock);
    + add_mm_counter(mm, MM_ANONPAGES, diff);
    + spin_unlock(&mm->page_table_lock);
    +#endif
    +}
    +
    static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
    int write)
    {
    @@ -186,6 +205,8 @@ static struct page *get_arg_page(struct
    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
    @@ -276,6 +297,10 @@ static bool valid_arg_len(struct linux_b

    #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)
    {
    @@ -1003,6 +1028,7 @@ int flush_old_exec(struct linux_binprm *
    /*
    * Release all of the old mmap stuff
    */
    + acct_arg_size(bprm, 0);
    retval = exec_mmap(bprm->mm);
    if (retval)
    goto out;
    @@ -1426,8 +1452,10 @@ int do_execve(const char * filename,
    return retval;

    out:
    - if (bprm->mm)
    - mmput (bprm->mm);
    + if (bprm->mm) {
    + acct_arg_size(bprm, 0);
    + mmput(bprm->mm);
    + }

    out_file:
    if (bprm->file) {


    \
     
     \ /
      Last update: 2010-11-30 21:05    [W:0.055 / U:61.980 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site