Commit d7b767b5 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull execve updates from Kees Cook:

 - Allow unsharing time namespace on vfork+exec (Andrei Vagin)

 - Replace usage of deprecated kmap APIs (Fabio M. De Francesco)

 - Fix spelling mistake (Zhang Jiaming)

* tag 'execve-v5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  exec: Call kmap_local_page() in copy_string_kernel()
  exec: Fix a spelling mistake
  selftests/timens: add a test for vfork+exit
  fs/exec: allow to unshare a time namespace on vfork+exec
parents 9a8ac9ee c6e8e36c
Loading
Loading
Loading
Loading
+9 −6
Original line number Diff line number Diff line
@@ -65,6 +65,7 @@
#include <linux/io_uring.h>
#include <linux/syscall_user_dispatch.h>
#include <linux/coredump.h>
#include <linux/time_namespace.h>

#include <linux/uaccess.h>
#include <asm/mmu_context.h>
@@ -630,7 +631,6 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm)
		unsigned int bytes_to_copy = min_t(unsigned int, len,
				min_not_zero(offset_in_page(pos), PAGE_SIZE));
		struct page *page;
		char *kaddr;

		pos -= bytes_to_copy;
		arg -= bytes_to_copy;
@@ -639,11 +639,8 @@ int copy_string_kernel(const char *arg, struct linux_binprm *bprm)
		page = get_arg_page(bprm, pos, 1);
		if (!page)
			return -E2BIG;
		kaddr = kmap_atomic(page);
		flush_arg_page(bprm, pos & PAGE_MASK, page);
		memcpy(kaddr + offset_in_page(pos), arg, bytes_to_copy);
		flush_dcache_page(page);
		kunmap_atomic(kaddr);
		memcpy_to_page(page, offset_in_page(pos), arg, bytes_to_copy);
		put_arg_page(page);
	}

@@ -982,10 +979,12 @@ static int exec_mmap(struct mm_struct *mm)
{
	struct task_struct *tsk;
	struct mm_struct *old_mm, *active_mm;
	bool vfork;
	int ret;

	/* Notify parent that we're no longer interested in the old VM */
	tsk = current;
	vfork = !!tsk->vfork_done;
	old_mm = current->mm;
	exec_mm_release(tsk, old_mm);
	if (old_mm)
@@ -1030,6 +1029,10 @@ static int exec_mmap(struct mm_struct *mm)
	tsk->mm->vmacache_seqnum = 0;
	vmacache_flush(tsk);
	task_unlock(tsk);

	if (vfork)
		timens_on_fork(tsk->nsproxy, tsk);

	if (old_mm) {
		mmap_read_unlock(old_mm);
		BUG_ON(active_mm != old_mm);
@@ -1149,7 +1152,7 @@ static int de_thread(struct task_struct *tsk)
		/*
		 * We are going to release_task()->ptrace_unlink() silently,
		 * the tracer can sleep in do_wait(). EXIT_DEAD guarantees
		 * the tracer wont't block again waiting for this thread.
		 * the tracer won't block again waiting for this thread.
		 */
		if (unlikely(leader->ptrace))
			__wake_up_parent(leader, leader->parent);
+4 −1
Original line number Diff line number Diff line
@@ -2033,8 +2033,11 @@ static __latent_entropy struct task_struct *copy_process(
	/*
	 * If the new process will be in a different time namespace
	 * do not allow it to share VM or a thread group with the forking task.
	 *
	 * On vfork, the child process enters the target time namespace only
	 * after exec.
	 */
	if (clone_flags & (CLONE_THREAD | CLONE_VM)) {
	if ((clone_flags & (CLONE_VM | CLONE_VFORK)) == CLONE_VM) {
		if (nsp->time_ns != nsp->time_ns_for_children)
			return ERR_PTR(-EINVAL);
	}
+2 −1
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
	if (IS_ERR(new_ns))
		return  PTR_ERR(new_ns);

	if ((flags & CLONE_VM) == 0)
		timens_on_fork(new_ns, tsk);

	tsk->nsproxy = new_ns;
+1 −1
Original line number Diff line number Diff line
TEST_GEN_PROGS := timens timerfd timer clock_nanosleep procfs exec futex
TEST_GEN_PROGS := timens timerfd timer clock_nanosleep procfs exec futex vfork_exec
TEST_GEN_PROGS_EXTENDED := gettime_perf

CFLAGS := -Wall -Werror -pthread
+90 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <sched.h>
#include <stdio.h>
#include <stdbool.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <string.h>

#include "log.h"
#include "timens.h"

#define OFFSET (36000)

int main(int argc, char *argv[])
{
	struct timespec now, tst;
	int status, i;
	pid_t pid;

	if (argc > 1) {
		if (sscanf(argv[1], "%ld", &now.tv_sec) != 1)
			return pr_perror("sscanf");

		for (i = 0; i < 2; i++) {
			_gettime(CLOCK_MONOTONIC, &tst, i);
			if (abs(tst.tv_sec - now.tv_sec) > 5)
				return pr_fail("%ld %ld\n", now.tv_sec, tst.tv_sec);
		}
		return 0;
	}

	nscheck();

	ksft_set_plan(1);

	clock_gettime(CLOCK_MONOTONIC, &now);

	if (unshare_timens())
		return 1;

	if (_settime(CLOCK_MONOTONIC, OFFSET))
		return 1;

	for (i = 0; i < 2; i++) {
		_gettime(CLOCK_MONOTONIC, &tst, i);
		if (abs(tst.tv_sec - now.tv_sec) > 5)
			return pr_fail("%ld %ld\n",
					now.tv_sec, tst.tv_sec);
	}

	pid = vfork();
	if (pid < 0)
		return pr_perror("fork");

	if (pid == 0) {
		char now_str[64];
		char *cargv[] = {"exec", now_str, NULL};
		char *cenv[] = {NULL};

		// Check that we are still in the source timens.
		for (i = 0; i < 2; i++) {
			_gettime(CLOCK_MONOTONIC, &tst, i);
			if (abs(tst.tv_sec - now.tv_sec) > 5)
				return pr_fail("%ld %ld\n",
						now.tv_sec, tst.tv_sec);
		}

		/* Check for proper vvar offsets after execve. */
		snprintf(now_str, sizeof(now_str), "%ld", now.tv_sec + OFFSET);
		execve("/proc/self/exe", cargv, cenv);
		return pr_perror("execve");
	}

	if (waitpid(pid, &status, 0) != pid)
		return pr_perror("waitpid");

	if (status)
		ksft_exit_fail();

	ksft_test_result_pass("exec\n");
	ksft_exit_pass();
	return 0;
}