Commit 7fc03505 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull execve updates from Kees Cook:
 "Most are small refactorings and bug fixes, but three things stand out:
  switching timens (which got reverted before) looks solid now,
  FOLL_FORCE has been removed (no failures seen yet across several weeks
  in -next), and some whitespace cleanups (which are long overdue).

   - Add timens support (when switching mm). This version has survived
     in -next for the entire cycle (Andrei Vagin)

   - Various small bug fixes, refactoring, and readability improvements
     (Bernd Edlinger, Rolf Eike Beer, Bo Liu, Li Zetao Liu Shixin)

   - Remove FOLL_FORCE for stack setup (Kees Cook)

   - Whitespace cleanups (Rolf Eike Beer, Kees Cook)"

* tag 'execve-v6.2-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux:
  binfmt_misc: fix shift-out-of-bounds in check_special_flags
  binfmt: Fix error return code in load_elf_fdpic_binary()
  exec: Remove FOLL_FORCE for stack setup
  binfmt_elf: replace IS_ERR() with IS_ERR_VALUE()
  binfmt_elf: simplify error handling in load_elf_phdrs()
  binfmt_elf: fix documented return value for load_elf_phdrs()
  exec: simplify initial stack size expansion
  binfmt: Fix whitespace issues
  exec: Add comments on check_unsafe_exec() fs counting
  ELF uapi: add spaces before '{'
  selftests/timens: add a test for vfork+exit
  fs/exec: switch timens when a task gets a new mm
parents 667161ba 6a46bf55
...@@ -248,7 +248,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec, ...@@ -248,7 +248,7 @@ create_elf_tables(struct linux_binprm *bprm, const struct elfhdr *exec,
} while (0) } while (0)
#ifdef ARCH_DLINFO #ifdef ARCH_DLINFO
/* /*
* ARCH_DLINFO must come first so PPC can do its special alignment of * ARCH_DLINFO must come first so PPC can do its special alignment of
* AUXV. * AUXV.
* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT() in * update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT() in
...@@ -456,13 +456,13 @@ static unsigned long maximum_alignment(struct elf_phdr *cmds, int nr) ...@@ -456,13 +456,13 @@ static unsigned long maximum_alignment(struct elf_phdr *cmds, int nr)
* *
* Loads ELF program headers from the binary file elf_file, which has the ELF * Loads ELF program headers from the binary file elf_file, which has the ELF
* header pointed to by elf_ex, into a newly allocated array. The caller is * header pointed to by elf_ex, into a newly allocated array. The caller is
* responsible for freeing the allocated data. Returns an ERR_PTR upon failure. * responsible for freeing the allocated data. Returns NULL upon failure.
*/ */
static struct elf_phdr *load_elf_phdrs(const struct elfhdr *elf_ex, static struct elf_phdr *load_elf_phdrs(const struct elfhdr *elf_ex,
struct file *elf_file) struct file *elf_file)
{ {
struct elf_phdr *elf_phdata = NULL; struct elf_phdr *elf_phdata = NULL;
int retval, err = -1; int retval = -1;
unsigned int size; unsigned int size;
/* /*
...@@ -484,15 +484,9 @@ static struct elf_phdr *load_elf_phdrs(const struct elfhdr *elf_ex, ...@@ -484,15 +484,9 @@ static struct elf_phdr *load_elf_phdrs(const struct elfhdr *elf_ex,
/* Read in the program headers */ /* Read in the program headers */
retval = elf_read(elf_file, elf_phdata, size, elf_ex->e_phoff); retval = elf_read(elf_file, elf_phdata, size, elf_ex->e_phoff);
if (retval < 0) {
err = retval;
goto out;
}
/* Success! */
err = 0;
out: out:
if (err) { if (retval) {
kfree(elf_phdata); kfree(elf_phdata);
elf_phdata = NULL; elf_phdata = NULL;
} }
...@@ -1020,7 +1014,7 @@ static int load_elf_binary(struct linux_binprm *bprm) ...@@ -1020,7 +1014,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
executable_stack); executable_stack);
if (retval < 0) if (retval < 0)
goto out_free_dentry; goto out_free_dentry;
elf_bss = 0; elf_bss = 0;
elf_brk = 0; elf_brk = 0;
...@@ -1043,7 +1037,7 @@ static int load_elf_binary(struct linux_binprm *bprm) ...@@ -1043,7 +1037,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
if (unlikely (elf_brk > elf_bss)) { if (unlikely (elf_brk > elf_bss)) {
unsigned long nbyte; unsigned long nbyte;
/* There was a PT_LOAD segment with p_memsz > p_filesz /* There was a PT_LOAD segment with p_memsz > p_filesz
before this one. Map anonymous pages, if needed, before this one. Map anonymous pages, if needed,
and clear the area. */ and clear the area. */
...@@ -1166,7 +1160,7 @@ static int load_elf_binary(struct linux_binprm *bprm) ...@@ -1166,7 +1160,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt, error = elf_map(bprm->file, load_bias + vaddr, elf_ppnt,
elf_prot, elf_flags, total_size); elf_prot, elf_flags, total_size);
if (BAD_ADDR(error)) { if (BAD_ADDR(error)) {
retval = IS_ERR((void *)error) ? retval = IS_ERR_VALUE(error) ?
PTR_ERR((void*)error) : -EINVAL; PTR_ERR((void*)error) : -EINVAL;
goto out_free_dentry; goto out_free_dentry;
} }
...@@ -1251,7 +1245,7 @@ static int load_elf_binary(struct linux_binprm *bprm) ...@@ -1251,7 +1245,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
interpreter, interpreter,
load_bias, interp_elf_phdata, load_bias, interp_elf_phdata,
&arch_state); &arch_state);
if (!IS_ERR((void *)elf_entry)) { if (!IS_ERR_VALUE(elf_entry)) {
/* /*
* load_elf_interp() returns relocation * load_elf_interp() returns relocation
* adjustment * adjustment
...@@ -1260,7 +1254,7 @@ static int load_elf_binary(struct linux_binprm *bprm) ...@@ -1260,7 +1254,7 @@ static int load_elf_binary(struct linux_binprm *bprm)
elf_entry += interp_elf_ex->e_entry; elf_entry += interp_elf_ex->e_entry;
} }
if (BAD_ADDR(elf_entry)) { if (BAD_ADDR(elf_entry)) {
retval = IS_ERR((void *)elf_entry) ? retval = IS_ERR_VALUE(elf_entry) ?
(int)elf_entry : -EINVAL; (int)elf_entry : -EINVAL;
goto out_free_dentry; goto out_free_dentry;
} }
...@@ -1521,7 +1515,7 @@ static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, loff_t offset) ...@@ -1521,7 +1515,7 @@ static void fill_elf_note_phdr(struct elf_phdr *phdr, int sz, loff_t offset)
phdr->p_align = 0; phdr->p_align = 0;
} }
static void fill_note(struct memelfnote *note, const char *name, int type, static void fill_note(struct memelfnote *note, const char *name, int type,
unsigned int sz, void *data) unsigned int sz, void *data)
{ {
note->name = name; note->name = name;
...@@ -2004,8 +1998,8 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t) ...@@ -2004,8 +1998,8 @@ static int elf_dump_thread_status(long signr, struct elf_thread_status *t)
t->num_notes = 0; t->num_notes = 0;
fill_prstatus(&t->prstatus.common, p, signr); fill_prstatus(&t->prstatus.common, p, signr);
elf_core_copy_task_regs(p, &t->prstatus.pr_reg); elf_core_copy_task_regs(p, &t->prstatus.pr_reg);
fill_note(&t->notes[0], "CORE", NT_PRSTATUS, sizeof(t->prstatus), fill_note(&t->notes[0], "CORE", NT_PRSTATUS, sizeof(t->prstatus),
&(t->prstatus)); &(t->prstatus));
t->num_notes++; t->num_notes++;
...@@ -2295,7 +2289,7 @@ static int elf_core_dump(struct coredump_params *cprm) ...@@ -2295,7 +2289,7 @@ static int elf_core_dump(struct coredump_params *cprm)
if (!elf_core_write_extra_phdrs(cprm, offset)) if (!elf_core_write_extra_phdrs(cprm, offset))
goto end_coredump; goto end_coredump;
/* write out the notes section */ /* write out the notes section */
if (!write_note_info(&info, cprm)) if (!write_note_info(&info, cprm))
goto end_coredump; goto end_coredump;
......
...@@ -434,8 +434,9 @@ static int load_elf_fdpic_binary(struct linux_binprm *bprm) ...@@ -434,8 +434,9 @@ 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
if (create_elf_fdpic_tables(bprm, current->mm, retval = create_elf_fdpic_tables(bprm, current->mm, &exec_params,
&exec_params, &interp_params) < 0) &interp_params);
if (retval < 0)
goto error; goto error;
kdebug("- start_code %lx", current->mm->start_code); kdebug("- start_code %lx", current->mm->start_code);
...@@ -1603,7 +1604,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm) ...@@ -1603,7 +1604,7 @@ static int elf_fdpic_core_dump(struct coredump_params *cprm)
if (!elf_core_write_extra_phdrs(cprm, offset)) if (!elf_core_write_extra_phdrs(cprm, offset))
goto end_coredump; goto end_coredump;
/* write out the notes section */ /* write out the notes section */
if (!writenote(thread_list->notes, cprm)) if (!writenote(thread_list->notes, cprm))
goto end_coredump; goto end_coredump;
if (!writenote(&psinfo_note, cprm)) if (!writenote(&psinfo_note, cprm))
......
...@@ -44,10 +44,10 @@ static LIST_HEAD(entries); ...@@ -44,10 +44,10 @@ static LIST_HEAD(entries);
static int enabled = 1; static int enabled = 1;
enum {Enabled, Magic}; enum {Enabled, Magic};
#define MISC_FMT_PRESERVE_ARGV0 (1 << 31) #define MISC_FMT_PRESERVE_ARGV0 (1UL << 31)
#define MISC_FMT_OPEN_BINARY (1 << 30) #define MISC_FMT_OPEN_BINARY (1UL << 30)
#define MISC_FMT_CREDENTIALS (1 << 29) #define MISC_FMT_CREDENTIALS (1UL << 29)
#define MISC_FMT_OPEN_FILE (1 << 28) #define MISC_FMT_OPEN_FILE (1UL << 28)
typedef struct { typedef struct {
struct list_head list; struct list_head list;
......
...@@ -64,6 +64,7 @@ ...@@ -64,6 +64,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>
...@@ -171,7 +172,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library) ...@@ -171,7 +172,7 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
exit: exit:
fput(file); fput(file);
out: out:
return error; return error;
} }
#endif /* #ifdef CONFIG_USELIB */ #endif /* #ifdef CONFIG_USELIB */
...@@ -199,7 +200,7 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos, ...@@ -199,7 +200,7 @@ static struct page *get_arg_page(struct linux_binprm *bprm, unsigned long pos,
{ {
struct page *page; struct page *page;
int ret; int ret;
unsigned int gup_flags = FOLL_FORCE; unsigned int gup_flags = 0;
#ifdef CONFIG_STACK_GROWSUP #ifdef CONFIG_STACK_GROWSUP
if (write) { if (write) {
...@@ -842,16 +843,13 @@ int setup_arg_pages(struct linux_binprm *bprm, ...@@ -842,16 +843,13 @@ int setup_arg_pages(struct linux_binprm *bprm,
* will align it up. * will align it up.
*/ */
rlim_stack = bprm->rlim_stack.rlim_cur & PAGE_MASK; rlim_stack = bprm->rlim_stack.rlim_cur & PAGE_MASK;
stack_expand = min(rlim_stack, stack_size + stack_expand);
#ifdef CONFIG_STACK_GROWSUP #ifdef CONFIG_STACK_GROWSUP
if (stack_size + stack_expand > rlim_stack) stack_base = vma->vm_start + stack_expand;
stack_base = vma->vm_start + rlim_stack;
else
stack_base = vma->vm_end + stack_expand;
#else #else
if (stack_size + stack_expand > rlim_stack) stack_base = vma->vm_end - stack_expand;
stack_base = vma->vm_end - rlim_stack;
else
stack_base = vma->vm_start - stack_expand;
#endif #endif
current->mm->start_stack = bprm->p; current->mm->start_stack = bprm->p;
ret = expand_stack(vma, stack_base); ret = expand_stack(vma, stack_base);
...@@ -1297,6 +1295,10 @@ int begin_new_exec(struct linux_binprm * bprm) ...@@ -1297,6 +1295,10 @@ int begin_new_exec(struct linux_binprm * bprm)
bprm->mm = NULL; bprm->mm = NULL;
retval = exec_task_namespaces();
if (retval)
goto out_unlock;
#ifdef CONFIG_POSIX_TIMERS #ifdef CONFIG_POSIX_TIMERS
spin_lock_irq(&me->sighand->siglock); spin_lock_irq(&me->sighand->siglock);
posix_cpu_timers_exit(me); posix_cpu_timers_exit(me);
...@@ -1568,6 +1570,12 @@ static void check_unsafe_exec(struct linux_binprm *bprm) ...@@ -1568,6 +1570,12 @@ static void check_unsafe_exec(struct linux_binprm *bprm)
if (task_no_new_privs(current)) if (task_no_new_privs(current))
bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS; bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS;
/*
* If another task is sharing our fs, we cannot safely
* suid exec because the differently privileged task
* will be able to manipulate the current directory, etc.
* It would be nice to force an unshare instead...
*/
t = p; t = p;
n_fs = 1; n_fs = 1;
spin_lock(&p->fs->lock); spin_lock(&p->fs->lock);
...@@ -1748,6 +1756,7 @@ static int search_binary_handler(struct linux_binprm *bprm) ...@@ -1748,6 +1756,7 @@ static int search_binary_handler(struct linux_binprm *bprm)
return retval; return retval;
} }
/* binfmt handlers will call back into begin_new_exec() on success. */
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;
...@@ -1806,6 +1815,11 @@ static int bprm_execve(struct linux_binprm *bprm, ...@@ -1806,6 +1815,11 @@ static int bprm_execve(struct linux_binprm *bprm,
if (retval) if (retval)
return retval; return retval;
/*
* Check for unsafe execution states before exec_binprm(), which
* will call back into begin_new_exec(), into bprm_creds_from_file(),
* where setuid-ness is evaluated.
*/
check_unsafe_exec(bprm); check_unsafe_exec(bprm);
current->in_execve = 1; current->in_execve = 1;
......
...@@ -94,6 +94,7 @@ static inline struct cred *nsset_cred(struct nsset *set) ...@@ -94,6 +94,7 @@ static inline struct cred *nsset_cred(struct nsset *set)
int copy_namespaces(unsigned long flags, struct task_struct *tsk); int copy_namespaces(unsigned long flags, struct task_struct *tsk);
void exit_task_namespaces(struct task_struct *tsk); void exit_task_namespaces(struct task_struct *tsk);
void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new); void switch_task_namespaces(struct task_struct *tsk, struct nsproxy *new);
int exec_task_namespaces(void);
void free_nsproxy(struct nsproxy *ns); void free_nsproxy(struct nsproxy *ns);
int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **, int unshare_nsproxy_namespaces(unsigned long, struct nsproxy **,
struct cred *, struct fs_struct *); struct cred *, struct fs_struct *);
......
...@@ -91,7 +91,7 @@ typedef __s64 Elf64_Sxword; ...@@ -91,7 +91,7 @@ typedef __s64 Elf64_Sxword;
#define DT_INIT 12 #define DT_INIT 12
#define DT_FINI 13 #define DT_FINI 13
#define DT_SONAME 14 #define DT_SONAME 14
#define DT_RPATH 15 #define DT_RPATH 15
#define DT_SYMBOLIC 16 #define DT_SYMBOLIC 16
#define DT_REL 17 #define DT_REL 17
#define DT_RELSZ 18 #define DT_RELSZ 18
...@@ -140,9 +140,9 @@ typedef __s64 Elf64_Sxword; ...@@ -140,9 +140,9 @@ typedef __s64 Elf64_Sxword;
#define ELF64_ST_BIND(x) ELF_ST_BIND(x) #define ELF64_ST_BIND(x) ELF_ST_BIND(x)
#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x) #define ELF64_ST_TYPE(x) ELF_ST_TYPE(x)
typedef struct dynamic{ typedef struct dynamic {
Elf32_Sword d_tag; Elf32_Sword d_tag;
union{ union {
Elf32_Sword d_val; Elf32_Sword d_val;
Elf32_Addr d_ptr; Elf32_Addr d_ptr;
} d_un; } d_un;
...@@ -173,7 +173,7 @@ typedef struct elf64_rel { ...@@ -173,7 +173,7 @@ typedef struct elf64_rel {
Elf64_Xword r_info; /* index and type of relocation */ Elf64_Xword r_info; /* index and type of relocation */
} Elf64_Rel; } Elf64_Rel;
typedef struct elf32_rela{ typedef struct elf32_rela {
Elf32_Addr r_offset; Elf32_Addr r_offset;
Elf32_Word r_info; Elf32_Word r_info;
Elf32_Sword r_addend; Elf32_Sword r_addend;
...@@ -185,7 +185,7 @@ typedef struct elf64_rela { ...@@ -185,7 +185,7 @@ typedef struct elf64_rela {
Elf64_Sxword r_addend; /* Constant addend used to compute value */ Elf64_Sxword r_addend; /* Constant addend used to compute value */
} Elf64_Rela; } Elf64_Rela;
typedef struct elf32_sym{ typedef struct elf32_sym {
Elf32_Word st_name; Elf32_Word st_name;
Elf32_Addr st_value; Elf32_Addr st_value;
Elf32_Word st_size; Elf32_Word st_size;
...@@ -206,7 +206,7 @@ typedef struct elf64_sym { ...@@ -206,7 +206,7 @@ typedef struct elf64_sym {
#define EI_NIDENT 16 #define EI_NIDENT 16
typedef struct elf32_hdr{ typedef struct elf32_hdr {
unsigned char e_ident[EI_NIDENT]; unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type; Elf32_Half e_type;
Elf32_Half e_machine; Elf32_Half e_machine;
...@@ -246,7 +246,7 @@ typedef struct elf64_hdr { ...@@ -246,7 +246,7 @@ typedef struct elf64_hdr {
#define PF_W 0x2 #define PF_W 0x2
#define PF_X 0x1 #define PF_X 0x1
typedef struct elf32_phdr{ typedef struct elf32_phdr {
Elf32_Word p_type; Elf32_Word p_type;
Elf32_Off p_offset; Elf32_Off p_offset;
Elf32_Addr p_vaddr; Elf32_Addr p_vaddr;
......
...@@ -2046,15 +2046,6 @@ static __latent_entropy struct task_struct *copy_process( ...@@ -2046,15 +2046,6 @@ static __latent_entropy struct task_struct *copy_process(
return ERR_PTR(-EINVAL); return ERR_PTR(-EINVAL);
} }
/*
* 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.
*/
if (clone_flags & (CLONE_THREAD | CLONE_VM)) {
if (nsp->time_ns != nsp->time_ns_for_children)
return ERR_PTR(-EINVAL);
}
if (clone_flags & CLONE_PIDFD) { if (clone_flags & CLONE_PIDFD) {
/* /*
* - CLONE_DETACHED is blocked so that we can potentially * - CLONE_DETACHED is blocked so that we can potentially
......
...@@ -157,7 +157,8 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) ...@@ -157,7 +157,8 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk)
if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | if (likely(!(flags & (CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
CLONE_NEWPID | CLONE_NEWNET | CLONE_NEWPID | CLONE_NEWNET |
CLONE_NEWCGROUP | CLONE_NEWTIME)))) { CLONE_NEWCGROUP | CLONE_NEWTIME)))) {
if (likely(old_ns->time_ns_for_children == old_ns->time_ns)) { if ((flags & CLONE_VM) ||
likely(old_ns->time_ns_for_children == old_ns->time_ns)) {
get_nsproxy(old_ns); get_nsproxy(old_ns);
return 0; return 0;
} }
...@@ -179,7 +180,8 @@ int copy_namespaces(unsigned long flags, struct task_struct *tsk) ...@@ -179,7 +180,8 @@ 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);
timens_on_fork(new_ns, tsk); if ((flags & CLONE_VM) == 0)
timens_on_fork(new_ns, tsk);
tsk->nsproxy = new_ns; tsk->nsproxy = new_ns;
return 0; return 0;
...@@ -254,6 +256,23 @@ void exit_task_namespaces(struct task_struct *p) ...@@ -254,6 +256,23 @@ void exit_task_namespaces(struct task_struct *p)
switch_task_namespaces(p, NULL); switch_task_namespaces(p, NULL);
} }
int exec_task_namespaces(void)
{
struct task_struct *tsk = current;
struct nsproxy *new;
if (tsk->nsproxy->time_ns_for_children == tsk->nsproxy->time_ns)
return 0;
new = create_new_namespaces(0, tsk, current_user_ns(), tsk->fs);
if (IS_ERR(new))
return PTR_ERR(new);
timens_on_fork(new, tsk);
switch_task_namespaces(tsk, new);
return 0;
}
static int check_setns_flags(unsigned long flags) static int check_setns_flags(unsigned long flags)
{ {
if (!flags || (flags & ~(CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | if (!flags || (flags & ~(CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC |
......
...@@ -8,3 +8,4 @@ procfs ...@@ -8,3 +8,4 @@ procfs
timens timens
timer timer
timerfd timerfd
vfork_exec
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
......
// 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 <pthread.h>
#include "log.h"
#include "timens.h"
#define OFFSET (36000)
struct thread_args {
char *tst_name;
struct timespec *now;
};
static void *tcheck(void *_args)
{
struct thread_args *args = _args;
struct timespec *now = args->now, tst;
int i;
for (i = 0; i < 2; i++) {
_gettime(CLOCK_MONOTONIC, &tst, i);
if (abs(tst.tv_sec - now->tv_sec) > 5) {
pr_fail("%s: in-thread: unexpected value: %ld (%ld)\n",
args->tst_name, tst.tv_sec, now->tv_sec);
return (void *)1UL;
}
}
return NULL;
}
static int check_in_thread(char *tst_name, struct timespec *now)
{
struct thread_args args = {
.tst_name = tst_name,
.now = now,
};
pthread_t th;
void *retval;
if (pthread_create(&th, NULL, tcheck, &args))
return pr_perror("thread");
if (pthread_join(th, &retval))
return pr_perror("pthread_join");
return !(retval == NULL);
}
static int check(char *tst_name, struct timespec *now)
{
struct timespec tst;
int i;
for (i = 0; i < 2; i++) {
_gettime(CLOCK_MONOTONIC, &tst, i);
if (abs(tst.tv_sec - now->tv_sec) > 5)
return pr_fail("%s: unexpected value: %ld (%ld)\n",
tst_name, tst.tv_sec, now->tv_sec);
}
if (check_in_thread(tst_name, now))
return 1;
ksft_test_result_pass("%s\n", tst_name);
return 0;
}
int main(int argc, char *argv[])
{
struct timespec now;
int status;
pid_t pid;
if (argc > 1) {
char *endptr;
ksft_cnt.ksft_pass = 1;
now.tv_sec = strtoul(argv[1], &endptr, 0);
if (*endptr != 0)
return pr_perror("strtoul");
return check("child after exec", &now);
}
nscheck();
ksft_set_plan(4);
clock_gettime(CLOCK_MONOTONIC, &now);
if (unshare_timens())
return 1;
if (_settime(CLOCK_MONOTONIC, OFFSET))
return 1;
if (check("parent before vfork", &now))
return 1;
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 for proper vvar offsets after execve. */
snprintf(now_str, sizeof(now_str), "%ld", now.tv_sec + OFFSET);
execve("/proc/self/exe", cargv, cenv);
pr_perror("execve");
_exit(1);
}
if (waitpid(pid, &status, 0) != pid)
return pr_perror("waitpid");
if (status)
ksft_exit_fail();
ksft_inc_pass_cnt();
ksft_test_result_pass("wait for child\n");
/* Check that we are still in the source timens. */
if (check("parent after vfork", &now))
return 1;
ksft_exit_pass();
return 0;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment