mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2024-12-28 00:32:00 +00:00
Merge branch 'for-next/topic/execve/core' into for-next/execve
This commit is contained in:
commit
c7c1167fcb
@ -8551,8 +8551,8 @@ F: rust/kernel/net/phy.rs
|
||||
F: rust/kernel/net/phy/reg.rs
|
||||
|
||||
EXEC & BINFMT API, ELF
|
||||
M: Kees Cook <kees@kernel.org>
|
||||
R: Eric Biederman <ebiederm@xmission.com>
|
||||
R: Kees Cook <kees@kernel.org>
|
||||
L: linux-mm@kvack.org
|
||||
S: Supported
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/execve
|
||||
@ -8564,6 +8564,7 @@ F: fs/tests/binfmt_*_kunit.c
|
||||
F: fs/tests/exec_kunit.c
|
||||
F: include/linux/binfmts.h
|
||||
F: include/linux/elf.h
|
||||
F: include/uapi/linux/auxvec.h
|
||||
F: include/uapi/linux/binfmts.h
|
||||
F: include/uapi/linux/elf.h
|
||||
F: tools/testing/selftests/exec/
|
||||
|
@ -1001,7 +1001,7 @@ static int bm_fill_super(struct super_block *sb, struct fs_context *fc)
|
||||
/*
|
||||
* If it turns out that most user namespaces actually want to
|
||||
* register their own binary type handler and therefore all
|
||||
* create their own separate binfm_misc mounts we should
|
||||
* create their own separate binfmt_misc mounts we should
|
||||
* consider turning this into a kmem cache.
|
||||
*/
|
||||
misc = kzalloc(sizeof(struct binfmt_misc), GFP_KERNEL);
|
||||
|
63
fs/exec.c
63
fs/exec.c
@ -1195,16 +1195,16 @@ static int unshare_sighand(struct task_struct *me)
|
||||
}
|
||||
|
||||
/*
|
||||
* These functions flushes out all traces of the currently running executable
|
||||
* so that a new one can be started
|
||||
* This is unlocked -- the string will always be NUL-terminated, but
|
||||
* may show overlapping contents if racing concurrent reads.
|
||||
*/
|
||||
|
||||
void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec)
|
||||
{
|
||||
task_lock(tsk);
|
||||
size_t len = min(strlen(buf), sizeof(tsk->comm) - 1);
|
||||
|
||||
trace_task_rename(tsk, buf);
|
||||
strscpy_pad(tsk->comm, buf, sizeof(tsk->comm));
|
||||
task_unlock(tsk);
|
||||
memcpy(tsk->comm, buf, len);
|
||||
memset(&tsk->comm[len], 0, sizeof(tsk->comm) - len);
|
||||
perf_event_comm(tsk, exec);
|
||||
}
|
||||
|
||||
@ -1342,7 +1342,28 @@ int begin_new_exec(struct linux_binprm * bprm)
|
||||
set_dumpable(current->mm, SUID_DUMP_USER);
|
||||
|
||||
perf_event_exec();
|
||||
__set_task_comm(me, kbasename(bprm->filename), true);
|
||||
|
||||
/*
|
||||
* If the original filename was empty, alloc_bprm() made up a path
|
||||
* that will probably not be useful to admins running ps or similar.
|
||||
* Let's fix it up to be something reasonable.
|
||||
*/
|
||||
if (bprm->comm_from_dentry) {
|
||||
/*
|
||||
* Hold RCU lock to keep the name from being freed behind our back.
|
||||
* Use acquire semantics to make sure the terminating NUL from
|
||||
* __d_alloc() is seen.
|
||||
*
|
||||
* Note, we're deliberately sloppy here. We don't need to care about
|
||||
* detecting a concurrent rename and just want a terminated name.
|
||||
*/
|
||||
rcu_read_lock();
|
||||
__set_task_comm(me, smp_load_acquire(&bprm->file->f_path.dentry->d_name.name),
|
||||
true);
|
||||
rcu_read_unlock();
|
||||
} else {
|
||||
__set_task_comm(me, kbasename(bprm->filename), true);
|
||||
}
|
||||
|
||||
/* An exec changes our domain. We are no longer part of the thread
|
||||
group */
|
||||
@ -1518,11 +1539,13 @@ static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int fl
|
||||
if (fd == AT_FDCWD || filename->name[0] == '/') {
|
||||
bprm->filename = filename->name;
|
||||
} else {
|
||||
if (filename->name[0] == '\0')
|
||||
if (filename->name[0] == '\0') {
|
||||
bprm->fdpath = kasprintf(GFP_KERNEL, "/dev/fd/%d", fd);
|
||||
else
|
||||
bprm->comm_from_dentry = 1;
|
||||
} else {
|
||||
bprm->fdpath = kasprintf(GFP_KERNEL, "/dev/fd/%d/%s",
|
||||
fd, filename->name);
|
||||
}
|
||||
if (!bprm->fdpath)
|
||||
goto out_free;
|
||||
|
||||
@ -1735,13 +1758,11 @@ int remove_arg_zero(struct linux_binprm *bprm)
|
||||
}
|
||||
EXPORT_SYMBOL(remove_arg_zero);
|
||||
|
||||
#define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e))
|
||||
/*
|
||||
* cycle the list of binary formats handler, until one recognizes the image
|
||||
*/
|
||||
static int search_binary_handler(struct linux_binprm *bprm)
|
||||
{
|
||||
bool need_retry = IS_ENABLED(CONFIG_MODULES);
|
||||
struct linux_binfmt *fmt;
|
||||
int retval;
|
||||
|
||||
@ -1753,8 +1774,6 @@ static int search_binary_handler(struct linux_binprm *bprm)
|
||||
if (retval)
|
||||
return retval;
|
||||
|
||||
retval = -ENOENT;
|
||||
retry:
|
||||
read_lock(&binfmt_lock);
|
||||
list_for_each_entry(fmt, &formats, lh) {
|
||||
if (!try_module_get(fmt->module))
|
||||
@ -1772,17 +1791,7 @@ static int search_binary_handler(struct linux_binprm *bprm)
|
||||
}
|
||||
read_unlock(&binfmt_lock);
|
||||
|
||||
if (need_retry) {
|
||||
if (printable(bprm->buf[0]) && printable(bprm->buf[1]) &&
|
||||
printable(bprm->buf[2]) && printable(bprm->buf[3]))
|
||||
return retval;
|
||||
if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0)
|
||||
return retval;
|
||||
need_retry = false;
|
||||
goto retry;
|
||||
}
|
||||
|
||||
return retval;
|
||||
return -ENOEXEC;
|
||||
}
|
||||
|
||||
/* binfmt handlers will call back into begin_new_exec() on success. */
|
||||
@ -1920,9 +1929,6 @@ static int do_execveat_common(int fd, struct filename *filename,
|
||||
}
|
||||
|
||||
retval = count(argv, MAX_ARG_STRINGS);
|
||||
if (retval == 0)
|
||||
pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
|
||||
current->comm, bprm->filename);
|
||||
if (retval < 0)
|
||||
goto out_free;
|
||||
bprm->argc = retval;
|
||||
@ -1960,6 +1966,9 @@ static int do_execveat_common(int fd, struct filename *filename,
|
||||
if (retval < 0)
|
||||
goto out_free;
|
||||
bprm->argc = 1;
|
||||
|
||||
pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
|
||||
current->comm, bprm->filename);
|
||||
}
|
||||
|
||||
retval = bprm_execve(bprm);
|
||||
|
@ -47,7 +47,9 @@ struct linux_binprm {
|
||||
* Set by user space to check executability according to the
|
||||
* caller's environment.
|
||||
*/
|
||||
is_check:1;
|
||||
is_check:1,
|
||||
/* Set when "comm" must come from the dentry. */
|
||||
comm_from_dentry:1;
|
||||
struct file *executable; /* Executable to pass to the interpreter */
|
||||
struct file *interpreter;
|
||||
struct file *file;
|
||||
|
@ -52,8 +52,8 @@ extern void do_coredump(const kernel_siginfo_t *siginfo);
|
||||
#define __COREDUMP_PRINTK(Level, Format, ...) \
|
||||
do { \
|
||||
char comm[TASK_COMM_LEN]; \
|
||||
\
|
||||
get_task_comm(comm, current); \
|
||||
/* This will always be NUL terminated. */ \
|
||||
memcpy(comm, current->comm, sizeof(comm)); \
|
||||
printk_ratelimited(Level "coredump: %d(%*pE): " Format "\n", \
|
||||
task_tgid_vnr(current), (int)strlen(comm), comm, ##__VA_ARGS__); \
|
||||
} while (0) \
|
||||
|
@ -1936,11 +1936,10 @@ static inline void kick_process(struct task_struct *tsk) { }
|
||||
#endif
|
||||
|
||||
extern void __set_task_comm(struct task_struct *tsk, const char *from, bool exec);
|
||||
|
||||
static inline void set_task_comm(struct task_struct *tsk, const char *from)
|
||||
{
|
||||
__set_task_comm(tsk, from, false);
|
||||
}
|
||||
#define set_task_comm(tsk, from) ({ \
|
||||
BUILD_BUG_ON(sizeof(from) != TASK_COMM_LEN); \
|
||||
__set_task_comm(tsk, from, false); \
|
||||
})
|
||||
|
||||
/*
|
||||
* - Why not use task_lock()?
|
||||
|
@ -634,7 +634,7 @@ static int io_wq_worker(void *data)
|
||||
struct io_wq_acct *acct = io_wq_get_acct(worker);
|
||||
struct io_wq *wq = worker->wq;
|
||||
bool exit_mask = false, last_timeout = false;
|
||||
char buf[TASK_COMM_LEN];
|
||||
char buf[TASK_COMM_LEN] = {};
|
||||
|
||||
set_mask_bits(&worker->flags, 0,
|
||||
BIT(IO_WORKER_F_UP) | BIT(IO_WORKER_F_RUNNING));
|
||||
|
@ -264,7 +264,7 @@ static int io_sq_thread(void *data)
|
||||
struct io_ring_ctx *ctx;
|
||||
struct rusage start;
|
||||
unsigned long timeout = 0;
|
||||
char buf[TASK_COMM_LEN];
|
||||
char buf[TASK_COMM_LEN] = {};
|
||||
DEFINE_WAIT(wait);
|
||||
|
||||
/* offload context creation failed, just exit */
|
||||
|
@ -738,10 +738,11 @@ EXPORT_SYMBOL(kthread_stop_put);
|
||||
|
||||
int kthreadd(void *unused)
|
||||
{
|
||||
static const char comm[TASK_COMM_LEN] = "kthreadd";
|
||||
struct task_struct *tsk = current;
|
||||
|
||||
/* Setup a clean context for our children to inherit. */
|
||||
set_task_comm(tsk, "kthreadd");
|
||||
set_task_comm(tsk, comm);
|
||||
ignore_signals(tsk);
|
||||
set_cpus_allowed_ptr(tsk, housekeeping_cpumask(HK_TYPE_KTHREAD));
|
||||
set_mems_allowed(node_states[N_MEMORY]);
|
||||
|
@ -23,9 +23,11 @@
|
||||
|
||||
#include "../kselftest.h"
|
||||
|
||||
#define TESTS_EXPECTED 51
|
||||
#define TESTS_EXPECTED 54
|
||||
#define TEST_NAME_LEN (PATH_MAX * 4)
|
||||
|
||||
#define CHECK_COMM "CHECK_COMM"
|
||||
|
||||
static char longpath[2 * PATH_MAX] = "";
|
||||
static char *envp[] = { "IN_TEST=yes", NULL, NULL };
|
||||
static char *argv[] = { "execveat", "99", NULL };
|
||||
@ -237,6 +239,29 @@ static int check_execveat_pathmax(int root_dfd, const char *src, int is_script)
|
||||
return fail;
|
||||
}
|
||||
|
||||
static int check_execveat_comm(int fd, char *argv0, char *expected)
|
||||
{
|
||||
char buf[128], *old_env, *old_argv0;
|
||||
int ret;
|
||||
|
||||
snprintf(buf, sizeof(buf), CHECK_COMM "=%s", expected);
|
||||
|
||||
old_env = envp[1];
|
||||
envp[1] = buf;
|
||||
|
||||
old_argv0 = argv[0];
|
||||
argv[0] = argv0;
|
||||
|
||||
ksft_print_msg("Check execveat(AT_EMPTY_PATH)'s comm is %s\n",
|
||||
expected);
|
||||
ret = check_execveat_invoked_rc(fd, "", AT_EMPTY_PATH, 0, 0);
|
||||
|
||||
envp[1] = old_env;
|
||||
argv[0] = old_argv0;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int run_tests(void)
|
||||
{
|
||||
int fail = 0;
|
||||
@ -389,6 +414,14 @@ static int run_tests(void)
|
||||
|
||||
fail += check_execveat_pathmax(root_dfd, "execveat", 0);
|
||||
fail += check_execveat_pathmax(root_dfd, "script", 1);
|
||||
|
||||
/* /proc/pid/comm gives filename by default */
|
||||
fail += check_execveat_comm(fd, "sentinel", "execveat");
|
||||
/* /proc/pid/comm gives argv[0] when invoked via link */
|
||||
fail += check_execveat_comm(fd_symlink, "sentinel", "execveat");
|
||||
/* /proc/pid/comm gives filename if NULL is passed */
|
||||
fail += check_execveat_comm(fd, NULL, "execveat");
|
||||
|
||||
return fail;
|
||||
}
|
||||
|
||||
@ -415,9 +448,13 @@ int main(int argc, char **argv)
|
||||
int ii;
|
||||
int rc;
|
||||
const char *verbose = getenv("VERBOSE");
|
||||
const char *check_comm = getenv(CHECK_COMM);
|
||||
|
||||
if (argc >= 2) {
|
||||
/* If we are invoked with an argument, don't run tests. */
|
||||
if (argc >= 2 || check_comm) {
|
||||
/*
|
||||
* If we are invoked with an argument, or no arguments but a
|
||||
* command to check, don't run tests.
|
||||
*/
|
||||
const char *in_test = getenv("IN_TEST");
|
||||
|
||||
if (verbose) {
|
||||
@ -426,6 +463,38 @@ int main(int argc, char **argv)
|
||||
ksft_print_msg("\t[%d]='%s\n'", ii, argv[ii]);
|
||||
}
|
||||
|
||||
/* If the tests wanted us to check the command, do so. */
|
||||
if (check_comm) {
|
||||
/* TASK_COMM_LEN == 16 */
|
||||
char buf[32];
|
||||
int fd, ret;
|
||||
|
||||
fd = open("/proc/self/comm", O_RDONLY);
|
||||
if (fd < 0) {
|
||||
ksft_perror("open() comm failed");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ret = read(fd, buf, sizeof(buf));
|
||||
if (ret < 0) {
|
||||
ksft_perror("read() comm failed");
|
||||
close(fd);
|
||||
exit(1);
|
||||
}
|
||||
close(fd);
|
||||
|
||||
// trim off the \n
|
||||
buf[ret-1] = 0;
|
||||
|
||||
if (strcmp(buf, check_comm)) {
|
||||
ksft_print_msg("bad comm, got: %s expected: %s\n",
|
||||
buf, check_comm);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
/* Check expected environment transferred. */
|
||||
if (!in_test || strcmp(in_test, "yes") != 0) {
|
||||
ksft_print_msg("no IN_TEST=yes in env\n");
|
||||
|
Loading…
Reference in New Issue
Block a user