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

Configure Feed

Select the types of activity you want to include in your feed.

Merge tag 'execve-v6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux

Pull execve updates from Kees Cook:

- fix up /proc/pid/comm in the execveat(AT_EMPTY_PATH) case (Tycho
Andersen, Kees Cook)

- binfmt_misc: Fix comment typos (Christophe JAILLET)

- move empty argv[0] warning closer to actual logic (Nir Lichtman)

- remove legacy custom binfmt modules autoloading (Nir Lichtman)

- Make sure set_task_comm() always NUL-terminates

- binfmt_flat: Fix integer overflow bug on 32 bit systems (Dan
Carpenter)

- coredump: Do not lock when copying "comm"

- MAINTAINERS: add auxvec.h and set myself as maintainer

* tag 'execve-v6.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
binfmt_flat: Fix integer overflow bug on 32 bit systems
selftests/exec: add a test for execveat()'s comm
exec: fix up /proc/pid/comm in the execveat(AT_EMPTY_PATH) case
exec: Make sure task->comm is always NUL-terminated
exec: remove legacy custom binfmt modules autoloading
exec: move warning of null argv to be next to the relevant code
fs: binfmt: Fix a typo
MAINTAINERS: exec: Mark Kees as maintainer
MAINTAINERS: exec: Add auxvec.h UAPI
coredump: Do not lock during 'comm' reporting

+125 -44
+2 -1
MAINTAINERS
··· 8548 8548 F: rust/kernel/net/phy/reg.rs 8549 8549 8550 8550 EXEC & BINFMT API, ELF 8551 + M: Kees Cook <kees@kernel.org> 8551 8552 R: Eric Biederman <ebiederm@xmission.com> 8552 - R: Kees Cook <kees@kernel.org> 8553 8553 L: linux-mm@kvack.org 8554 8554 S: Supported 8555 8555 T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/execve ··· 8561 8561 F: fs/tests/exec_kunit.c 8562 8562 F: include/linux/binfmts.h 8563 8563 F: include/linux/elf.h 8564 + F: include/uapi/linux/auxvec.h 8564 8565 F: include/uapi/linux/binfmts.h 8565 8566 F: include/uapi/linux/elf.h 8566 8567 F: tools/testing/selftests/exec/
+1 -1
fs/binfmt_flat.c
··· 478 478 * 28 bits (256 MB) is way more than reasonable in this case. 479 479 * If some top bits are set we have probable binary corruption. 480 480 */ 481 - if ((text_len | data_len | bss_len | stack_len | full_data) >> 28) { 481 + if ((text_len | data_len | bss_len | stack_len | relocs | full_data) >> 28) { 482 482 pr_err("bad header\n"); 483 483 ret = -ENOEXEC; 484 484 goto err;
+1 -1
fs/binfmt_misc.c
··· 1001 1001 /* 1002 1002 * If it turns out that most user namespaces actually want to 1003 1003 * register their own binary type handler and therefore all 1004 - * create their own separate binfm_misc mounts we should 1004 + * create their own separate binfmt_misc mounts we should 1005 1005 * consider turning this into a kmem cache. 1006 1006 */ 1007 1007 misc = kzalloc(sizeof(struct binfmt_misc), GFP_KERNEL);
+36 -27
fs/exec.c
··· 1194 1194 } 1195 1195 1196 1196 /* 1197 - * These functions flushes out all traces of the currently running executable 1198 - * so that a new one can be started 1197 + * This is unlocked -- the string will always be NUL-terminated, but 1198 + * may show overlapping contents if racing concurrent reads. 1199 1199 */ 1200 - 1201 1200 void __set_task_comm(struct task_struct *tsk, const char *buf, bool exec) 1202 1201 { 1203 - task_lock(tsk); 1202 + size_t len = min(strlen(buf), sizeof(tsk->comm) - 1); 1203 + 1204 1204 trace_task_rename(tsk, buf); 1205 - strscpy_pad(tsk->comm, buf, sizeof(tsk->comm)); 1206 - task_unlock(tsk); 1205 + memcpy(tsk->comm, buf, len); 1206 + memset(&tsk->comm[len], 0, sizeof(tsk->comm) - len); 1207 1207 perf_event_comm(tsk, exec); 1208 1208 } 1209 1209 ··· 1341 1341 set_dumpable(current->mm, SUID_DUMP_USER); 1342 1342 1343 1343 perf_event_exec(); 1344 - __set_task_comm(me, kbasename(bprm->filename), true); 1344 + 1345 + /* 1346 + * If the original filename was empty, alloc_bprm() made up a path 1347 + * that will probably not be useful to admins running ps or similar. 1348 + * Let's fix it up to be something reasonable. 1349 + */ 1350 + if (bprm->comm_from_dentry) { 1351 + /* 1352 + * Hold RCU lock to keep the name from being freed behind our back. 1353 + * Use acquire semantics to make sure the terminating NUL from 1354 + * __d_alloc() is seen. 1355 + * 1356 + * Note, we're deliberately sloppy here. We don't need to care about 1357 + * detecting a concurrent rename and just want a terminated name. 1358 + */ 1359 + rcu_read_lock(); 1360 + __set_task_comm(me, smp_load_acquire(&bprm->file->f_path.dentry->d_name.name), 1361 + true); 1362 + rcu_read_unlock(); 1363 + } else { 1364 + __set_task_comm(me, kbasename(bprm->filename), true); 1365 + } 1345 1366 1346 1367 /* An exec changes our domain. We are no longer part of the thread 1347 1368 group */ ··· 1538 1517 if (fd == AT_FDCWD || filename->name[0] == '/') { 1539 1518 bprm->filename = filename->name; 1540 1519 } else { 1541 - if (filename->name[0] == '\0') 1520 + if (filename->name[0] == '\0') { 1542 1521 bprm->fdpath = kasprintf(GFP_KERNEL, "/dev/fd/%d", fd); 1543 - else 1522 + bprm->comm_from_dentry = 1; 1523 + } else { 1544 1524 bprm->fdpath = kasprintf(GFP_KERNEL, "/dev/fd/%d/%s", 1545 1525 fd, filename->name); 1526 + } 1546 1527 if (!bprm->fdpath) 1547 1528 goto out_free; 1548 1529 ··· 1742 1719 } 1743 1720 EXPORT_SYMBOL(remove_arg_zero); 1744 1721 1745 - #define printable(c) (((c)=='\t') || ((c)=='\n') || (0x20<=(c) && (c)<=0x7e)) 1746 1722 /* 1747 1723 * cycle the list of binary formats handler, until one recognizes the image 1748 1724 */ 1749 1725 static int search_binary_handler(struct linux_binprm *bprm) 1750 1726 { 1751 - bool need_retry = IS_ENABLED(CONFIG_MODULES); 1752 1727 struct linux_binfmt *fmt; 1753 1728 int retval; 1754 1729 ··· 1758 1737 if (retval) 1759 1738 return retval; 1760 1739 1761 - retval = -ENOENT; 1762 - retry: 1763 1740 read_lock(&binfmt_lock); 1764 1741 list_for_each_entry(fmt, &formats, lh) { 1765 1742 if (!try_module_get(fmt->module)) ··· 1775 1756 } 1776 1757 read_unlock(&binfmt_lock); 1777 1758 1778 - if (need_retry) { 1779 - if (printable(bprm->buf[0]) && printable(bprm->buf[1]) && 1780 - printable(bprm->buf[2]) && printable(bprm->buf[3])) 1781 - return retval; 1782 - if (request_module("binfmt-%04x", *(ushort *)(bprm->buf + 2)) < 0) 1783 - return retval; 1784 - need_retry = false; 1785 - goto retry; 1786 - } 1787 - 1788 - return retval; 1759 + return -ENOEXEC; 1789 1760 } 1790 1761 1791 1762 /* binfmt handlers will call back into begin_new_exec() on success. */ ··· 1913 1904 } 1914 1905 1915 1906 retval = count(argv, MAX_ARG_STRINGS); 1916 - if (retval == 0) 1917 - pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n", 1918 - current->comm, bprm->filename); 1919 1907 if (retval < 0) 1920 1908 goto out_free; 1921 1909 bprm->argc = retval; ··· 1950 1944 if (retval < 0) 1951 1945 goto out_free; 1952 1946 bprm->argc = 1; 1947 + 1948 + pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n", 1949 + current->comm, bprm->filename); 1953 1950 } 1954 1951 1955 1952 retval = bprm_execve(bprm);
+3 -1
include/linux/binfmts.h
··· 42 42 * Set when errors can no longer be returned to the 43 43 * original userspace. 44 44 */ 45 - point_of_no_return:1; 45 + point_of_no_return:1, 46 + /* Set when "comm" must come from the dentry. */ 47 + comm_from_dentry:1; 46 48 struct file *executable; /* Executable to pass to the interpreter */ 47 49 struct file *interpreter; 48 50 struct file *file;
+2 -2
include/linux/coredump.h
··· 52 52 #define __COREDUMP_PRINTK(Level, Format, ...) \ 53 53 do { \ 54 54 char comm[TASK_COMM_LEN]; \ 55 - \ 56 - get_task_comm(comm, current); \ 55 + /* This will always be NUL terminated. */ \ 56 + memcpy(comm, current->comm, sizeof(comm)); \ 57 57 printk_ratelimited(Level "coredump: %d(%*pE): " Format "\n", \ 58 58 task_tgid_vnr(current), (int)strlen(comm), comm, ##__VA_ARGS__); \ 59 59 } while (0) \
+4 -5
include/linux/sched.h
··· 1944 1944 #endif 1945 1945 1946 1946 extern void __set_task_comm(struct task_struct *tsk, const char *from, bool exec); 1947 - 1948 - static inline void set_task_comm(struct task_struct *tsk, const char *from) 1949 - { 1950 - __set_task_comm(tsk, from, false); 1951 - } 1947 + #define set_task_comm(tsk, from) ({ \ 1948 + BUILD_BUG_ON(sizeof(from) != TASK_COMM_LEN); \ 1949 + __set_task_comm(tsk, from, false); \ 1950 + }) 1952 1951 1953 1952 /* 1954 1953 * - Why not use task_lock()?
+1 -1
io_uring/io-wq.c
··· 634 634 struct io_wq_acct *acct = io_wq_get_acct(worker); 635 635 struct io_wq *wq = worker->wq; 636 636 bool exit_mask = false, last_timeout = false; 637 - char buf[TASK_COMM_LEN]; 637 + char buf[TASK_COMM_LEN] = {}; 638 638 639 639 set_mask_bits(&worker->flags, 0, 640 640 BIT(IO_WORKER_F_UP) | BIT(IO_WORKER_F_RUNNING));
+1 -1
io_uring/sqpoll.c
··· 264 264 struct io_ring_ctx *ctx; 265 265 struct rusage start; 266 266 unsigned long timeout = 0; 267 - char buf[TASK_COMM_LEN]; 267 + char buf[TASK_COMM_LEN] = {}; 268 268 DEFINE_WAIT(wait); 269 269 270 270 /* offload context creation failed, just exit */
+2 -1
kernel/kthread.c
··· 738 738 739 739 int kthreadd(void *unused) 740 740 { 741 + static const char comm[TASK_COMM_LEN] = "kthreadd"; 741 742 struct task_struct *tsk = current; 742 743 743 744 /* Setup a clean context for our children to inherit. */ 744 - set_task_comm(tsk, "kthreadd"); 745 + set_task_comm(tsk, comm); 745 746 ignore_signals(tsk); 746 747 set_cpus_allowed_ptr(tsk, housekeeping_cpumask(HK_TYPE_KTHREAD)); 747 748 set_mems_allowed(node_states[N_MEMORY]);
+72 -3
tools/testing/selftests/exec/execveat.c
··· 23 23 24 24 #include "../kselftest.h" 25 25 26 - #define TESTS_EXPECTED 51 26 + #define TESTS_EXPECTED 54 27 27 #define TEST_NAME_LEN (PATH_MAX * 4) 28 + 29 + #define CHECK_COMM "CHECK_COMM" 28 30 29 31 static char longpath[2 * PATH_MAX] = ""; 30 32 static char *envp[] = { "IN_TEST=yes", NULL, NULL }; ··· 239 237 return fail; 240 238 } 241 239 240 + static int check_execveat_comm(int fd, char *argv0, char *expected) 241 + { 242 + char buf[128], *old_env, *old_argv0; 243 + int ret; 244 + 245 + snprintf(buf, sizeof(buf), CHECK_COMM "=%s", expected); 246 + 247 + old_env = envp[1]; 248 + envp[1] = buf; 249 + 250 + old_argv0 = argv[0]; 251 + argv[0] = argv0; 252 + 253 + ksft_print_msg("Check execveat(AT_EMPTY_PATH)'s comm is %s\n", 254 + expected); 255 + ret = check_execveat_invoked_rc(fd, "", AT_EMPTY_PATH, 0, 0); 256 + 257 + envp[1] = old_env; 258 + argv[0] = old_argv0; 259 + 260 + return ret; 261 + } 262 + 242 263 static int run_tests(void) 243 264 { 244 265 int fail = 0; ··· 414 389 415 390 fail += check_execveat_pathmax(root_dfd, "execveat", 0); 416 391 fail += check_execveat_pathmax(root_dfd, "script", 1); 392 + 393 + /* /proc/pid/comm gives filename by default */ 394 + fail += check_execveat_comm(fd, "sentinel", "execveat"); 395 + /* /proc/pid/comm gives argv[0] when invoked via link */ 396 + fail += check_execveat_comm(fd_symlink, "sentinel", "execveat"); 397 + /* /proc/pid/comm gives filename if NULL is passed */ 398 + fail += check_execveat_comm(fd, NULL, "execveat"); 399 + 417 400 return fail; 418 401 } 419 402 ··· 448 415 int ii; 449 416 int rc; 450 417 const char *verbose = getenv("VERBOSE"); 418 + const char *check_comm = getenv(CHECK_COMM); 451 419 452 - if (argc >= 2) { 453 - /* If we are invoked with an argument, don't run tests. */ 420 + if (argc >= 2 || check_comm) { 421 + /* 422 + * If we are invoked with an argument, or no arguments but a 423 + * command to check, don't run tests. 424 + */ 454 425 const char *in_test = getenv("IN_TEST"); 455 426 456 427 if (verbose) { 457 428 ksft_print_msg("invoked with:\n"); 458 429 for (ii = 0; ii < argc; ii++) 459 430 ksft_print_msg("\t[%d]='%s\n'", ii, argv[ii]); 431 + } 432 + 433 + /* If the tests wanted us to check the command, do so. */ 434 + if (check_comm) { 435 + /* TASK_COMM_LEN == 16 */ 436 + char buf[32]; 437 + int fd, ret; 438 + 439 + fd = open("/proc/self/comm", O_RDONLY); 440 + if (fd < 0) { 441 + ksft_perror("open() comm failed"); 442 + exit(1); 443 + } 444 + 445 + ret = read(fd, buf, sizeof(buf)); 446 + if (ret < 0) { 447 + ksft_perror("read() comm failed"); 448 + close(fd); 449 + exit(1); 450 + } 451 + close(fd); 452 + 453 + // trim off the \n 454 + buf[ret-1] = 0; 455 + 456 + if (strcmp(buf, check_comm)) { 457 + ksft_print_msg("bad comm, got: %s expected: %s\n", 458 + buf, check_comm); 459 + exit(1); 460 + } 461 + 462 + exit(0); 460 463 } 461 464 462 465 /* Check expected environment transferred. */