mirror of https://gitee.com/openkylin/linux.git
Merge branch 'exec-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace
Pull execve updates from Eric Biederman: "Last cycle for the Nth time I ran into bugs and quality of implementation issues related to exec that could not be easily be fixed because of the way exec is implemented. So I have been digging into exec and cleanup up what I can. I don't think I have exec sorted out enough to fix the issues I started with but I have made some headway this cycle with 4 sets of changes. - promised cleanups after introducing exec_update_mutex - trivial cleanups for exec - control flow simplifications - remove the recomputation of bprm->cred The net result is code that is a bit easier to understand and work with and a decrease in the number of lines of code (if you don't count the added tests)" * 'exec-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ebiederm/user-namespace: (24 commits) exec: Compute file based creds only once exec: Add a per bprm->file version of per_clear binfmt_elf_fdpic: fix execfd build regression selftests/exec: Add binfmt_script regression test exec: Remove recursion from search_binary_handler exec: Generic execfd support exec/binfmt_script: Don't modify bprm->buf and then return -ENOEXEC exec: Move the call of prepare_binprm into search_binary_handler exec: Allow load_misc_binary to call prepare_binprm unconditionally exec: Convert security_bprm_set_creds into security_bprm_repopulate_creds exec: Factor security_bprm_creds_for_exec out of security_bprm_set_creds exec: Teach prepare_exec_creds how exec treats uids & gids exec: Set the point of no return sooner exec: Move handling of the point of no return to the top level exec: Run sync_mm_rss before taking exec_update_mutex exec: Fix spelling of search_binary_handler in a comment exec: Move the comment from above de_thread to above unshare_sighand exec: Rename flush_old_exec begin_new_exec exec: Move most of setup_new_exec into flush_old_exec exec: In setup_new_exec cache current in the local variable me ...
This commit is contained in:
commit
15a2bc4dbb
|
@ -1524,7 +1524,7 @@ display-graph option::
|
||||||
=> remove_vma
|
=> remove_vma
|
||||||
=> exit_mmap
|
=> exit_mmap
|
||||||
=> mmput
|
=> mmput
|
||||||
=> flush_old_exec
|
=> begin_new_exec
|
||||||
=> load_elf_binary
|
=> load_elf_binary
|
||||||
=> search_binary_handler
|
=> search_binary_handler
|
||||||
=> __do_execve_file.isra.32
|
=> __do_execve_file.isra.32
|
||||||
|
|
|
@ -19,10 +19,6 @@ static int load_binary(struct linux_binprm *bprm)
|
||||||
if (bprm->loader)
|
if (bprm->loader)
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
|
|
||||||
allow_write_access(bprm->file);
|
|
||||||
fput(bprm->file);
|
|
||||||
bprm->file = NULL;
|
|
||||||
|
|
||||||
loader = bprm->vma->vm_end - sizeof(void *);
|
loader = bprm->vma->vm_end - sizeof(void *);
|
||||||
|
|
||||||
file = open_exec("/sbin/loader");
|
file = open_exec("/sbin/loader");
|
||||||
|
@ -33,12 +29,9 @@ static int load_binary(struct linux_binprm *bprm)
|
||||||
/* Remember if the application is TASO. */
|
/* Remember if the application is TASO. */
|
||||||
bprm->taso = eh->ah.entry < 0x100000000UL;
|
bprm->taso = eh->ah.entry < 0x100000000UL;
|
||||||
|
|
||||||
bprm->file = file;
|
bprm->interpreter = file;
|
||||||
bprm->loader = loader;
|
bprm->loader = loader;
|
||||||
retval = prepare_binprm(bprm);
|
return 0;
|
||||||
if (retval < 0)
|
|
||||||
return retval;
|
|
||||||
return search_binary_handler(bprm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct linux_binfmt loader_format = {
|
static struct linux_binfmt loader_format = {
|
||||||
|
|
|
@ -131,7 +131,7 @@ static int load_aout_binary(struct linux_binprm *bprm)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Flush all traces of the currently running executable */
|
/* Flush all traces of the currently running executable */
|
||||||
retval = flush_old_exec(bprm);
|
retval = begin_new_exec(bprm);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
@ -156,8 +156,6 @@ static int load_aout_binary(struct linux_binprm *bprm)
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
install_exec_creds(bprm);
|
|
||||||
|
|
||||||
if (N_MAGIC(ex) == OMAGIC) {
|
if (N_MAGIC(ex) == OMAGIC) {
|
||||||
unsigned long text_addr, map_size;
|
unsigned long text_addr, map_size;
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ static int load_aout_binary(struct linux_binprm * bprm)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
/* Flush all traces of the currently running executable */
|
/* Flush all traces of the currently running executable */
|
||||||
retval = flush_old_exec(bprm);
|
retval = begin_new_exec(bprm);
|
||||||
if (retval)
|
if (retval)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
|
@ -174,7 +174,6 @@ static int load_aout_binary(struct linux_binprm * bprm)
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
install_exec_creds(bprm);
|
|
||||||
|
|
||||||
if (N_MAGIC(ex) == OMAGIC) {
|
if (N_MAGIC(ex) == OMAGIC) {
|
||||||
unsigned long text_addr, map_size;
|
unsigned long text_addr, map_size;
|
||||||
|
|
|
@ -279,8 +279,8 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
|
||||||
NEW_AUX_ENT(AT_BASE_PLATFORM,
|
NEW_AUX_ENT(AT_BASE_PLATFORM,
|
||||||
(elf_addr_t)(unsigned long)u_base_platform);
|
(elf_addr_t)(unsigned long)u_base_platform);
|
||||||
}
|
}
|
||||||
if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) {
|
if (bprm->have_execfd) {
|
||||||
NEW_AUX_ENT(AT_EXECFD, bprm->interp_data);
|
NEW_AUX_ENT(AT_EXECFD, bprm->execfd);
|
||||||
}
|
}
|
||||||
#undef NEW_AUX_ENT
|
#undef NEW_AUX_ENT
|
||||||
/* AT_NULL is zero; clear the rest too */
|
/* AT_NULL is zero; clear the rest too */
|
||||||
|
@ -975,7 +975,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
|
||||||
goto out_free_dentry;
|
goto out_free_dentry;
|
||||||
|
|
||||||
/* Flush all traces of the currently running executable */
|
/* Flush all traces of the currently running executable */
|
||||||
retval = flush_old_exec(bprm);
|
retval = begin_new_exec(bprm);
|
||||||
if (retval)
|
if (retval)
|
||||||
goto out_free_dentry;
|
goto out_free_dentry;
|
||||||
|
|
||||||
|
@ -989,7 +989,6 @@ static int load_elf_binary(struct linux_binprm *bprm)
|
||||||
current->flags |= PF_RANDOMIZE;
|
current->flags |= PF_RANDOMIZE;
|
||||||
|
|
||||||
setup_new_exec(bprm);
|
setup_new_exec(bprm);
|
||||||
install_exec_creds(bprm);
|
|
||||||
|
|
||||||
/* Do this so that we can load the interpreter, if need be. We will
|
/* Do this so that we can load the interpreter, if need be. We will
|
||||||
change some of these later */
|
change some of these later */
|
||||||
|
|
|
@ -338,7 +338,7 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm)
|
||||||
interp_params.flags |= ELF_FDPIC_FLAG_CONSTDISP;
|
interp_params.flags |= ELF_FDPIC_FLAG_CONSTDISP;
|
||||||
|
|
||||||
/* flush all traces of the currently running executable */
|
/* flush all traces of the currently running executable */
|
||||||
retval = flush_old_exec(bprm);
|
retval = begin_new_exec(bprm);
|
||||||
if (retval)
|
if (retval)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
@ -434,7 +434,6 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm)
|
||||||
current->mm->start_stack = current->mm->start_brk + stack_size;
|
current->mm->start_stack = current->mm->start_brk + stack_size;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
install_exec_creds(bprm);
|
|
||||||
if (create_elf_fdpic_tables(bprm, current->mm,
|
if (create_elf_fdpic_tables(bprm, current->mm,
|
||||||
&exec_params, &interp_params) < 0)
|
&exec_params, &interp_params) < 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
@ -589,7 +588,7 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
|
||||||
nitems = 1 + DLINFO_ITEMS + (k_platform ? 1 : 0) +
|
nitems = 1 + DLINFO_ITEMS + (k_platform ? 1 : 0) +
|
||||||
(k_base_platform ? 1 : 0) + AT_VECTOR_SIZE_ARCH;
|
(k_base_platform ? 1 : 0) + AT_VECTOR_SIZE_ARCH;
|
||||||
|
|
||||||
if (bprm->interp_flags & BINPRM_FLAGS_EXECFD)
|
if (bprm->have_execfd)
|
||||||
nitems++;
|
nitems++;
|
||||||
|
|
||||||
csp = sp;
|
csp = sp;
|
||||||
|
@ -629,10 +628,10 @@ static int create_elf_fdpic_tables(struct linux_binprm *bprm,
|
||||||
(elf_addr_t) (unsigned long) u_base_platform);
|
(elf_addr_t) (unsigned long) u_base_platform);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bprm->interp_flags & BINPRM_FLAGS_EXECFD) {
|
if (bprm->have_execfd) {
|
||||||
nr = 0;
|
nr = 0;
|
||||||
csp -= 2 * sizeof(unsigned long);
|
csp -= 2 * sizeof(unsigned long);
|
||||||
NEW_AUX_ENT(AT_EXECFD, bprm->interp_data);
|
NEW_AUX_ENT(AT_EXECFD, bprm->execfd);
|
||||||
}
|
}
|
||||||
|
|
||||||
nr = 0;
|
nr = 0;
|
||||||
|
|
|
@ -48,10 +48,6 @@ static int load_em86(struct linux_binprm *bprm)
|
||||||
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
|
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
|
||||||
return -ENOENT;
|
return -ENOENT;
|
||||||
|
|
||||||
allow_write_access(bprm->file);
|
|
||||||
fput(bprm->file);
|
|
||||||
bprm->file = NULL;
|
|
||||||
|
|
||||||
/* Unlike in the script case, we don't have to do any hairy
|
/* Unlike in the script case, we don't have to do any hairy
|
||||||
* parsing to find our interpreter... it's hardcoded!
|
* parsing to find our interpreter... it's hardcoded!
|
||||||
*/
|
*/
|
||||||
|
@ -89,13 +85,8 @@ static int load_em86(struct linux_binprm *bprm)
|
||||||
if (IS_ERR(file))
|
if (IS_ERR(file))
|
||||||
return PTR_ERR(file);
|
return PTR_ERR(file);
|
||||||
|
|
||||||
bprm->file = file;
|
bprm->interpreter = file;
|
||||||
|
return 0;
|
||||||
retval = prepare_binprm(bprm);
|
|
||||||
if (retval < 0)
|
|
||||||
return retval;
|
|
||||||
|
|
||||||
return search_binary_handler(bprm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct linux_binfmt em86_format = {
|
static struct linux_binfmt em86_format = {
|
||||||
|
|
|
@ -534,7 +534,7 @@ static int load_flat_file(struct linux_binprm *bprm,
|
||||||
|
|
||||||
/* Flush all traces of the currently running executable */
|
/* Flush all traces of the currently running executable */
|
||||||
if (id == 0) {
|
if (id == 0) {
|
||||||
ret = flush_old_exec(bprm);
|
ret = begin_new_exec(bprm);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
@ -963,8 +963,6 @@ static int load_flat_binary(struct linux_binprm *bprm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
install_exec_creds(bprm);
|
|
||||||
|
|
||||||
set_binfmt(&flat_format);
|
set_binfmt(&flat_format);
|
||||||
|
|
||||||
#ifdef CONFIG_MMU
|
#ifdef CONFIG_MMU
|
||||||
|
|
|
@ -134,7 +134,6 @@ static int load_misc_binary(struct linux_binprm *bprm)
|
||||||
Node *fmt;
|
Node *fmt;
|
||||||
struct file *interp_file = NULL;
|
struct file *interp_file = NULL;
|
||||||
int retval;
|
int retval;
|
||||||
int fd_binary = -1;
|
|
||||||
|
|
||||||
retval = -ENOEXEC;
|
retval = -ENOEXEC;
|
||||||
if (!enabled)
|
if (!enabled)
|
||||||
|
@ -160,51 +159,25 @@ static int load_misc_binary(struct linux_binprm *bprm)
|
||||||
goto ret;
|
goto ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fmt->flags & MISC_FMT_OPEN_BINARY) {
|
if (fmt->flags & MISC_FMT_OPEN_BINARY)
|
||||||
|
bprm->have_execfd = 1;
|
||||||
|
|
||||||
/* if the binary should be opened on behalf of the
|
|
||||||
* interpreter than keep it open and assign descriptor
|
|
||||||
* to it
|
|
||||||
*/
|
|
||||||
fd_binary = get_unused_fd_flags(0);
|
|
||||||
if (fd_binary < 0) {
|
|
||||||
retval = fd_binary;
|
|
||||||
goto ret;
|
|
||||||
}
|
|
||||||
fd_install(fd_binary, bprm->file);
|
|
||||||
|
|
||||||
/* if the binary is not readable than enforce mm->dumpable=0
|
|
||||||
regardless of the interpreter's permissions */
|
|
||||||
would_dump(bprm, bprm->file);
|
|
||||||
|
|
||||||
allow_write_access(bprm->file);
|
|
||||||
bprm->file = NULL;
|
|
||||||
|
|
||||||
/* mark the bprm that fd should be passed to interp */
|
|
||||||
bprm->interp_flags |= BINPRM_FLAGS_EXECFD;
|
|
||||||
bprm->interp_data = fd_binary;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
allow_write_access(bprm->file);
|
|
||||||
fput(bprm->file);
|
|
||||||
bprm->file = NULL;
|
|
||||||
}
|
|
||||||
/* make argv[1] be the path to the binary */
|
/* make argv[1] be the path to the binary */
|
||||||
retval = copy_strings_kernel(1, &bprm->interp, bprm);
|
retval = copy_strings_kernel(1, &bprm->interp, bprm);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
goto error;
|
goto ret;
|
||||||
bprm->argc++;
|
bprm->argc++;
|
||||||
|
|
||||||
/* add the interp as argv[0] */
|
/* add the interp as argv[0] */
|
||||||
retval = copy_strings_kernel(1, &fmt->interpreter, bprm);
|
retval = copy_strings_kernel(1, &fmt->interpreter, bprm);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
goto error;
|
goto ret;
|
||||||
bprm->argc++;
|
bprm->argc++;
|
||||||
|
|
||||||
/* Update interp in case binfmt_script needs it. */
|
/* Update interp in case binfmt_script needs it. */
|
||||||
retval = bprm_change_interp(fmt->interpreter, bprm);
|
retval = bprm_change_interp(fmt->interpreter, bprm);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
goto error;
|
goto ret;
|
||||||
|
|
||||||
if (fmt->flags & MISC_FMT_OPEN_FILE) {
|
if (fmt->flags & MISC_FMT_OPEN_FILE) {
|
||||||
interp_file = file_clone_open(fmt->interp_file);
|
interp_file = file_clone_open(fmt->interp_file);
|
||||||
|
@ -215,38 +188,16 @@ static int load_misc_binary(struct linux_binprm *bprm)
|
||||||
}
|
}
|
||||||
retval = PTR_ERR(interp_file);
|
retval = PTR_ERR(interp_file);
|
||||||
if (IS_ERR(interp_file))
|
if (IS_ERR(interp_file))
|
||||||
goto error;
|
goto ret;
|
||||||
|
|
||||||
bprm->file = interp_file;
|
bprm->interpreter = interp_file;
|
||||||
if (fmt->flags & MISC_FMT_CREDENTIALS) {
|
if (fmt->flags & MISC_FMT_CREDENTIALS)
|
||||||
loff_t pos = 0;
|
bprm->execfd_creds = 1;
|
||||||
|
|
||||||
/*
|
|
||||||
* No need to call prepare_binprm(), it's already been
|
|
||||||
* done. bprm->buf is stale, update from interp_file.
|
|
||||||
*/
|
|
||||||
memset(bprm->buf, 0, BINPRM_BUF_SIZE);
|
|
||||||
retval = kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE,
|
|
||||||
&pos);
|
|
||||||
} else
|
|
||||||
retval = prepare_binprm(bprm);
|
|
||||||
|
|
||||||
if (retval < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
retval = search_binary_handler(bprm);
|
|
||||||
if (retval < 0)
|
|
||||||
goto error;
|
|
||||||
|
|
||||||
|
retval = 0;
|
||||||
ret:
|
ret:
|
||||||
dput(fmt->dentry);
|
dput(fmt->dentry);
|
||||||
return retval;
|
return retval;
|
||||||
error:
|
|
||||||
if (fd_binary > 0)
|
|
||||||
ksys_close(fd_binary);
|
|
||||||
bprm->interp_flags = 0;
|
|
||||||
bprm->interp_data = 0;
|
|
||||||
goto ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Command parsers */
|
/* Command parsers */
|
||||||
|
|
|
@ -16,14 +16,14 @@
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
|
||||||
static inline bool spacetab(char c) { return c == ' ' || c == '\t'; }
|
static inline bool spacetab(char c) { return c == ' ' || c == '\t'; }
|
||||||
static inline char *next_non_spacetab(char *first, const char *last)
|
static inline const char *next_non_spacetab(const char *first, const char *last)
|
||||||
{
|
{
|
||||||
for (; first <= last; first++)
|
for (; first <= last; first++)
|
||||||
if (!spacetab(*first))
|
if (!spacetab(*first))
|
||||||
return first;
|
return first;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
static inline char *next_terminator(char *first, const char *last)
|
static inline const char *next_terminator(const char *first, const char *last)
|
||||||
{
|
{
|
||||||
for (; first <= last; first++)
|
for (; first <= last; first++)
|
||||||
if (spacetab(*first) || !*first)
|
if (spacetab(*first) || !*first)
|
||||||
|
@ -33,8 +33,7 @@ static inline char *next_terminator(char *first, const char *last)
|
||||||
|
|
||||||
static int load_script(struct linux_binprm *bprm)
|
static int load_script(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
const char *i_arg, *i_name;
|
const char *i_name, *i_sep, *i_arg, *i_end, *buf_end;
|
||||||
char *cp, *buf_end;
|
|
||||||
struct file *file;
|
struct file *file;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
@ -42,20 +41,6 @@ static int load_script(struct linux_binprm *bprm)
|
||||||
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
|
if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
|
|
||||||
/*
|
|
||||||
* If the script filename will be inaccessible after exec, typically
|
|
||||||
* because it is a "/dev/fd/<fd>/.." path against an O_CLOEXEC fd, give
|
|
||||||
* up now (on the assumption that the interpreter will want to load
|
|
||||||
* this file).
|
|
||||||
*/
|
|
||||||
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
|
|
||||||
return -ENOENT;
|
|
||||||
|
|
||||||
/* Release since we are not mapping a binary into memory. */
|
|
||||||
allow_write_access(bprm->file);
|
|
||||||
fput(bprm->file);
|
|
||||||
bprm->file = NULL;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This section handles parsing the #! line into separate
|
* This section handles parsing the #! line into separate
|
||||||
* interpreter path and argument strings. We must be careful
|
* interpreter path and argument strings. We must be careful
|
||||||
|
@ -71,39 +56,43 @@ static int load_script(struct linux_binprm *bprm)
|
||||||
* parse them on its own.
|
* parse them on its own.
|
||||||
*/
|
*/
|
||||||
buf_end = bprm->buf + sizeof(bprm->buf) - 1;
|
buf_end = bprm->buf + sizeof(bprm->buf) - 1;
|
||||||
cp = strnchr(bprm->buf, sizeof(bprm->buf), '\n');
|
i_end = strnchr(bprm->buf, sizeof(bprm->buf), '\n');
|
||||||
if (!cp) {
|
if (!i_end) {
|
||||||
cp = next_non_spacetab(bprm->buf + 2, buf_end);
|
i_end = next_non_spacetab(bprm->buf + 2, buf_end);
|
||||||
if (!cp)
|
if (!i_end)
|
||||||
return -ENOEXEC; /* Entire buf is spaces/tabs */
|
return -ENOEXEC; /* Entire buf is spaces/tabs */
|
||||||
/*
|
/*
|
||||||
* If there is no later space/tab/NUL we must assume the
|
* If there is no later space/tab/NUL we must assume the
|
||||||
* interpreter path is truncated.
|
* interpreter path is truncated.
|
||||||
*/
|
*/
|
||||||
if (!next_terminator(cp, buf_end))
|
if (!next_terminator(i_end, buf_end))
|
||||||
return -ENOEXEC;
|
return -ENOEXEC;
|
||||||
cp = buf_end;
|
i_end = buf_end;
|
||||||
}
|
}
|
||||||
/* NUL-terminate the buffer and any trailing spaces/tabs. */
|
/* Trim any trailing spaces/tabs from i_end */
|
||||||
*cp = '\0';
|
while (spacetab(i_end[-1]))
|
||||||
while (cp > bprm->buf) {
|
i_end--;
|
||||||
cp--;
|
|
||||||
if ((*cp == ' ') || (*cp == '\t'))
|
/* Skip over leading spaces/tabs */
|
||||||
*cp = '\0';
|
i_name = next_non_spacetab(bprm->buf+2, i_end);
|
||||||
else
|
if (!i_name || (i_name == i_end))
|
||||||
break;
|
|
||||||
}
|
|
||||||
for (cp = bprm->buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
|
|
||||||
if (*cp == '\0')
|
|
||||||
return -ENOEXEC; /* No interpreter name found */
|
return -ENOEXEC; /* No interpreter name found */
|
||||||
i_name = cp;
|
|
||||||
|
/* Is there an optional argument? */
|
||||||
i_arg = NULL;
|
i_arg = NULL;
|
||||||
for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
|
i_sep = next_terminator(i_name, i_end);
|
||||||
/* nothing */ ;
|
if (i_sep && (*i_sep != '\0'))
|
||||||
while ((*cp == ' ') || (*cp == '\t'))
|
i_arg = next_non_spacetab(i_sep, i_end);
|
||||||
*cp++ = '\0';
|
|
||||||
if (*cp)
|
/*
|
||||||
i_arg = cp;
|
* If the script filename will be inaccessible after exec, typically
|
||||||
|
* because it is a "/dev/fd/<fd>/.." path against an O_CLOEXEC fd, give
|
||||||
|
* up now (on the assumption that the interpreter will want to load
|
||||||
|
* this file).
|
||||||
|
*/
|
||||||
|
if (bprm->interp_flags & BINPRM_FLAGS_PATH_INACCESSIBLE)
|
||||||
|
return -ENOENT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* OK, we've parsed out the interpreter name and
|
* OK, we've parsed out the interpreter name and
|
||||||
* (optional) argument.
|
* (optional) argument.
|
||||||
|
@ -121,7 +110,9 @@ static int load_script(struct linux_binprm *bprm)
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
return retval;
|
return retval;
|
||||||
bprm->argc++;
|
bprm->argc++;
|
||||||
|
*((char *)i_end) = '\0';
|
||||||
if (i_arg) {
|
if (i_arg) {
|
||||||
|
*((char *)i_sep) = '\0';
|
||||||
retval = copy_strings_kernel(1, &i_arg, bprm);
|
retval = copy_strings_kernel(1, &i_arg, bprm);
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
return retval;
|
return retval;
|
||||||
|
@ -142,11 +133,8 @@ static int load_script(struct linux_binprm *bprm)
|
||||||
if (IS_ERR(file))
|
if (IS_ERR(file))
|
||||||
return PTR_ERR(file);
|
return PTR_ERR(file);
|
||||||
|
|
||||||
bprm->file = file;
|
bprm->interpreter = file;
|
||||||
retval = prepare_binprm(bprm);
|
return 0;
|
||||||
if (retval < 0)
|
|
||||||
return retval;
|
|
||||||
return search_binary_handler(bprm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct linux_binfmt script_format = {
|
static struct linux_binfmt script_format = {
|
||||||
|
|
316
fs/exec.c
316
fs/exec.c
|
@ -72,6 +72,8 @@
|
||||||
|
|
||||||
#include <trace/events/sched.h>
|
#include <trace/events/sched.h>
|
||||||
|
|
||||||
|
static int bprm_creds_from_file(struct linux_binprm *bprm);
|
||||||
|
|
||||||
int suid_dumpable = 0;
|
int suid_dumpable = 0;
|
||||||
|
|
||||||
static LIST_HEAD(formats);
|
static LIST_HEAD(formats);
|
||||||
|
@ -1051,13 +1053,14 @@ static int exec_mmap(struct mm_struct *mm)
|
||||||
tsk = current;
|
tsk = current;
|
||||||
old_mm = current->mm;
|
old_mm = current->mm;
|
||||||
exec_mm_release(tsk, old_mm);
|
exec_mm_release(tsk, old_mm);
|
||||||
|
if (old_mm)
|
||||||
|
sync_mm_rss(old_mm);
|
||||||
|
|
||||||
ret = mutex_lock_killable(&tsk->signal->exec_update_mutex);
|
ret = mutex_lock_killable(&tsk->signal->exec_update_mutex);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
if (old_mm) {
|
if (old_mm) {
|
||||||
sync_mm_rss(old_mm);
|
|
||||||
/*
|
/*
|
||||||
* Make sure that if there is a core dump in progress
|
* Make sure that if there is a core dump in progress
|
||||||
* for the old mm, we get out and die instead of going
|
* for the old mm, we get out and die instead of going
|
||||||
|
@ -1093,12 +1096,6 @@ static int exec_mmap(struct mm_struct *mm)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* This function makes sure the current process has its own signal table,
|
|
||||||
* so that flush_signal_handlers can later reset the handlers without
|
|
||||||
* disturbing other processes. (Other processes might share the signal
|
|
||||||
* table via the CLONE_SIGHAND option to clone().)
|
|
||||||
*/
|
|
||||||
static int de_thread(struct task_struct *tsk)
|
static int de_thread(struct task_struct *tsk)
|
||||||
{
|
{
|
||||||
struct signal_struct *sig = tsk->signal;
|
struct signal_struct *sig = tsk->signal;
|
||||||
|
@ -1236,6 +1233,12 @@ static int de_thread(struct task_struct *tsk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function makes sure the current process has its own signal table,
|
||||||
|
* so that flush_signal_handlers can later reset the handlers without
|
||||||
|
* disturbing other processes. (Other processes might share the signal
|
||||||
|
* table via the CLONE_SIGHAND option to clone().)
|
||||||
|
*/
|
||||||
static int unshare_sighand(struct task_struct *me)
|
static int unshare_sighand(struct task_struct *me)
|
||||||
{
|
{
|
||||||
struct sighand_struct *oldsighand = me->sighand;
|
struct sighand_struct *oldsighand = me->sighand;
|
||||||
|
@ -1292,13 +1295,23 @@ void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
|
||||||
* Calling this is the point of no return. None of the failures will be
|
* Calling this is the point of no return. None of the failures will be
|
||||||
* seen by userspace since either the process is already taking a fatal
|
* seen by userspace since either the process is already taking a fatal
|
||||||
* signal (via de_thread() or coredump), or will have SEGV raised
|
* signal (via de_thread() or coredump), or will have SEGV raised
|
||||||
* (after exec_mmap()) by search_binary_handlers (see below).
|
* (after exec_mmap()) by search_binary_handler (see below).
|
||||||
*/
|
*/
|
||||||
int flush_old_exec(struct linux_binprm * bprm)
|
int begin_new_exec(struct linux_binprm * bprm)
|
||||||
{
|
{
|
||||||
struct task_struct *me = current;
|
struct task_struct *me = current;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
|
/* Once we are committed compute the creds */
|
||||||
|
retval = bprm_creds_from_file(bprm);
|
||||||
|
if (retval)
|
||||||
|
return retval;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure all future errors are fatal.
|
||||||
|
*/
|
||||||
|
bprm->point_of_no_return = true;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Make this the only thread in the thread group.
|
* Make this the only thread in the thread group.
|
||||||
*/
|
*/
|
||||||
|
@ -1313,7 +1326,10 @@ int flush_old_exec(struct linux_binprm * bprm)
|
||||||
*/
|
*/
|
||||||
set_mm_exe_file(bprm->mm, bprm->file);
|
set_mm_exe_file(bprm->mm, bprm->file);
|
||||||
|
|
||||||
|
/* If the binary is not readable then enforce mm->dumpable=0 */
|
||||||
would_dump(bprm, bprm->file);
|
would_dump(bprm, bprm->file);
|
||||||
|
if (bprm->have_execfd)
|
||||||
|
would_dump(bprm, bprm->executable);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Release all of the old mmap stuff
|
* Release all of the old mmap stuff
|
||||||
|
@ -1323,13 +1339,6 @@ int flush_old_exec(struct linux_binprm * bprm)
|
||||||
if (retval)
|
if (retval)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/*
|
|
||||||
* After setting bprm->called_exec_mmap (to mark that current is
|
|
||||||
* using the prepared mm now), we have nothing left of the original
|
|
||||||
* process. If anything from here on returns an error, the check
|
|
||||||
* in search_binary_handler() will SEGV current.
|
|
||||||
*/
|
|
||||||
bprm->called_exec_mmap = 1;
|
|
||||||
bprm->mm = NULL;
|
bprm->mm = NULL;
|
||||||
|
|
||||||
#ifdef CONFIG_POSIX_TIMERS
|
#ifdef CONFIG_POSIX_TIMERS
|
||||||
|
@ -1342,7 +1351,7 @@ int flush_old_exec(struct linux_binprm * bprm)
|
||||||
*/
|
*/
|
||||||
retval = unshare_sighand(me);
|
retval = unshare_sighand(me);
|
||||||
if (retval)
|
if (retval)
|
||||||
goto out;
|
goto out_unlock;
|
||||||
|
|
||||||
set_fs(USER_DS);
|
set_fs(USER_DS);
|
||||||
me->flags &= ~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD |
|
me->flags &= ~(PF_RANDOMIZE | PF_FORKNOEXEC | PF_KTHREAD |
|
||||||
|
@ -1357,12 +1366,84 @@ int flush_old_exec(struct linux_binprm * bprm)
|
||||||
* undergoing exec(2).
|
* undergoing exec(2).
|
||||||
*/
|
*/
|
||||||
do_close_on_exec(me->files);
|
do_close_on_exec(me->files);
|
||||||
|
|
||||||
|
if (bprm->secureexec) {
|
||||||
|
/* Make sure parent cannot signal privileged process. */
|
||||||
|
me->pdeath_signal = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For secureexec, reset the stack limit to sane default to
|
||||||
|
* avoid bad behavior from the prior rlimits. This has to
|
||||||
|
* happen before arch_pick_mmap_layout(), which examines
|
||||||
|
* RLIMIT_STACK, but after the point of no return to avoid
|
||||||
|
* needing to clean up the change on failure.
|
||||||
|
*/
|
||||||
|
if (bprm->rlim_stack.rlim_cur > _STK_LIM)
|
||||||
|
bprm->rlim_stack.rlim_cur = _STK_LIM;
|
||||||
|
}
|
||||||
|
|
||||||
|
me->sas_ss_sp = me->sas_ss_size = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Figure out dumpability. Note that this checking only of current
|
||||||
|
* is wrong, but userspace depends on it. This should be testing
|
||||||
|
* bprm->secureexec instead.
|
||||||
|
*/
|
||||||
|
if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP ||
|
||||||
|
!(uid_eq(current_euid(), current_uid()) &&
|
||||||
|
gid_eq(current_egid(), current_gid())))
|
||||||
|
set_dumpable(current->mm, suid_dumpable);
|
||||||
|
else
|
||||||
|
set_dumpable(current->mm, SUID_DUMP_USER);
|
||||||
|
|
||||||
|
perf_event_exec();
|
||||||
|
__set_task_comm(me, kbasename(bprm->filename), true);
|
||||||
|
|
||||||
|
/* An exec changes our domain. We are no longer part of the thread
|
||||||
|
group */
|
||||||
|
WRITE_ONCE(me->self_exec_id, me->self_exec_id + 1);
|
||||||
|
flush_signal_handlers(me, 0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* install the new credentials for this executable
|
||||||
|
*/
|
||||||
|
security_bprm_committing_creds(bprm);
|
||||||
|
|
||||||
|
commit_creds(bprm->cred);
|
||||||
|
bprm->cred = NULL;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable monitoring for regular users
|
||||||
|
* when executing setuid binaries. Must
|
||||||
|
* wait until new credentials are committed
|
||||||
|
* by commit_creds() above
|
||||||
|
*/
|
||||||
|
if (get_dumpable(me->mm) != SUID_DUMP_USER)
|
||||||
|
perf_event_exit_task(me);
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
security_bprm_committed_creds(bprm);
|
||||||
|
|
||||||
|
/* Pass the opened binary to the interpreter. */
|
||||||
|
if (bprm->have_execfd) {
|
||||||
|
retval = get_unused_fd_flags(0);
|
||||||
|
if (retval < 0)
|
||||||
|
goto out_unlock;
|
||||||
|
fd_install(retval, bprm->executable);
|
||||||
|
bprm->executable = NULL;
|
||||||
|
bprm->execfd = retval;
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
out_unlock:
|
||||||
|
mutex_unlock(&me->signal->exec_update_mutex);
|
||||||
out:
|
out:
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(flush_old_exec);
|
EXPORT_SYMBOL(begin_new_exec);
|
||||||
|
|
||||||
void would_dump(struct linux_binprm *bprm, struct file *file)
|
void would_dump(struct linux_binprm *bprm, struct file *file)
|
||||||
{
|
{
|
||||||
|
@ -1387,58 +1468,20 @@ EXPORT_SYMBOL(would_dump);
|
||||||
|
|
||||||
void setup_new_exec(struct linux_binprm * bprm)
|
void setup_new_exec(struct linux_binprm * bprm)
|
||||||
{
|
{
|
||||||
/*
|
/* Setup things that can depend upon the personality */
|
||||||
* Once here, prepare_binrpm() will not be called any more, so
|
struct task_struct *me = current;
|
||||||
* the final state of setuid/setgid/fscaps can be merged into the
|
|
||||||
* secureexec flag.
|
|
||||||
*/
|
|
||||||
bprm->secureexec |= bprm->cap_elevated;
|
|
||||||
|
|
||||||
if (bprm->secureexec) {
|
arch_pick_mmap_layout(me->mm, &bprm->rlim_stack);
|
||||||
/* Make sure parent cannot signal privileged process. */
|
|
||||||
current->pdeath_signal = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For secureexec, reset the stack limit to sane default to
|
|
||||||
* avoid bad behavior from the prior rlimits. This has to
|
|
||||||
* happen before arch_pick_mmap_layout(), which examines
|
|
||||||
* RLIMIT_STACK, but after the point of no return to avoid
|
|
||||||
* needing to clean up the change on failure.
|
|
||||||
*/
|
|
||||||
if (bprm->rlim_stack.rlim_cur > _STK_LIM)
|
|
||||||
bprm->rlim_stack.rlim_cur = _STK_LIM;
|
|
||||||
}
|
|
||||||
|
|
||||||
arch_pick_mmap_layout(current->mm, &bprm->rlim_stack);
|
|
||||||
|
|
||||||
current->sas_ss_sp = current->sas_ss_size = 0;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Figure out dumpability. Note that this checking only of current
|
|
||||||
* is wrong, but userspace depends on it. This should be testing
|
|
||||||
* bprm->secureexec instead.
|
|
||||||
*/
|
|
||||||
if (bprm->interp_flags & BINPRM_FLAGS_ENFORCE_NONDUMP ||
|
|
||||||
!(uid_eq(current_euid(), current_uid()) &&
|
|
||||||
gid_eq(current_egid(), current_gid())))
|
|
||||||
set_dumpable(current->mm, suid_dumpable);
|
|
||||||
else
|
|
||||||
set_dumpable(current->mm, SUID_DUMP_USER);
|
|
||||||
|
|
||||||
arch_setup_new_exec();
|
arch_setup_new_exec();
|
||||||
perf_event_exec();
|
|
||||||
__set_task_comm(current, kbasename(bprm->filename), true);
|
|
||||||
|
|
||||||
/* Set the new mm task size. We have to do that late because it may
|
/* Set the new mm task size. We have to do that late because it may
|
||||||
* depend on TIF_32BIT which is only updated in flush_thread() on
|
* depend on TIF_32BIT which is only updated in flush_thread() on
|
||||||
* some architectures like powerpc
|
* some architectures like powerpc
|
||||||
*/
|
*/
|
||||||
current->mm->task_size = TASK_SIZE;
|
me->mm->task_size = TASK_SIZE;
|
||||||
|
mutex_unlock(&me->signal->exec_update_mutex);
|
||||||
/* An exec changes our domain. We are no longer part of the thread
|
mutex_unlock(&me->signal->cred_guard_mutex);
|
||||||
group */
|
|
||||||
WRITE_ONCE(current->self_exec_id, current->self_exec_id + 1);
|
|
||||||
flush_signal_handlers(current, 0);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(setup_new_exec);
|
EXPORT_SYMBOL(setup_new_exec);
|
||||||
|
|
||||||
|
@ -1454,7 +1497,7 @@ EXPORT_SYMBOL(finalize_exec);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Prepare credentials and lock ->cred_guard_mutex.
|
* Prepare credentials and lock ->cred_guard_mutex.
|
||||||
* install_exec_creds() commits the new creds and drops the lock.
|
* setup_new_exec() commits the new creds and drops the lock.
|
||||||
* Or, if exec fails before, free_bprm() should release ->cred and
|
* Or, if exec fails before, free_bprm() should release ->cred and
|
||||||
* and unlock.
|
* and unlock.
|
||||||
*/
|
*/
|
||||||
|
@ -1475,8 +1518,6 @@ static void free_bprm(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
free_arg_pages(bprm);
|
free_arg_pages(bprm);
|
||||||
if (bprm->cred) {
|
if (bprm->cred) {
|
||||||
if (bprm->called_exec_mmap)
|
|
||||||
mutex_unlock(¤t->signal->exec_update_mutex);
|
|
||||||
mutex_unlock(¤t->signal->cred_guard_mutex);
|
mutex_unlock(¤t->signal->cred_guard_mutex);
|
||||||
abort_creds(bprm->cred);
|
abort_creds(bprm->cred);
|
||||||
}
|
}
|
||||||
|
@ -1484,6 +1525,8 @@ static void free_bprm(struct linux_binprm *bprm)
|
||||||
allow_write_access(bprm->file);
|
allow_write_access(bprm->file);
|
||||||
fput(bprm->file);
|
fput(bprm->file);
|
||||||
}
|
}
|
||||||
|
if (bprm->executable)
|
||||||
|
fput(bprm->executable);
|
||||||
/* If a binfmt changed the interp, free it. */
|
/* If a binfmt changed the interp, free it. */
|
||||||
if (bprm->interp != bprm->filename)
|
if (bprm->interp != bprm->filename)
|
||||||
kfree(bprm->interp);
|
kfree(bprm->interp);
|
||||||
|
@ -1502,35 +1545,6 @@ int bprm_change_interp(const char *interp, struct linux_binprm *bprm)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(bprm_change_interp);
|
EXPORT_SYMBOL(bprm_change_interp);
|
||||||
|
|
||||||
/*
|
|
||||||
* install the new credentials for this executable
|
|
||||||
*/
|
|
||||||
void install_exec_creds(struct linux_binprm *bprm)
|
|
||||||
{
|
|
||||||
security_bprm_committing_creds(bprm);
|
|
||||||
|
|
||||||
commit_creds(bprm->cred);
|
|
||||||
bprm->cred = NULL;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Disable monitoring for regular users
|
|
||||||
* when executing setuid binaries. Must
|
|
||||||
* wait until new credentials are committed
|
|
||||||
* by commit_creds() above
|
|
||||||
*/
|
|
||||||
if (get_dumpable(current->mm) != SUID_DUMP_USER)
|
|
||||||
perf_event_exit_task(current);
|
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
security_bprm_committed_creds(bprm);
|
|
||||||
mutex_unlock(¤t->signal->exec_update_mutex);
|
|
||||||
mutex_unlock(¤t->signal->cred_guard_mutex);
|
|
||||||
}
|
|
||||||
EXPORT_SYMBOL(install_exec_creds);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* determine how safe it is to execute the proposed program
|
* determine how safe it is to execute the proposed program
|
||||||
* - the caller must hold ->cred_guard_mutex to protect against
|
* - the caller must hold ->cred_guard_mutex to protect against
|
||||||
|
@ -1568,29 +1582,21 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
|
||||||
spin_unlock(&p->fs->lock);
|
spin_unlock(&p->fs->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void bprm_fill_uid(struct linux_binprm *bprm)
|
static void bprm_fill_uid(struct linux_binprm *bprm, struct file *file)
|
||||||
{
|
{
|
||||||
|
/* Handle suid and sgid on files */
|
||||||
struct inode *inode;
|
struct inode *inode;
|
||||||
unsigned int mode;
|
unsigned int mode;
|
||||||
kuid_t uid;
|
kuid_t uid;
|
||||||
kgid_t gid;
|
kgid_t gid;
|
||||||
|
|
||||||
/*
|
if (!mnt_may_suid(file->f_path.mnt))
|
||||||
* Since this can be called multiple times (via prepare_binprm),
|
|
||||||
* we must clear any previous work done when setting set[ug]id
|
|
||||||
* bits from any earlier bprm->file uses (for example when run
|
|
||||||
* first for a setuid script then again for its interpreter).
|
|
||||||
*/
|
|
||||||
bprm->cred->euid = current_euid();
|
|
||||||
bprm->cred->egid = current_egid();
|
|
||||||
|
|
||||||
if (!mnt_may_suid(bprm->file->f_path.mnt))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (task_no_new_privs(current))
|
if (task_no_new_privs(current))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
inode = bprm->file->f_path.dentry->d_inode;
|
inode = file->f_path.dentry->d_inode;
|
||||||
mode = READ_ONCE(inode->i_mode);
|
mode = READ_ONCE(inode->i_mode);
|
||||||
if (!(mode & (S_ISUID|S_ISGID)))
|
if (!(mode & (S_ISUID|S_ISGID)))
|
||||||
return;
|
return;
|
||||||
|
@ -1620,31 +1626,32 @@ static void bprm_fill_uid(struct linux_binprm *bprm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Compute brpm->cred based upon the final binary.
|
||||||
|
*/
|
||||||
|
static int bprm_creds_from_file(struct linux_binprm *bprm)
|
||||||
|
{
|
||||||
|
/* Compute creds based on which file? */
|
||||||
|
struct file *file = bprm->execfd_creds ? bprm->executable : bprm->file;
|
||||||
|
|
||||||
|
bprm_fill_uid(bprm, file);
|
||||||
|
return security_bprm_creds_from_file(bprm, file);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fill the binprm structure from the inode.
|
* Fill the binprm structure from the inode.
|
||||||
* Check permissions, then read the first BINPRM_BUF_SIZE bytes
|
* Read the first BINPRM_BUF_SIZE bytes
|
||||||
*
|
*
|
||||||
* This may be called multiple times for binary chains (scripts for example).
|
* This may be called multiple times for binary chains (scripts for example).
|
||||||
*/
|
*/
|
||||||
int prepare_binprm(struct linux_binprm *bprm)
|
static int prepare_binprm(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
int retval;
|
|
||||||
loff_t pos = 0;
|
loff_t pos = 0;
|
||||||
|
|
||||||
bprm_fill_uid(bprm);
|
|
||||||
|
|
||||||
/* fill in binprm security blob */
|
|
||||||
retval = security_bprm_set_creds(bprm);
|
|
||||||
if (retval)
|
|
||||||
return retval;
|
|
||||||
bprm->called_set_creds = 1;
|
|
||||||
|
|
||||||
memset(bprm->buf, 0, BINPRM_BUF_SIZE);
|
memset(bprm->buf, 0, BINPRM_BUF_SIZE);
|
||||||
return kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE, &pos);
|
return kernel_read(bprm->file, bprm->buf, BINPRM_BUF_SIZE, &pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXPORT_SYMBOL(prepare_binprm);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Arguments are '\0' separated strings found at the location bprm->p
|
* Arguments are '\0' separated strings found at the location bprm->p
|
||||||
* points to; chop off the first by relocating brpm->p to right after
|
* points to; chop off the first by relocating brpm->p to right after
|
||||||
|
@ -1690,15 +1697,15 @@ EXPORT_SYMBOL(remove_arg_zero);
|
||||||
/*
|
/*
|
||||||
* cycle the list of binary formats handler, until one recognizes the image
|
* cycle the list of binary formats handler, until one recognizes the image
|
||||||
*/
|
*/
|
||||||
int search_binary_handler(struct linux_binprm *bprm)
|
static int search_binary_handler(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
bool need_retry = IS_ENABLED(CONFIG_MODULES);
|
bool need_retry = IS_ENABLED(CONFIG_MODULES);
|
||||||
struct linux_binfmt *fmt;
|
struct linux_binfmt *fmt;
|
||||||
int retval;
|
int retval;
|
||||||
|
|
||||||
/* This allows 4 levels of binfmt rewrites before failing hard. */
|
retval = prepare_binprm(bprm);
|
||||||
if (bprm->recursion_depth > 5)
|
if (retval < 0)
|
||||||
return -ELOOP;
|
return retval;
|
||||||
|
|
||||||
retval = security_bprm_check(bprm);
|
retval = security_bprm_check(bprm);
|
||||||
if (retval)
|
if (retval)
|
||||||
|
@ -1712,19 +1719,11 @@ int search_binary_handler(struct linux_binprm *bprm)
|
||||||
continue;
|
continue;
|
||||||
read_unlock(&binfmt_lock);
|
read_unlock(&binfmt_lock);
|
||||||
|
|
||||||
bprm->recursion_depth++;
|
|
||||||
retval = fmt->load_binary(bprm);
|
retval = fmt->load_binary(bprm);
|
||||||
bprm->recursion_depth--;
|
|
||||||
|
|
||||||
read_lock(&binfmt_lock);
|
read_lock(&binfmt_lock);
|
||||||
put_binfmt(fmt);
|
put_binfmt(fmt);
|
||||||
if (retval < 0 && bprm->called_exec_mmap) {
|
if (bprm->point_of_no_return || (retval != -ENOEXEC)) {
|
||||||
/* we got to flush_old_exec() and failed after it */
|
|
||||||
read_unlock(&binfmt_lock);
|
|
||||||
force_sigsegv(SIGSEGV);
|
|
||||||
return retval;
|
|
||||||
}
|
|
||||||
if (retval != -ENOEXEC || !bprm->file) {
|
|
||||||
read_unlock(&binfmt_lock);
|
read_unlock(&binfmt_lock);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
@ -1743,12 +1742,11 @@ int search_binary_handler(struct linux_binprm *bprm)
|
||||||
|
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(search_binary_handler);
|
|
||||||
|
|
||||||
static int exec_binprm(struct linux_binprm *bprm)
|
static int exec_binprm(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
pid_t old_pid, old_vpid;
|
pid_t old_pid, old_vpid;
|
||||||
int ret;
|
int ret, depth;
|
||||||
|
|
||||||
/* Need to fetch pid before load_binary changes it */
|
/* Need to fetch pid before load_binary changes it */
|
||||||
old_pid = current->pid;
|
old_pid = current->pid;
|
||||||
|
@ -1756,15 +1754,38 @@ static int exec_binprm(struct linux_binprm *bprm)
|
||||||
old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
|
old_vpid = task_pid_nr_ns(current, task_active_pid_ns(current->parent));
|
||||||
rcu_read_unlock();
|
rcu_read_unlock();
|
||||||
|
|
||||||
ret = search_binary_handler(bprm);
|
/* This allows 4 levels of binfmt rewrites before failing hard. */
|
||||||
if (ret >= 0) {
|
for (depth = 0;; depth++) {
|
||||||
audit_bprm(bprm);
|
struct file *exec;
|
||||||
trace_sched_process_exec(current, old_pid, bprm);
|
if (depth > 5)
|
||||||
ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
|
return -ELOOP;
|
||||||
proc_exec_connector(current);
|
|
||||||
|
ret = search_binary_handler(bprm);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
if (!bprm->interpreter)
|
||||||
|
break;
|
||||||
|
|
||||||
|
exec = bprm->file;
|
||||||
|
bprm->file = bprm->interpreter;
|
||||||
|
bprm->interpreter = NULL;
|
||||||
|
|
||||||
|
allow_write_access(exec);
|
||||||
|
if (unlikely(bprm->have_execfd)) {
|
||||||
|
if (bprm->executable) {
|
||||||
|
fput(exec);
|
||||||
|
return -ENOEXEC;
|
||||||
|
}
|
||||||
|
bprm->executable = exec;
|
||||||
|
} else
|
||||||
|
fput(exec);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
audit_bprm(bprm);
|
||||||
|
trace_sched_process_exec(current, old_pid, bprm);
|
||||||
|
ptrace_event(PTRACE_EVENT_EXEC, old_vpid);
|
||||||
|
proc_exec_connector(current);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1857,8 +1878,9 @@ static int __do_execve_file(int fd, struct filename *filename,
|
||||||
if (retval < 0)
|
if (retval < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
retval = prepare_binprm(bprm);
|
/* Set the unchanging part of bprm->cred */
|
||||||
if (retval < 0)
|
retval = security_bprm_creds_for_exec(bprm);
|
||||||
|
if (retval)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
retval = copy_strings_kernel(1, &bprm->filename, bprm);
|
retval = copy_strings_kernel(1, &bprm->filename, bprm);
|
||||||
|
@ -1893,6 +1915,14 @@ static int __do_execve_file(int fd, struct filename *filename,
|
||||||
return retval;
|
return retval;
|
||||||
|
|
||||||
out:
|
out:
|
||||||
|
/*
|
||||||
|
* If past the point of no return ensure the the code never
|
||||||
|
* returns to the userspace process. Use an existing fatal
|
||||||
|
* signal if present otherwise terminate the process with
|
||||||
|
* SIGSEGV.
|
||||||
|
*/
|
||||||
|
if (bprm->point_of_no_return && !fatal_signal_pending(current))
|
||||||
|
force_sigsegv(SIGSEGV);
|
||||||
if (bprm->mm) {
|
if (bprm->mm) {
|
||||||
acct_arg_size(bprm, 0);
|
acct_arg_size(bprm, 0);
|
||||||
mmput(bprm->mm);
|
mmput(bprm->mm);
|
||||||
|
|
|
@ -26,35 +26,27 @@ struct linux_binprm {
|
||||||
unsigned long p; /* current top of mem */
|
unsigned long p; /* current top of mem */
|
||||||
unsigned long argmin; /* rlimit marker for copy_strings() */
|
unsigned long argmin; /* rlimit marker for copy_strings() */
|
||||||
unsigned int
|
unsigned int
|
||||||
|
/* Should an execfd be passed to userspace? */
|
||||||
|
have_execfd:1,
|
||||||
|
|
||||||
|
/* Use the creds of a script (see binfmt_misc) */
|
||||||
|
execfd_creds:1,
|
||||||
/*
|
/*
|
||||||
* True after the bprm_set_creds hook has been called once
|
* Set by bprm_creds_for_exec hook to indicate a
|
||||||
* (multiple calls can be made via prepare_binprm() for
|
* privilege-gaining exec has happened. Used to set
|
||||||
* binfmt_script/misc).
|
* AT_SECURE auxv for glibc.
|
||||||
*/
|
|
||||||
called_set_creds:1,
|
|
||||||
/*
|
|
||||||
* True if most recent call to the commoncaps bprm_set_creds
|
|
||||||
* hook (due to multiple prepare_binprm() calls from the
|
|
||||||
* binfmt_script/misc handlers) resulted in elevated
|
|
||||||
* privileges.
|
|
||||||
*/
|
|
||||||
cap_elevated:1,
|
|
||||||
/*
|
|
||||||
* Set by bprm_set_creds hook to indicate a privilege-gaining
|
|
||||||
* exec has happened. Used to sanitize execution environment
|
|
||||||
* and to set AT_SECURE auxv for glibc.
|
|
||||||
*/
|
*/
|
||||||
secureexec:1,
|
secureexec:1,
|
||||||
/*
|
/*
|
||||||
* Set by flush_old_exec, when exec_mmap has been called.
|
* Set when errors can no longer be returned to the
|
||||||
* This is past the point of no return, when the
|
* original userspace.
|
||||||
* exec_update_mutex has been taken.
|
|
||||||
*/
|
*/
|
||||||
called_exec_mmap:1;
|
point_of_no_return:1;
|
||||||
#ifdef __alpha__
|
#ifdef __alpha__
|
||||||
unsigned int taso:1;
|
unsigned int taso:1;
|
||||||
#endif
|
#endif
|
||||||
unsigned int recursion_depth; /* only for search_binary_handler() */
|
struct file * executable; /* Executable to pass to the interpreter */
|
||||||
|
struct file * interpreter;
|
||||||
struct file * file;
|
struct file * file;
|
||||||
struct cred *cred; /* new credentials */
|
struct cred *cred; /* new credentials */
|
||||||
int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
|
int unsafe; /* how unsafe this exec is (mask of LSM_UNSAFE_*) */
|
||||||
|
@ -65,7 +57,7 @@ struct linux_binprm {
|
||||||
of the time same as filename, but could be
|
of the time same as filename, but could be
|
||||||
different for binfmt_{misc,script} */
|
different for binfmt_{misc,script} */
|
||||||
unsigned interp_flags;
|
unsigned interp_flags;
|
||||||
unsigned interp_data;
|
int execfd; /* File descriptor of the executable */
|
||||||
unsigned long loader, exec;
|
unsigned long loader, exec;
|
||||||
|
|
||||||
struct rlimit rlim_stack; /* Saved RLIMIT_STACK used during exec. */
|
struct rlimit rlim_stack; /* Saved RLIMIT_STACK used during exec. */
|
||||||
|
@ -76,10 +68,6 @@ struct linux_binprm {
|
||||||
#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
|
#define BINPRM_FLAGS_ENFORCE_NONDUMP_BIT 0
|
||||||
#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT)
|
#define BINPRM_FLAGS_ENFORCE_NONDUMP (1 << BINPRM_FLAGS_ENFORCE_NONDUMP_BIT)
|
||||||
|
|
||||||
/* fd of the binary should be passed to the interpreter */
|
|
||||||
#define BINPRM_FLAGS_EXECFD_BIT 1
|
|
||||||
#define BINPRM_FLAGS_EXECFD (1 << BINPRM_FLAGS_EXECFD_BIT)
|
|
||||||
|
|
||||||
/* filename of the binary will be inaccessible after exec */
|
/* filename of the binary will be inaccessible after exec */
|
||||||
#define BINPRM_FLAGS_PATH_INACCESSIBLE_BIT 2
|
#define BINPRM_FLAGS_PATH_INACCESSIBLE_BIT 2
|
||||||
#define BINPRM_FLAGS_PATH_INACCESSIBLE (1 << BINPRM_FLAGS_PATH_INACCESSIBLE_BIT)
|
#define BINPRM_FLAGS_PATH_INACCESSIBLE (1 << BINPRM_FLAGS_PATH_INACCESSIBLE_BIT)
|
||||||
|
@ -123,10 +111,8 @@ static inline void insert_binfmt(struct linux_binfmt *fmt)
|
||||||
|
|
||||||
extern void unregister_binfmt(struct linux_binfmt *);
|
extern void unregister_binfmt(struct linux_binfmt *);
|
||||||
|
|
||||||
extern int prepare_binprm(struct linux_binprm *);
|
|
||||||
extern int __must_check remove_arg_zero(struct linux_binprm *);
|
extern int __must_check remove_arg_zero(struct linux_binprm *);
|
||||||
extern int search_binary_handler(struct linux_binprm *);
|
extern int begin_new_exec(struct linux_binprm * bprm);
|
||||||
extern int flush_old_exec(struct linux_binprm * bprm);
|
|
||||||
extern void setup_new_exec(struct linux_binprm * bprm);
|
extern void setup_new_exec(struct linux_binprm * bprm);
|
||||||
extern void finalize_exec(struct linux_binprm *bprm);
|
extern void finalize_exec(struct linux_binprm *bprm);
|
||||||
extern void would_dump(struct linux_binprm *, struct file *);
|
extern void would_dump(struct linux_binprm *, struct file *);
|
||||||
|
@ -146,7 +132,6 @@ extern int transfer_args_to_stack(struct linux_binprm *bprm,
|
||||||
extern int bprm_change_interp(const char *interp, struct linux_binprm *bprm);
|
extern int bprm_change_interp(const char *interp, struct linux_binprm *bprm);
|
||||||
extern int copy_strings_kernel(int argc, const char *const *argv,
|
extern int copy_strings_kernel(int argc, const char *const *argv,
|
||||||
struct linux_binprm *bprm);
|
struct linux_binprm *bprm);
|
||||||
extern void install_exec_creds(struct linux_binprm *bprm);
|
|
||||||
extern void set_binfmt(struct linux_binfmt *new);
|
extern void set_binfmt(struct linux_binfmt *new);
|
||||||
extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t);
|
extern ssize_t read_code(struct file *, unsigned long, loff_t, size_t);
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,8 @@ LSM_HOOK(int, 0, syslog, int type)
|
||||||
LSM_HOOK(int, 0, settime, const struct timespec64 *ts,
|
LSM_HOOK(int, 0, settime, const struct timespec64 *ts,
|
||||||
const struct timezone *tz)
|
const struct timezone *tz)
|
||||||
LSM_HOOK(int, 0, vm_enough_memory, struct mm_struct *mm, long pages)
|
LSM_HOOK(int, 0, vm_enough_memory, struct mm_struct *mm, long pages)
|
||||||
LSM_HOOK(int, 0, bprm_set_creds, struct linux_binprm *bprm)
|
LSM_HOOK(int, 0, bprm_creds_for_exec, struct linux_binprm *bprm)
|
||||||
|
LSM_HOOK(int, 0, bprm_creds_from_file, struct linux_binprm *bprm, struct file *file)
|
||||||
LSM_HOOK(int, 0, bprm_check_security, struct linux_binprm *bprm)
|
LSM_HOOK(int, 0, bprm_check_security, struct linux_binprm *bprm)
|
||||||
LSM_HOOK(void, LSM_RET_VOID, bprm_committing_creds, struct linux_binprm *bprm)
|
LSM_HOOK(void, LSM_RET_VOID, bprm_committing_creds, struct linux_binprm *bprm)
|
||||||
LSM_HOOK(void, LSM_RET_VOID, bprm_committed_creds, struct linux_binprm *bprm)
|
LSM_HOOK(void, LSM_RET_VOID, bprm_committed_creds, struct linux_binprm *bprm)
|
||||||
|
|
|
@ -34,40 +34,48 @@
|
||||||
*
|
*
|
||||||
* Security hooks for program execution operations.
|
* Security hooks for program execution operations.
|
||||||
*
|
*
|
||||||
* @bprm_set_creds:
|
* @bprm_creds_for_exec:
|
||||||
* Save security information in the bprm->security field, typically based
|
* If the setup in prepare_exec_creds did not setup @bprm->cred->security
|
||||||
* on information about the bprm->file, for later use by the apply_creds
|
* properly for executing @bprm->file, update the LSM's portion of
|
||||||
* hook. This hook may also optionally check permissions (e.g. for
|
* @bprm->cred->security to be what commit_creds needs to install for the
|
||||||
* transitions between security domains).
|
* new program. This hook may also optionally check permissions
|
||||||
* This hook may be called multiple times during a single execve, e.g. for
|
* (e.g. for transitions between security domains).
|
||||||
* interpreters. The hook can tell whether it has already been called by
|
* The hook must set @bprm->secureexec to 1 if AT_SECURE should be set to
|
||||||
* checking to see if @bprm->security is non-NULL. If so, then the hook
|
* request libc enable secure mode.
|
||||||
* may decide either to retain the security information saved earlier or
|
* @bprm contains the linux_binprm structure.
|
||||||
* to replace it. The hook must set @bprm->secureexec to 1 if a "secure
|
* Return 0 if the hook is successful and permission is granted.
|
||||||
* exec" has happened as a result of this hook call. The flag is used to
|
* @bprm_creds_from_file:
|
||||||
* indicate the need for a sanitized execution environment, and is also
|
* If @file is setpcap, suid, sgid or otherwise marked to change
|
||||||
* passed in the ELF auxiliary table on the initial stack to indicate
|
* privilege upon exec, update @bprm->cred to reflect that change.
|
||||||
* whether libc should enable secure mode.
|
* This is called after finding the binary that will be executed.
|
||||||
|
* without an interpreter. This ensures that the credentials will not
|
||||||
|
* be derived from a script that the binary will need to reopen, which
|
||||||
|
* when reopend may end up being a completely different file. This
|
||||||
|
* hook may also optionally check permissions (e.g. for transitions
|
||||||
|
* between security domains).
|
||||||
|
* The hook must set @bprm->secureexec to 1 if AT_SECURE should be set to
|
||||||
|
* request libc enable secure mode.
|
||||||
|
* The hook must add to @bprm->per_clear any personality flags that
|
||||||
|
* should be cleared from current->personality.
|
||||||
* @bprm contains the linux_binprm structure.
|
* @bprm contains the linux_binprm structure.
|
||||||
* Return 0 if the hook is successful and permission is granted.
|
* Return 0 if the hook is successful and permission is granted.
|
||||||
* @bprm_check_security:
|
* @bprm_check_security:
|
||||||
* This hook mediates the point when a search for a binary handler will
|
* This hook mediates the point when a search for a binary handler will
|
||||||
* begin. It allows a check the @bprm->security value which is set in the
|
* begin. It allows a check against the @bprm->cred->security value
|
||||||
* preceding set_creds call. The primary difference from set_creds is
|
* which was set in the preceding creds_for_exec call. The argv list and
|
||||||
* that the argv list and envp list are reliably available in @bprm. This
|
* envp list are reliably available in @bprm. This hook may be called
|
||||||
* hook may be called multiple times during a single execve; and in each
|
* multiple times during a single execve.
|
||||||
* pass set_creds is called first.
|
|
||||||
* @bprm contains the linux_binprm structure.
|
* @bprm contains the linux_binprm structure.
|
||||||
* Return 0 if the hook is successful and permission is granted.
|
* Return 0 if the hook is successful and permission is granted.
|
||||||
* @bprm_committing_creds:
|
* @bprm_committing_creds:
|
||||||
* Prepare to install the new security attributes of a process being
|
* Prepare to install the new security attributes of a process being
|
||||||
* transformed by an execve operation, based on the old credentials
|
* transformed by an execve operation, based on the old credentials
|
||||||
* pointed to by @current->cred and the information set in @bprm->cred by
|
* pointed to by @current->cred and the information set in @bprm->cred by
|
||||||
* the bprm_set_creds hook. @bprm points to the linux_binprm structure.
|
* the bprm_creds_for_exec hook. @bprm points to the linux_binprm
|
||||||
* This hook is a good place to perform state changes on the process such
|
* structure. This hook is a good place to perform state changes on the
|
||||||
* as closing open file descriptors to which access will no longer be
|
* process such as closing open file descriptors to which access will no
|
||||||
* granted when the attributes are changed. This is called immediately
|
* longer be granted when the attributes are changed. This is called
|
||||||
* before commit_creds().
|
* immediately before commit_creds().
|
||||||
* @bprm_committed_creds:
|
* @bprm_committed_creds:
|
||||||
* Tidy up after the installation of the new security attributes of a
|
* Tidy up after the installation of the new security attributes of a
|
||||||
* process being transformed by an execve operation. The new credentials
|
* process being transformed by an execve operation. The new credentials
|
||||||
|
|
|
@ -140,7 +140,7 @@ extern int cap_capset(struct cred *new, const struct cred *old,
|
||||||
const kernel_cap_t *effective,
|
const kernel_cap_t *effective,
|
||||||
const kernel_cap_t *inheritable,
|
const kernel_cap_t *inheritable,
|
||||||
const kernel_cap_t *permitted);
|
const kernel_cap_t *permitted);
|
||||||
extern int cap_bprm_set_creds(struct linux_binprm *bprm);
|
extern int cap_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file);
|
||||||
extern int cap_inode_setxattr(struct dentry *dentry, const char *name,
|
extern int cap_inode_setxattr(struct dentry *dentry, const char *name,
|
||||||
const void *value, size_t size, int flags);
|
const void *value, size_t size, int flags);
|
||||||
extern int cap_inode_removexattr(struct dentry *dentry, const char *name);
|
extern int cap_inode_removexattr(struct dentry *dentry, const char *name);
|
||||||
|
@ -276,7 +276,8 @@ int security_quota_on(struct dentry *dentry);
|
||||||
int security_syslog(int type);
|
int security_syslog(int type);
|
||||||
int security_settime64(const struct timespec64 *ts, const struct timezone *tz);
|
int security_settime64(const struct timespec64 *ts, const struct timezone *tz);
|
||||||
int security_vm_enough_memory_mm(struct mm_struct *mm, long pages);
|
int security_vm_enough_memory_mm(struct mm_struct *mm, long pages);
|
||||||
int security_bprm_set_creds(struct linux_binprm *bprm);
|
int security_bprm_creds_for_exec(struct linux_binprm *bprm);
|
||||||
|
int security_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file);
|
||||||
int security_bprm_check(struct linux_binprm *bprm);
|
int security_bprm_check(struct linux_binprm *bprm);
|
||||||
void security_bprm_committing_creds(struct linux_binprm *bprm);
|
void security_bprm_committing_creds(struct linux_binprm *bprm);
|
||||||
void security_bprm_committed_creds(struct linux_binprm *bprm);
|
void security_bprm_committed_creds(struct linux_binprm *bprm);
|
||||||
|
@ -569,9 +570,15 @@ static inline int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
|
||||||
return __vm_enough_memory(mm, pages, cap_vm_enough_memory(mm, pages));
|
return __vm_enough_memory(mm, pages, cap_vm_enough_memory(mm, pages));
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int security_bprm_set_creds(struct linux_binprm *bprm)
|
static inline int security_bprm_creds_for_exec(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
return cap_bprm_set_creds(bprm);
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int security_bprm_creds_from_file(struct linux_binprm *bprm,
|
||||||
|
struct file *file)
|
||||||
|
{
|
||||||
|
return cap_bprm_creds_from_file(bprm, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline int security_bprm_check(struct linux_binprm *bprm)
|
static inline int security_bprm_check(struct linux_binprm *bprm)
|
||||||
|
|
|
@ -315,6 +315,9 @@ struct cred *prepare_exec_creds(void)
|
||||||
new->process_keyring = NULL;
|
new->process_keyring = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
new->suid = new->fsuid = new->euid;
|
||||||
|
new->sgid = new->fsgid = new->egid;
|
||||||
|
|
||||||
return new;
|
return new;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12220,7 +12220,7 @@ static void perf_event_exit_task_context(struct task_struct *child, int ctxn)
|
||||||
* When a child task exits, feed back event values to parent events.
|
* When a child task exits, feed back event values to parent events.
|
||||||
*
|
*
|
||||||
* Can be called with exec_update_mutex held when called from
|
* Can be called with exec_update_mutex held when called from
|
||||||
* install_exec_creds().
|
* setup_new_exec().
|
||||||
*/
|
*/
|
||||||
void perf_event_exit_task(struct task_struct *child)
|
void perf_event_exit_task(struct task_struct *child)
|
||||||
{
|
{
|
||||||
|
|
|
@ -854,14 +854,14 @@ static struct aa_label *handle_onexec(struct aa_label *label,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* apparmor_bprm_set_creds - set the new creds on the bprm struct
|
* apparmor_bprm_creds_for_exec - Update the new creds on the bprm struct
|
||||||
* @bprm: binprm for the exec (NOT NULL)
|
* @bprm: binprm for the exec (NOT NULL)
|
||||||
*
|
*
|
||||||
* Returns: %0 or error on failure
|
* Returns: %0 or error on failure
|
||||||
*
|
*
|
||||||
* TODO: once the other paths are done see if we can't refactor into a fn
|
* TODO: once the other paths are done see if we can't refactor into a fn
|
||||||
*/
|
*/
|
||||||
int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
struct aa_task_ctx *ctx;
|
struct aa_task_ctx *ctx;
|
||||||
struct aa_label *label, *new = NULL;
|
struct aa_label *label, *new = NULL;
|
||||||
|
@ -875,9 +875,6 @@ int apparmor_bprm_set_creds(struct linux_binprm *bprm)
|
||||||
file_inode(bprm->file)->i_mode
|
file_inode(bprm->file)->i_mode
|
||||||
};
|
};
|
||||||
|
|
||||||
if (bprm->called_set_creds)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
ctx = task_ctx(current);
|
ctx = task_ctx(current);
|
||||||
AA_BUG(!cred_label(bprm->cred));
|
AA_BUG(!cred_label(bprm->cred));
|
||||||
AA_BUG(!ctx);
|
AA_BUG(!ctx);
|
||||||
|
|
|
@ -30,7 +30,7 @@ struct aa_domain {
|
||||||
struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
|
struct aa_label *x_table_lookup(struct aa_profile *profile, u32 xindex,
|
||||||
const char **name);
|
const char **name);
|
||||||
|
|
||||||
int apparmor_bprm_set_creds(struct linux_binprm *bprm);
|
int apparmor_bprm_creds_for_exec(struct linux_binprm *bprm);
|
||||||
|
|
||||||
void aa_free_domain_entries(struct aa_domain *domain);
|
void aa_free_domain_entries(struct aa_domain *domain);
|
||||||
int aa_change_hat(const char *hats[], int count, u64 token, int flags);
|
int aa_change_hat(const char *hats[], int count, u64 token, int flags);
|
||||||
|
|
|
@ -1232,7 +1232,7 @@ static struct security_hook_list apparmor_hooks[] __lsm_ro_after_init = {
|
||||||
LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare),
|
LSM_HOOK_INIT(cred_prepare, apparmor_cred_prepare),
|
||||||
LSM_HOOK_INIT(cred_transfer, apparmor_cred_transfer),
|
LSM_HOOK_INIT(cred_transfer, apparmor_cred_transfer),
|
||||||
|
|
||||||
LSM_HOOK_INIT(bprm_set_creds, apparmor_bprm_set_creds),
|
LSM_HOOK_INIT(bprm_creds_for_exec, apparmor_bprm_creds_for_exec),
|
||||||
LSM_HOOK_INIT(bprm_committing_creds, apparmor_bprm_committing_creds),
|
LSM_HOOK_INIT(bprm_committing_creds, apparmor_bprm_committing_creds),
|
||||||
LSM_HOOK_INIT(bprm_committed_creds, apparmor_bprm_committed_creds),
|
LSM_HOOK_INIT(bprm_committed_creds, apparmor_bprm_committed_creds),
|
||||||
|
|
||||||
|
|
|
@ -647,7 +647,8 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
|
||||||
* its xattrs and, if present, apply them to the proposed credentials being
|
* its xattrs and, if present, apply them to the proposed credentials being
|
||||||
* constructed by execve().
|
* constructed by execve().
|
||||||
*/
|
*/
|
||||||
static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_fcap)
|
static int get_file_caps(struct linux_binprm *bprm, struct file *file,
|
||||||
|
bool *effective, bool *has_fcap)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
struct cpu_vfs_cap_data vcaps;
|
struct cpu_vfs_cap_data vcaps;
|
||||||
|
@ -657,7 +658,7 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_f
|
||||||
if (!file_caps_enabled)
|
if (!file_caps_enabled)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (!mnt_may_suid(bprm->file->f_path.mnt))
|
if (!mnt_may_suid(file->f_path.mnt))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -665,10 +666,10 @@ static int get_file_caps(struct linux_binprm *bprm, bool *effective, bool *has_f
|
||||||
* explicit that capability bits are limited to s_user_ns and its
|
* explicit that capability bits are limited to s_user_ns and its
|
||||||
* descendants.
|
* descendants.
|
||||||
*/
|
*/
|
||||||
if (!current_in_userns(bprm->file->f_path.mnt->mnt_sb->s_user_ns))
|
if (!current_in_userns(file->f_path.mnt->mnt_sb->s_user_ns))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
rc = get_vfs_caps_from_disk(bprm->file->f_path.dentry, &vcaps);
|
rc = get_vfs_caps_from_disk(file->f_path.dentry, &vcaps);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
if (rc == -EINVAL)
|
if (rc == -EINVAL)
|
||||||
printk(KERN_NOTICE "Invalid argument reading file caps for %s\n",
|
printk(KERN_NOTICE "Invalid argument reading file caps for %s\n",
|
||||||
|
@ -797,26 +798,27 @@ static inline bool nonroot_raised_pE(struct cred *new, const struct cred *old,
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cap_bprm_set_creds - Set up the proposed credentials for execve().
|
* cap_bprm_creds_from_file - Set up the proposed credentials for execve().
|
||||||
* @bprm: The execution parameters, including the proposed creds
|
* @bprm: The execution parameters, including the proposed creds
|
||||||
|
* @file: The file to pull the credentials from
|
||||||
*
|
*
|
||||||
* Set up the proposed credentials for a new execution context being
|
* Set up the proposed credentials for a new execution context being
|
||||||
* constructed by execve(). The proposed creds in @bprm->cred is altered,
|
* constructed by execve(). The proposed creds in @bprm->cred is altered,
|
||||||
* which won't take effect immediately. Returns 0 if successful, -ve on error.
|
* which won't take effect immediately. Returns 0 if successful, -ve on error.
|
||||||
*/
|
*/
|
||||||
int cap_bprm_set_creds(struct linux_binprm *bprm)
|
int cap_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file)
|
||||||
{
|
{
|
||||||
|
/* Process setpcap binaries and capabilities for uid 0 */
|
||||||
const struct cred *old = current_cred();
|
const struct cred *old = current_cred();
|
||||||
struct cred *new = bprm->cred;
|
struct cred *new = bprm->cred;
|
||||||
bool effective = false, has_fcap = false, is_setid;
|
bool effective = false, has_fcap = false, is_setid;
|
||||||
int ret;
|
int ret;
|
||||||
kuid_t root_uid;
|
kuid_t root_uid;
|
||||||
|
|
||||||
new->cap_ambient = old->cap_ambient;
|
|
||||||
if (WARN_ON(!cap_ambient_invariant_ok(old)))
|
if (WARN_ON(!cap_ambient_invariant_ok(old)))
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
ret = get_file_caps(bprm, &effective, &has_fcap);
|
ret = get_file_caps(bprm, file, &effective, &has_fcap);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
@ -885,12 +887,11 @@ int cap_bprm_set_creds(struct linux_binprm *bprm)
|
||||||
return -EPERM;
|
return -EPERM;
|
||||||
|
|
||||||
/* Check for privilege-elevated exec. */
|
/* Check for privilege-elevated exec. */
|
||||||
bprm->cap_elevated = 0;
|
|
||||||
if (is_setid ||
|
if (is_setid ||
|
||||||
(!__is_real(root_uid, new) &&
|
(!__is_real(root_uid, new) &&
|
||||||
(effective ||
|
(effective ||
|
||||||
__cap_grew(permitted, ambient, new))))
|
__cap_grew(permitted, ambient, new))))
|
||||||
bprm->cap_elevated = 1;
|
bprm->secureexec = 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1347,7 +1348,7 @@ static struct security_hook_list capability_hooks[] __lsm_ro_after_init = {
|
||||||
LSM_HOOK_INIT(ptrace_traceme, cap_ptrace_traceme),
|
LSM_HOOK_INIT(ptrace_traceme, cap_ptrace_traceme),
|
||||||
LSM_HOOK_INIT(capget, cap_capget),
|
LSM_HOOK_INIT(capget, cap_capget),
|
||||||
LSM_HOOK_INIT(capset, cap_capset),
|
LSM_HOOK_INIT(capset, cap_capset),
|
||||||
LSM_HOOK_INIT(bprm_set_creds, cap_bprm_set_creds),
|
LSM_HOOK_INIT(bprm_creds_from_file, cap_bprm_creds_from_file),
|
||||||
LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv),
|
LSM_HOOK_INIT(inode_need_killpriv, cap_inode_need_killpriv),
|
||||||
LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv),
|
LSM_HOOK_INIT(inode_killpriv, cap_inode_killpriv),
|
||||||
LSM_HOOK_INIT(inode_getsecurity, cap_inode_getsecurity),
|
LSM_HOOK_INIT(inode_getsecurity, cap_inode_getsecurity),
|
||||||
|
|
|
@ -823,9 +823,14 @@ int security_vm_enough_memory_mm(struct mm_struct *mm, long pages)
|
||||||
return __vm_enough_memory(mm, pages, cap_sys_admin);
|
return __vm_enough_memory(mm, pages, cap_sys_admin);
|
||||||
}
|
}
|
||||||
|
|
||||||
int security_bprm_set_creds(struct linux_binprm *bprm)
|
int security_bprm_creds_for_exec(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
return call_int_hook(bprm_set_creds, 0, bprm);
|
return call_int_hook(bprm_creds_for_exec, 0, bprm);
|
||||||
|
}
|
||||||
|
|
||||||
|
int security_bprm_creds_from_file(struct linux_binprm *bprm, struct file *file)
|
||||||
|
{
|
||||||
|
return call_int_hook(bprm_creds_from_file, 0, bprm, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
int security_bprm_check(struct linux_binprm *bprm)
|
int security_bprm_check(struct linux_binprm *bprm)
|
||||||
|
|
|
@ -2286,7 +2286,7 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm,
|
||||||
return -EACCES;
|
return -EACCES;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int selinux_bprm_set_creds(struct linux_binprm *bprm)
|
static int selinux_bprm_creds_for_exec(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
const struct task_security_struct *old_tsec;
|
const struct task_security_struct *old_tsec;
|
||||||
struct task_security_struct *new_tsec;
|
struct task_security_struct *new_tsec;
|
||||||
|
@ -2297,8 +2297,6 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
|
||||||
|
|
||||||
/* SELinux context only depends on initial program or script and not
|
/* SELinux context only depends on initial program or script and not
|
||||||
* the script interpreter */
|
* the script interpreter */
|
||||||
if (bprm->called_set_creds)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
old_tsec = selinux_cred(current_cred());
|
old_tsec = selinux_cred(current_cred());
|
||||||
new_tsec = selinux_cred(bprm->cred);
|
new_tsec = selinux_cred(bprm->cred);
|
||||||
|
@ -6405,7 +6403,7 @@ static int selinux_setprocattr(const char *name, void *value, size_t size)
|
||||||
/* Permission checking based on the specified context is
|
/* Permission checking based on the specified context is
|
||||||
performed during the actual operation (execve,
|
performed during the actual operation (execve,
|
||||||
open/mkdir/...), when we know the full context of the
|
open/mkdir/...), when we know the full context of the
|
||||||
operation. See selinux_bprm_set_creds for the execve
|
operation. See selinux_bprm_creds_for_exec for the execve
|
||||||
checks and may_create for the file creation checks. The
|
checks and may_create for the file creation checks. The
|
||||||
operation will then fail if the context is not permitted. */
|
operation will then fail if the context is not permitted. */
|
||||||
tsec = selinux_cred(new);
|
tsec = selinux_cred(new);
|
||||||
|
@ -6934,7 +6932,7 @@ static struct security_hook_list selinux_hooks[] __lsm_ro_after_init = {
|
||||||
|
|
||||||
LSM_HOOK_INIT(netlink_send, selinux_netlink_send),
|
LSM_HOOK_INIT(netlink_send, selinux_netlink_send),
|
||||||
|
|
||||||
LSM_HOOK_INIT(bprm_set_creds, selinux_bprm_set_creds),
|
LSM_HOOK_INIT(bprm_creds_for_exec, selinux_bprm_creds_for_exec),
|
||||||
LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds),
|
LSM_HOOK_INIT(bprm_committing_creds, selinux_bprm_committing_creds),
|
||||||
LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),
|
LSM_HOOK_INIT(bprm_committed_creds, selinux_bprm_committed_creds),
|
||||||
|
|
||||||
|
|
|
@ -887,12 +887,12 @@ static int smack_sb_statfs(struct dentry *dentry)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* smack_bprm_set_creds - set creds for exec
|
* smack_bprm_creds_for_exec - Update bprm->cred if needed for exec
|
||||||
* @bprm: the exec information
|
* @bprm: the exec information
|
||||||
*
|
*
|
||||||
* Returns 0 if it gets a blob, -EPERM if exec forbidden and -ENOMEM otherwise
|
* Returns 0 if it gets a blob, -EPERM if exec forbidden and -ENOMEM otherwise
|
||||||
*/
|
*/
|
||||||
static int smack_bprm_set_creds(struct linux_binprm *bprm)
|
static int smack_bprm_creds_for_exec(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
struct inode *inode = file_inode(bprm->file);
|
struct inode *inode = file_inode(bprm->file);
|
||||||
struct task_smack *bsp = smack_cred(bprm->cred);
|
struct task_smack *bsp = smack_cred(bprm->cred);
|
||||||
|
@ -900,9 +900,6 @@ static int smack_bprm_set_creds(struct linux_binprm *bprm)
|
||||||
struct superblock_smack *sbsp;
|
struct superblock_smack *sbsp;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
if (bprm->called_set_creds)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
isp = smack_inode(inode);
|
isp = smack_inode(inode);
|
||||||
if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
|
if (isp->smk_task == NULL || isp->smk_task == bsp->smk_task)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -4584,7 +4581,7 @@ static struct security_hook_list smack_hooks[] __lsm_ro_after_init = {
|
||||||
LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
|
LSM_HOOK_INIT(sb_statfs, smack_sb_statfs),
|
||||||
LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),
|
LSM_HOOK_INIT(sb_set_mnt_opts, smack_set_mnt_opts),
|
||||||
|
|
||||||
LSM_HOOK_INIT(bprm_set_creds, smack_bprm_set_creds),
|
LSM_HOOK_INIT(bprm_creds_for_exec, smack_bprm_creds_for_exec),
|
||||||
|
|
||||||
LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security),
|
LSM_HOOK_INIT(inode_alloc_security, smack_inode_alloc_security),
|
||||||
LSM_HOOK_INIT(inode_init_security, smack_inode_init_security),
|
LSM_HOOK_INIT(inode_init_security, smack_inode_init_security),
|
||||||
|
|
|
@ -63,20 +63,14 @@ static void tomoyo_bprm_committed_creds(struct linux_binprm *bprm)
|
||||||
|
|
||||||
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||||
/**
|
/**
|
||||||
* tomoyo_bprm_set_creds - Target for security_bprm_set_creds().
|
* tomoyo_bprm_for_exec - Target for security_bprm_creds_for_exec().
|
||||||
*
|
*
|
||||||
* @bprm: Pointer to "struct linux_binprm".
|
* @bprm: Pointer to "struct linux_binprm".
|
||||||
*
|
*
|
||||||
* Returns 0.
|
* Returns 0.
|
||||||
*/
|
*/
|
||||||
static int tomoyo_bprm_set_creds(struct linux_binprm *bprm)
|
static int tomoyo_bprm_creds_for_exec(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
/*
|
|
||||||
* Do only if this function is called for the first time of an execve
|
|
||||||
* operation.
|
|
||||||
*/
|
|
||||||
if (bprm->called_set_creds)
|
|
||||||
return 0;
|
|
||||||
/*
|
/*
|
||||||
* Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
|
* Load policy if /sbin/tomoyo-init exists and /sbin/init is requested
|
||||||
* for the first time.
|
* for the first time.
|
||||||
|
@ -539,7 +533,7 @@ static struct security_hook_list tomoyo_hooks[] __lsm_ro_after_init = {
|
||||||
LSM_HOOK_INIT(task_alloc, tomoyo_task_alloc),
|
LSM_HOOK_INIT(task_alloc, tomoyo_task_alloc),
|
||||||
LSM_HOOK_INIT(task_free, tomoyo_task_free),
|
LSM_HOOK_INIT(task_free, tomoyo_task_free),
|
||||||
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
#ifndef CONFIG_SECURITY_TOMOYO_OMIT_USERSPACE_LOADER
|
||||||
LSM_HOOK_INIT(bprm_set_creds, tomoyo_bprm_set_creds),
|
LSM_HOOK_INIT(bprm_creds_for_exec, tomoyo_bprm_creds_for_exec),
|
||||||
#endif
|
#endif
|
||||||
LSM_HOOK_INIT(bprm_check_security, tomoyo_bprm_check_security),
|
LSM_HOOK_INIT(bprm_check_security, tomoyo_bprm_check_security),
|
||||||
LSM_HOOK_INIT(file_fcntl, tomoyo_file_fcntl),
|
LSM_HOOK_INIT(file_fcntl, tomoyo_file_fcntl),
|
||||||
|
|
|
@ -3,6 +3,7 @@ CFLAGS = -Wall
|
||||||
CFLAGS += -Wno-nonnull
|
CFLAGS += -Wno-nonnull
|
||||||
CFLAGS += -D_GNU_SOURCE
|
CFLAGS += -D_GNU_SOURCE
|
||||||
|
|
||||||
|
TEST_PROGS := binfmt_script
|
||||||
TEST_GEN_PROGS := execveat
|
TEST_GEN_PROGS := execveat
|
||||||
TEST_GEN_FILES := execveat.symlink execveat.denatured script subdir
|
TEST_GEN_FILES := execveat.symlink execveat.denatured script subdir
|
||||||
# Makefile is a run-time dependency, since it's accessed by the execveat test
|
# Makefile is a run-time dependency, since it's accessed by the execveat test
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
# SPDX-License-Identifier: GPL-2.0
|
||||||
|
#
|
||||||
|
# Test that truncation of bprm->buf doesn't cause unexpected execs paths, along
|
||||||
|
# with various other pathological cases.
|
||||||
|
import os, subprocess
|
||||||
|
|
||||||
|
# Relevant commits
|
||||||
|
#
|
||||||
|
# b5372fe5dc84 ("exec: load_script: Do not exec truncated interpreter path")
|
||||||
|
# 6eb3c3d0a52d ("exec: increase BINPRM_BUF_SIZE to 256")
|
||||||
|
|
||||||
|
# BINPRM_BUF_SIZE
|
||||||
|
SIZE=256
|
||||||
|
|
||||||
|
NAME_MAX=int(subprocess.check_output(["getconf", "NAME_MAX", "."]))
|
||||||
|
|
||||||
|
test_num=0
|
||||||
|
|
||||||
|
code='''#!/usr/bin/perl
|
||||||
|
print "Executed interpreter! Args:\n";
|
||||||
|
print "0 : '$0'\n";
|
||||||
|
$counter = 1;
|
||||||
|
foreach my $a (@ARGV) {
|
||||||
|
print "$counter : '$a'\n";
|
||||||
|
$counter++;
|
||||||
|
}
|
||||||
|
'''
|
||||||
|
|
||||||
|
##
|
||||||
|
# test - produce a binfmt_script hashbang line for testing
|
||||||
|
#
|
||||||
|
# @size: bytes for bprm->buf line, including hashbang but not newline
|
||||||
|
# @good: whether this script is expected to execute correctly
|
||||||
|
# @hashbang: the special 2 bytes for running binfmt_script
|
||||||
|
# @leading: any leading whitespace before the executable path
|
||||||
|
# @root: start of executable pathname
|
||||||
|
# @target: end of executable pathname
|
||||||
|
# @arg: bytes following the executable pathname
|
||||||
|
# @fill: character to fill between @root and @target to reach @size bytes
|
||||||
|
# @newline: character to use as newline, not counted towards @size
|
||||||
|
# ...
|
||||||
|
def test(name, size, good=True, leading="", root="./", target="/perl",
|
||||||
|
fill="A", arg="", newline="\n", hashbang="#!"):
|
||||||
|
global test_num, tests, NAME_MAX
|
||||||
|
test_num += 1
|
||||||
|
if test_num > tests:
|
||||||
|
raise ValueError("more binfmt_script tests than expected! (want %d, expected %d)"
|
||||||
|
% (test_num, tests))
|
||||||
|
|
||||||
|
middle = ""
|
||||||
|
remaining = size - len(hashbang) - len(leading) - len(root) - len(target) - len(arg)
|
||||||
|
# The middle of the pathname must not exceed NAME_MAX
|
||||||
|
while remaining >= NAME_MAX:
|
||||||
|
middle += fill * (NAME_MAX - 1)
|
||||||
|
middle += '/'
|
||||||
|
remaining -= NAME_MAX
|
||||||
|
middle += fill * remaining
|
||||||
|
|
||||||
|
dirpath = root + middle
|
||||||
|
binary = dirpath + target
|
||||||
|
if len(target):
|
||||||
|
os.makedirs(dirpath, mode=0o755, exist_ok=True)
|
||||||
|
open(binary, "w").write(code)
|
||||||
|
os.chmod(binary, 0o755)
|
||||||
|
|
||||||
|
buf=hashbang + leading + root + middle + target + arg + newline
|
||||||
|
if len(newline) > 0:
|
||||||
|
buf += 'echo this is not really perl\n'
|
||||||
|
|
||||||
|
script = "binfmt_script-%s" % (name)
|
||||||
|
open(script, "w").write(buf)
|
||||||
|
os.chmod(script, 0o755)
|
||||||
|
|
||||||
|
proc = subprocess.Popen(["./%s" % (script)], shell=True,
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
|
stdout = proc.communicate()[0]
|
||||||
|
|
||||||
|
if proc.returncode == 0 and b'Executed interpreter' in stdout:
|
||||||
|
if good:
|
||||||
|
print("ok %d - binfmt_script %s (successful good exec)"
|
||||||
|
% (test_num, name))
|
||||||
|
else:
|
||||||
|
print("not ok %d - binfmt_script %s succeeded when it should have failed"
|
||||||
|
% (test_num, name))
|
||||||
|
else:
|
||||||
|
if good:
|
||||||
|
print("not ok %d - binfmt_script %s failed when it should have succeeded (rc:%d)"
|
||||||
|
% (test_num, name, proc.returncode))
|
||||||
|
else:
|
||||||
|
print("ok %d - binfmt_script %s (correctly failed bad exec)"
|
||||||
|
% (test_num, name))
|
||||||
|
|
||||||
|
# Clean up crazy binaries
|
||||||
|
os.unlink(script)
|
||||||
|
if len(target):
|
||||||
|
elements = binary.split('/')
|
||||||
|
os.unlink(binary)
|
||||||
|
elements.pop()
|
||||||
|
while len(elements) > 1:
|
||||||
|
os.rmdir("/".join(elements))
|
||||||
|
elements.pop()
|
||||||
|
|
||||||
|
tests=27
|
||||||
|
print("TAP version 1.3")
|
||||||
|
print("1..%d" % (tests))
|
||||||
|
|
||||||
|
### FAIL (8 tests)
|
||||||
|
|
||||||
|
# Entire path is well past the BINFMT_BUF_SIZE.
|
||||||
|
test(name="too-big", size=SIZE+80, good=False)
|
||||||
|
# Path is right at max size, making it impossible to tell if it was truncated.
|
||||||
|
test(name="exact", size=SIZE, good=False)
|
||||||
|
# Same as above, but with leading whitespace.
|
||||||
|
test(name="exact-space", size=SIZE, good=False, leading=" ")
|
||||||
|
# Huge buffer of only whitespace.
|
||||||
|
test(name="whitespace-too-big", size=SIZE+71, good=False, root="",
|
||||||
|
fill=" ", target="")
|
||||||
|
# A good path, but it gets truncated due to leading whitespace.
|
||||||
|
test(name="truncated", size=SIZE+17, good=False, leading=" " * 19)
|
||||||
|
# Entirely empty except for #!
|
||||||
|
test(name="empty", size=2, good=False, root="",
|
||||||
|
fill="", target="", newline="")
|
||||||
|
# Within size, but entirely spaces
|
||||||
|
test(name="spaces", size=SIZE-1, good=False, root="", fill=" ",
|
||||||
|
target="", newline="")
|
||||||
|
# Newline before binary.
|
||||||
|
test(name="newline-prefix", size=SIZE-1, good=False, leading="\n",
|
||||||
|
root="", fill=" ", target="")
|
||||||
|
|
||||||
|
### ok (19 tests)
|
||||||
|
|
||||||
|
# The original test case that was broken by commit:
|
||||||
|
# 8099b047ecc4 ("exec: load_script: don't blindly truncate shebang string")
|
||||||
|
test(name="test.pl", size=439, leading=" ",
|
||||||
|
root="./nix/store/bwav8kz8b3y471wjsybgzw84mrh4js9-perl-5.28.1/bin",
|
||||||
|
arg=" -I/nix/store/x6yyav38jgr924nkna62q3pkp0dgmzlx-perl5.28.1-File-Slurp-9999.25/lib/perl5/site_perl -I/nix/store/ha8v67sl8dac92r9z07vzr4gv1y9nwqz-perl5.28.1-Net-DBus-1.1.0/lib/perl5/site_perl -I/nix/store/dcrkvnjmwh69ljsvpbdjjdnqgwx90a9d-perl5.28.1-XML-Parser-2.44/lib/perl5/site_perl -I/nix/store/rmji88k2zz7h4zg97385bygcydrf2q8h-perl5.28.1-XML-Twig-3.52/lib/perl5/site_perl")
|
||||||
|
# One byte under size, leaving newline visible.
|
||||||
|
test(name="one-under", size=SIZE-1)
|
||||||
|
# Two bytes under size, leaving newline visible.
|
||||||
|
test(name="two-under", size=SIZE-2)
|
||||||
|
# Exact size, but trailing whitespace visible instead of newline
|
||||||
|
test(name="exact-trunc-whitespace", size=SIZE, arg=" ")
|
||||||
|
# Exact size, but trailing space and first arg char visible instead of newline.
|
||||||
|
test(name="exact-trunc-arg", size=SIZE, arg=" f")
|
||||||
|
# One bute under, with confirmed non-truncated arg since newline now visible.
|
||||||
|
test(name="one-under-full-arg", size=SIZE-1, arg=" f")
|
||||||
|
# Short read buffer by one byte.
|
||||||
|
test(name="one-under-no-nl", size=SIZE-1, newline="")
|
||||||
|
# Short read buffer by half buffer size.
|
||||||
|
test(name="half-under-no-nl", size=int(SIZE/2), newline="")
|
||||||
|
# One byte under with whitespace arg. leaving wenline visible.
|
||||||
|
test(name="one-under-trunc-arg", size=SIZE-1, arg=" ")
|
||||||
|
# One byte under with whitespace leading. leaving wenline visible.
|
||||||
|
test(name="one-under-leading", size=SIZE-1, leading=" ")
|
||||||
|
# One byte under with whitespace leading and as arg. leaving newline visible.
|
||||||
|
test(name="one-under-leading-trunc-arg", size=SIZE-1, leading=" ", arg=" ")
|
||||||
|
# Same as above, but with 2 bytes under
|
||||||
|
test(name="two-under-no-nl", size=SIZE-2, newline="")
|
||||||
|
test(name="two-under-trunc-arg", size=SIZE-2, arg=" ")
|
||||||
|
test(name="two-under-leading", size=SIZE-2, leading=" ")
|
||||||
|
test(name="two-under-leading-trunc-arg", size=SIZE-2, leading=" ", arg=" ")
|
||||||
|
# Same as above, but with buffer half filled
|
||||||
|
test(name="two-under-no-nl", size=int(SIZE/2), newline="")
|
||||||
|
test(name="two-under-trunc-arg", size=int(SIZE/2), arg=" ")
|
||||||
|
test(name="two-under-leading", size=int(SIZE/2), leading=" ")
|
||||||
|
test(name="two-under-lead-trunc-arg", size=int(SIZE/2), leading=" ", arg=" ")
|
||||||
|
|
||||||
|
if test_num != tests:
|
||||||
|
raise ValueError("fewer binfmt_script tests than expected! (ran %d, expected %d"
|
||||||
|
% (test_num, tests))
|
Loading…
Reference in New Issue