lkml.org 
[lkml]   [2009]   [Sep]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    Date
    From
    Subject[PATCH v3] exec: do not sleep in TASK_TRACED under ->cred_guard_mutex
    Tom Horsley reports that his debugger hangs when it tries to read
    /proc/pid_of_tracee/maps, this happens since

    "mm_for_maps: take ->cred_guard_mutex to fix the race with exec"
    04b836cbf19e885f8366bccb2e4b0474346c02d

    commit in 2.6.31.

    But the root of the problem lies in the fact that do_execve() path calls
    tracehook_report_exec() which can stop if the tracer sets PT_TRACE_EXEC.

    The tracee must not sleep in TASK_TRACED holding this mutex. Even if we
    remove ->cred_guard_mutex from mm_for_maps() and proc_pid_attr_write(),
    another task doing PTRACE_ATTACH should not hang until it is killed or
    the tracee resumes.

    With this patch do_execve() does not use ->cred_guard_mutex directly and
    we do not hold it throughout, instead:

    - introduce prepare_bprm_creds() helper, it locks the mutex
    and calls prepare_exec_creds() to initialize bprm->cred.

    - install_exec_creds() drops the mutex after commit_creds(),
    and thus before tracehook_report_exec()->ptrace_stop().

    or, if exec fails,

    free_bprm() drops this mutex when bprm->cred != NULL which
    indicates install_exec_creds() was not called.

    Reported-by: Tom Horsley <tom.horsley@att.net>
    Signed-off-by: Oleg Nesterov <oleg@redhat.com>
    Acked-by: David Howells <dhowells@redhat.com>
    ---

    include/linux/binfmts.h | 3 +-
    fs/exec.c | 63 ++++++++++++++++++++++++++++--------------------
    fs/compat.c | 17 +++---------
    3 files changed, 44 insertions(+), 39 deletions(-)

    --- WAIT/include/linux/binfmts.h~CRED_MUTEX_EEEVENT_EXEC 2009-07-24 19:02:19.000000000 +0200
    +++ WAIT/include/linux/binfmts.h 2009-09-03 17:37:04.000000000 +0200
    @@ -117,9 +117,10 @@ extern int setup_arg_pages(struct linux_
    int executable_stack);
    extern int bprm_mm_init(struct linux_binprm *bprm);
    extern int copy_strings_kernel(int argc,char ** argv,struct linux_binprm *bprm);
    -extern void install_exec_creds(struct linux_binprm *bprm);
    extern void do_coredump(long signr, int exit_code, struct pt_regs *regs);
    extern void set_binfmt(struct linux_binfmt *new);
    +extern int prepare_bprm_creds(struct linux_binprm *bprm);
    +extern void install_exec_creds(struct linux_binprm *bprm);
    extern void free_bprm(struct linux_binprm *);

    #endif /* __KERNEL__ */
    --- WAIT/fs/exec.c~CRED_MUTEX_EEEVENT_EXEC 2009-09-03 15:31:08.000000000 +0200
    +++ WAIT/fs/exec.c 2009-09-04 19:09:14.000000000 +0200
    @@ -1018,6 +1018,35 @@ out:
    EXPORT_SYMBOL(flush_old_exec);

    /*
    + * Prepare credentials and lock ->cred_guard_mutex.
    + * install_exec_creds() commits the new creds and drops the lock.
    + * Or, if exec fails before, free_bprm() should release ->cred and
    + * and unlock.
    + */
    +int prepare_bprm_creds(struct linux_binprm *bprm)
    +{
    + if (mutex_lock_interruptible(&current->cred_guard_mutex))
    + return -ERESTARTNOINTR;
    +
    + bprm->cred = prepare_exec_creds();
    + if (likely(bprm->cred))
    + return 0;
    +
    + mutex_unlock(&current->cred_guard_mutex);
    + return -ENOMEM;
    +}
    +
    +void free_bprm(struct linux_binprm *bprm)
    +{
    + free_arg_pages(bprm);
    + if (bprm->cred) {
    + mutex_unlock(&current->cred_guard_mutex);
    + abort_creds(bprm->cred);
    + }
    + kfree(bprm);
    +}
    +
    +/*
    * install the new credentials for this executable
    */
    void install_exec_creds(struct linux_binprm *bprm)
    @@ -1026,12 +1055,13 @@ void install_exec_creds(struct linux_bin

    commit_creds(bprm->cred);
    bprm->cred = NULL;
    -
    - /* cred_guard_mutex must be held at least to this point to prevent
    + /*
    + * cred_guard_mutex must be held at least to this point to prevent
    * ptrace_attach() from altering our determination of the task's
    - * credentials; any time after this it may be unlocked */
    -
    + * credentials; any time after this it may be unlocked.
    + */
    security_bprm_committed_creds(bprm);
    + mutex_unlock(&current->cred_guard_mutex);
    }
    EXPORT_SYMBOL(install_exec_creds);

    @@ -1248,14 +1278,6 @@ int search_binary_handler(struct linux_b

    EXPORT_SYMBOL(search_binary_handler);

    -void free_bprm(struct linux_binprm *bprm)
    -{
    - free_arg_pages(bprm);
    - if (bprm->cred)
    - abort_creds(bprm->cred);
    - kfree(bprm);
    -}
    -
    /*
    * sys_execve() executes a new program.
    */
    @@ -1279,20 +1301,15 @@ int do_execve(char * filename,
    if (!bprm)
    goto out_files;

    - retval = -ERESTARTNOINTR;
    - if (mutex_lock_interruptible(&current->cred_guard_mutex))
    + retval = prepare_bprm_creds(bprm);
    + if (retval)
    goto out_free;
    - current->in_execve = 1;
    -
    - retval = -ENOMEM;
    - bprm->cred = prepare_exec_creds();
    - if (!bprm->cred)
    - goto out_unlock;

    retval = check_unsafe_exec(bprm);
    if (retval < 0)
    - goto out_unlock;
    + goto out_free;
    clear_in_exec = retval;
    + current->in_execve = 1;

    file = open_exec(filename);
    retval = PTR_ERR(file);
    @@ -1342,7 +1359,6 @@ int do_execve(char * filename,
    /* execve succeeded */
    current->fs->in_exec = 0;
    current->in_execve = 0;
    - mutex_unlock(&current->cred_guard_mutex);
    acct_update_integrals(current);
    free_bprm(bprm);
    if (displaced)
    @@ -1362,10 +1378,7 @@ out_file:
    out_unmark:
    if (clear_in_exec)
    current->fs->in_exec = 0;
    -
    -out_unlock:
    current->in_execve = 0;
    - mutex_unlock(&current->cred_guard_mutex);

    out_free:
    free_bprm(bprm);
    --- WAIT/fs/compat.c~CRED_MUTEX_EEEVENT_EXEC 2009-09-03 15:31:08.000000000 +0200
    +++ WAIT/fs/compat.c 2009-09-03 16:42:17.000000000 +0200
    @@ -1486,20 +1486,15 @@ int compat_do_execve(char * filename,
    if (!bprm)
    goto out_files;

    - retval = -ERESTARTNOINTR;
    - if (mutex_lock_interruptible(&current->cred_guard_mutex))
    + retval = prepare_bprm_creds(bprm);
    + if (retval)
    goto out_free;
    - current->in_execve = 1;
    -
    - retval = -ENOMEM;
    - bprm->cred = prepare_exec_creds();
    - if (!bprm->cred)
    - goto out_unlock;

    retval = check_unsafe_exec(bprm);
    if (retval < 0)
    - goto out_unlock;
    + goto out_free;
    clear_in_exec = retval;
    + current->in_execve = 1;

    file = open_exec(filename);
    retval = PTR_ERR(file);
    @@ -1548,7 +1543,6 @@ int compat_do_execve(char * filename,
    /* execve succeeded */
    current->fs->in_exec = 0;
    current->in_execve = 0;
    - mutex_unlock(&current->cred_guard_mutex);
    acct_update_integrals(current);
    free_bprm(bprm);
    if (displaced)
    @@ -1568,10 +1562,7 @@ out_file:
    out_unmark:
    if (clear_in_exec)
    current->fs->in_exec = 0;
    -
    -out_unlock:
    current->in_execve = 0;
    - mutex_unlock(&current->cred_guard_mutex);

    out_free:
    free_bprm(bprm);


    \
     
     \ /
      Last update: 2009-09-04 19:35    [W:0.043 / U:29.804 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site