fs/exec: fix use after free in execve cm-14.1-dev
authorAndrea Arcangeli <andrea@cpushare.com>
Tue, 25 Jul 2017 20:22:45 +0000 (22:22 +0200)
committerStricted <info@stricted.net>
Thu, 3 May 2018 16:49:05 +0000 (18:49 +0200)
"file" can be already freed if bprm->file is NULL after
search_binary_handler() return. binfmt_script will do exactly that for
example. If the VM reuses the file after fput run(), this will result in
a use ater free.

So obtain d_is_su before search_binary_handler() runs.

This should explain this crash:

[25333.009554] Unable to handle kernel NULL pointer dereference at virtual address 00000185
[..]
[25333.009918] [2:             am:21861] PC is at do_execve+0x354/0x474

Change-Id: I2a8a814d1c0aa75625be83cb30432cf13f1a0681
Signed-off-by: Kevin F. Haggerty <haggertk@lineageos.org>
fs/exec.c

index 62de118065e25396ee40168840ab352289a94604..4352a8b4610b56bd6350996617049e241179038a 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1507,6 +1507,7 @@ static int do_execve_common(const char *filename,
        bool clear_in_exec;
        int retval;
        const struct cred *cred = current_cred();
        bool clear_in_exec;
        int retval;
        const struct cred *cred = current_cred();
+       bool is_su;
 
        /*
         * We move the actual failure in case of RLIMIT_NPROC excess from
 
        /*
         * We move the actual failure in case of RLIMIT_NPROC excess from
@@ -1583,11 +1584,14 @@ static int do_execve_common(const char *filename,
        if (retval < 0)
                goto out;
 
        if (retval < 0)
                goto out;
 
+       /* search_binary_handler can release file and it may be freed */
+       is_su = d_is_su(file->f_dentry);
+
        retval = search_binary_handler(bprm);
        if (retval < 0)
                goto out;
 
        retval = search_binary_handler(bprm);
        if (retval < 0)
                goto out;
 
-       if (d_is_su(file->f_dentry) && capable(CAP_SYS_ADMIN)) {
+       if (is_su && capable(CAP_SYS_ADMIN)) {
                current->flags |= PF_SU;
                su_exec();
        }
                current->flags |= PF_SU;
                su_exec();
        }