Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

exec: load_script: kill the onstack interp[BINPRM_BUF_SIZE] array

Patch series "exec: binfmt_misc: fix use-after-free, kill
iname[BINPRM_BUF_SIZE]".

It looks like this code was always wrong, then commit 948b701a607f
("binfmt_misc: add persistent opened binary handler for containers")
added more problems.

This patch (of 6):

load_script() can simply use i_name instead, it points into bprm->buf[]
and nobody can change this memory until we call prepare_binprm().

The only complication is that we need to also change the signature of
bprm_change_interp() but this change looks good too.

While at it, do whitespace/style cleanups.

NOTE: the real motivation for this change is that people want to
increase BINPRM_BUF_SIZE, we need to change load_misc_binary() too but
this looks more complicated because afaics it is very buggy.

Link: http://lkml.kernel.org/r/20170918163446.GA26793@redhat.com
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
Acked-by: Kees Cook <keescook@chromium.org>
Cc: Travis Gummels <tgummels@redhat.com>
Cc: Ben Woodard <woodard@redhat.com>
Cc: Jim Foraker <foraker1@llnl.gov>
Cc: <tdhooge@llnl.gov>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: James Bottomley <James.Bottomley@HansenPartnership.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>

authored by

Oleg Nesterov and committed by
Linus Torvalds
c2315c18 384632e6

+11 -10
+9 -8
fs/binfmt_script.c
··· 19 19 const char *i_arg, *i_name; 20 20 char *cp; 21 21 struct file *file; 22 - char interp[BINPRM_BUF_SIZE]; 23 22 int retval; 24 23 25 24 if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!')) ··· 54 55 break; 55 56 } 56 57 for (cp = bprm->buf+2; (*cp == ' ') || (*cp == '\t'); cp++); 57 - if (*cp == '\0') 58 + if (*cp == '\0') 58 59 return -ENOEXEC; /* No interpreter name found */ 59 60 i_name = cp; 60 61 i_arg = NULL; ··· 64 65 *cp++ = '\0'; 65 66 if (*cp) 66 67 i_arg = cp; 67 - strcpy (interp, i_name); 68 68 /* 69 69 * OK, we've parsed out the interpreter name and 70 70 * (optional) argument. ··· 78 80 if (retval) 79 81 return retval; 80 82 retval = copy_strings_kernel(1, &bprm->interp, bprm); 81 - if (retval < 0) return retval; 83 + if (retval < 0) 84 + return retval; 82 85 bprm->argc++; 83 86 if (i_arg) { 84 87 retval = copy_strings_kernel(1, &i_arg, bprm); 85 - if (retval < 0) return retval; 88 + if (retval < 0) 89 + return retval; 86 90 bprm->argc++; 87 91 } 88 92 retval = copy_strings_kernel(1, &i_name, bprm); 89 - if (retval) return retval; 93 + if (retval) 94 + return retval; 90 95 bprm->argc++; 91 - retval = bprm_change_interp(interp, bprm); 96 + retval = bprm_change_interp(i_name, bprm); 92 97 if (retval < 0) 93 98 return retval; 94 99 95 100 /* 96 101 * OK, now restart the process with the interpreter's dentry. 97 102 */ 98 - file = open_exec(interp); 103 + file = open_exec(i_name); 99 104 if (IS_ERR(file)) 100 105 return PTR_ERR(file); 101 106
+1 -1
fs/exec.c
··· 1410 1410 kfree(bprm); 1411 1411 } 1412 1412 1413 - int bprm_change_interp(char *interp, struct linux_binprm *bprm) 1413 + int bprm_change_interp(const char *interp, struct linux_binprm *bprm) 1414 1414 { 1415 1415 /* If a binfmt changed the interp, free it first. */ 1416 1416 if (bprm->interp != bprm->filename)
+1 -1
include/linux/binfmts.h
··· 131 131 int executable_stack); 132 132 extern int transfer_args_to_stack(struct linux_binprm *bprm, 133 133 unsigned long *sp_location); 134 - extern int bprm_change_interp(char *interp, struct linux_binprm *bprm); 134 + extern int bprm_change_interp(const char *interp, struct linux_binprm *bprm); 135 135 extern int copy_strings_kernel(int argc, const char *const *argv, 136 136 struct linux_binprm *bprm); 137 137 extern int prepare_bprm_creds(struct linux_binprm *bprm);