execve updates for v5.20-rc1
- Allow unsharing time namespace on vfork+exec (Andrei Vagin) - Replace usage of deprecated kmap APIs (Fabio M. De Francesco) - Fix spelling mistake (Zhang Jiaming) -----BEGIN PGP SIGNATURE----- iQJKBAABCgA0FiEEpcP2jyKd1g9yPm4TiXL039xtwCYFAmLoDyAWHGtlZXNjb29r QGNocm9taXVtLm9yZwAKCRCJcvTf3G3AJh0mEACL07hj3eT3rWg6ohZx9sCTcAjY /tG+zxLQ7xu717nM1a4j7CI5kdNNpYbsCqG71ikDDRrOCeEutu7M8zE1emctjtHv oh853D6BKhV2Hvsiuk1oM2ZHR1bmgiW1eFNAJcCLz6rE6wYu564R0wYJV0h418fH Rjk+Y989A7Srs9t/9GQSktjX3Q039/PG28avhA5q144/ZNycr5FnLFOf4RlmzEUz 7E8TfGsftX8eRAfxW/dPiWuIKMuYPLqspca9pT3aFj3ze2qKnldjNV3c9M5ajL5Q q7KKWeWzunKyYHMaRzIxkHyhs396ZGKFN2PbcNYyml+NBItyc3fCHishMF7bW0Vb nyZbmYJslBloYmrSJYgqCfxyjUuhe0cMMk9iMzDVp6ROwtLgFFLwfwunM6RwRmnr dAmM8QGwSE3qYLhVnLEcRqpgdXzVd+S0TGhB5k5AyI3628/mLxhE66/eWq0X8QF5 los5zku1GagMkylt6SOGb3TME4JZe6ZdZpU4fe/ilM22qw852xgbF3+6Zap6IBbD AdzXVCHyU/obORfIxx5KTF213m4KpkWBBi3N1/vVlxIAFAUy1WdXDM1o2RPMD7hw DeHe8sgfTZxLmSqfWLuX+3qC94IvrbDPFaRCIMj1QNK0ltM8I9oHRPcUFyZMaV0O xHN/5QtmgVDfKA3mTw== =82SS -----END PGP SIGNATURE----- Merge tag 'execve-v5.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux 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
This commit is contained in:
Коммит
d7b767b508
15
fs/exec.c
15
fs/exec.c
|
@ -65,6 +65,7 @@
|
||||||
#include <linux/io_uring.h>
|
#include <linux/io_uring.h>
|
||||||
#include <linux/syscall_user_dispatch.h>
|
#include <linux/syscall_user_dispatch.h>
|
||||||
#include <linux/coredump.h>
|
#include <linux/coredump.h>
|
||||||
|
#include <linux/time_namespace.h>
|
||||||
|
|
||||||
#include <linux/uaccess.h>
|
#include <linux/uaccess.h>
|
||||||
#include <asm/mmu_context.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,
|
unsigned int bytes_to_copy = min_t(unsigned int, len,
|
||||||
min_not_zero(offset_in_page(pos), PAGE_SIZE));
|
min_not_zero(offset_in_page(pos), PAGE_SIZE));
|
||||||
struct page *page;
|
struct page *page;
|
||||||
char *kaddr;
|
|
||||||
|
|
||||||
pos -= bytes_to_copy;
|
pos -= bytes_to_copy;
|
||||||
arg -= 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);
|
page = get_arg_page(bprm, pos, 1);
|
||||||
if (!page)
|
if (!page)
|
||||||
return -E2BIG;
|
return -E2BIG;
|
||||||
kaddr = kmap_atomic(page);
|
|
||||||
flush_arg_page(bprm, pos & PAGE_MASK, page);
|
flush_arg_page(bprm, pos & PAGE_MASK, page);
|
||||||
memcpy(kaddr + offset_in_page(pos), arg, bytes_to_copy);
|
memcpy_to_page(page, offset_in_page(pos), arg, bytes_to_copy);
|
||||||
flush_dcache_page(page);
|
|
||||||
kunmap_atomic(kaddr);
|
|
||||||
put_arg_page(page);
|
put_arg_page(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -982,10 +979,12 @@ static int exec_mmap(struct mm_struct *mm)
|
||||||
{
|
{
|
||||||
struct task_struct *tsk;
|
struct task_struct *tsk;
|
||||||
struct mm_struct *old_mm, *active_mm;
|
struct mm_struct *old_mm, *active_mm;
|
||||||
|
bool vfork;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Notify parent that we're no longer interested in the old VM */
|
/* Notify parent that we're no longer interested in the old VM */
|
||||||
tsk = current;
|
tsk = current;
|
||||||
|
vfork = !!tsk->vfork_done;
|
||||||
old_mm = current->mm;
|
old_mm = current->mm;
|
||||||
exec_mm_release(tsk, old_mm);
|
exec_mm_release(tsk, old_mm);
|
||||||
if (old_mm)
|
if (old_mm)
|
||||||
|
@ -1030,6 +1029,10 @@ static int exec_mmap(struct mm_struct *mm)
|
||||||
tsk->mm->vmacache_seqnum = 0;
|
tsk->mm->vmacache_seqnum = 0;
|
||||||
vmacache_flush(tsk);
|
vmacache_flush(tsk);
|
||||||
task_unlock(tsk);
|
task_unlock(tsk);
|
||||||
|
|
||||||
|
if (vfork)
|
||||||
|
timens_on_fork(tsk->nsproxy, tsk);
|
||||||
|
|
||||||
if (old_mm) {
|
if (old_mm) {
|
||||||
mmap_read_unlock(old_mm);
|
mmap_read_unlock(old_mm);
|
||||||
BUG_ON(active_mm != 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,
|
* We are going to release_task()->ptrace_unlink() silently,
|
||||||
* the tracer can sleep in do_wait(). EXIT_DEAD guarantees
|
* 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))
|
if (unlikely(leader->ptrace))
|
||||||
__wake_up_parent(leader, leader->parent);
|
__wake_up_parent(leader, leader->parent);
|
||||||
|
|
|
@ -2033,8 +2033,11 @@ static __latent_entropy struct task_struct *copy_process(
|
||||||
/*
|
/*
|
||||||
* If the new process will be in a different time namespace
|
* 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.
|
* 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)
|
if (nsp->time_ns != nsp->time_ns_for_children)
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
}
|
}
|
||||||
|
|
|
@ -179,6 +179,7 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
|
||||||
if (IS_ERR(new_ns))
|
if (IS_ERR(new_ns))
|
||||||
return PTR_ERR(new_ns);
|
return PTR_ERR(new_ns);
|
||||||
|
|
||||||
|
if ((flags & CLONE_VM) == 0)
|
||||||
timens_on_fork(new_ns, tsk);
|
timens_on_fork(new_ns, tsk);
|
||||||
|
|
||||||
tsk->nsproxy = new_ns;
|
tsk->nsproxy = new_ns;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
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
|
TEST_GEN_PROGS_EXTENDED := gettime_perf
|
||||||
|
|
||||||
CFLAGS := -Wall -Werror -pthread
|
CFLAGS := -Wall -Werror -pthread
|
||||||
|
|
|
@ -0,0 +1,90 @@
|
||||||
|
// 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;
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче