mirror of
https://github.com/AuxXxilium/linux_dsm_epyc7002.git
synced 2024-12-25 01:26:05 +07:00
7f192e3cd3
This adds the clone3 system call.
As mentioned several times already (cf. [7], [8]) here's the promised
patchset for clone3().
We recently merged the CLONE_PIDFD patchset (cf. [1]). It took the last
free flag from clone().
Independent of the CLONE_PIDFD patchset a time namespace has been discussed
at Linux Plumber Conference last year and has been sent out and reviewed
(cf. [5]). It is expected that it will go upstream in the not too distant
future. However, it relies on the addition of the CLONE_NEWTIME flag to
clone(). The only other good candidate - CLONE_DETACHED - is currently not
recyclable as we have identified at least two large or widely used
codebases that currently pass this flag (cf. [2], [3], and [4]). Given that
CLONE_PIDFD grabbed the last clone() flag the time namespace is effectively
blocked. clone3() has the advantage that it will unblock this patchset
again. In general, clone3() is extensible and allows for the implementation
of new features.
The idea is to keep clone3() very simple and close to the original clone(),
specifically, to keep on supporting old clone()-based workloads.
We know there have been various creative proposals how a new process
creation syscall or even api is supposed to look like. Some people even
going so far as to argue that the traditional fork()+exec() split should be
abandoned in favor of an in-kernel version of spawn(). Independent of
whether or not we personally think spawn() is a good idea this patchset has
and does not want to have anything to do with this.
One stance we take is that there's no real good alternative to
clone()+exec() and we need and want to support this model going forward;
independent of spawn().
The following requirements guided clone3():
- bump the number of available flags
- move arguments that are currently passed as separate arguments
in clone() into a dedicated struct clone_args
- choose a struct layout that is easy to handle on 32 and on 64 bit
- choose a struct layout that is extensible
- give new flags that currently need to abuse another flag's dedicated
return argument in clone() their own dedicated return argument
(e.g. CLONE_PIDFD)
- use a separate kernel internal struct kernel_clone_args that is
properly typed according to current kernel conventions in fork.c and is
different from the uapi struct clone_args
- port _do_fork() to use kernel_clone_args so that all process creation
syscalls such as fork(), vfork(), clone(), and clone3() behave identical
(Arnd suggested, that we can probably also port do_fork() itself in a
separate patchset.)
- ease of transition for userspace from clone() to clone3()
This very much means that we do *not* remove functionality that userspace
currently relies on as the latter is a good way of creating a syscall
that won't be adopted.
- do not try to be clever or complex: keep clone3() as dumb as possible
In accordance with Linus suggestions (cf. [11]), clone3() has the following
signature:
/* uapi */
struct clone_args {
__aligned_u64 flags;
__aligned_u64 pidfd;
__aligned_u64 child_tid;
__aligned_u64 parent_tid;
__aligned_u64 exit_signal;
__aligned_u64 stack;
__aligned_u64 stack_size;
__aligned_u64 tls;
};
/* kernel internal */
struct kernel_clone_args {
u64 flags;
int __user *pidfd;
int __user *child_tid;
int __user *parent_tid;
int exit_signal;
unsigned long stack;
unsigned long stack_size;
unsigned long tls;
};
long sys_clone3(struct clone_args __user *uargs, size_t size)
clone3() cleanly supports all of the supported flags from clone() and thus
all legacy workloads.
The advantage of sticking close to the old clone() is the low cost for
userspace to switch to this new api. Quite a lot of userspace apis (e.g.
pthreads) are based on the clone() syscall. With the new clone3() syscall
supporting all of the old workloads and opening up the ability to add new
features should make switching to it for userspace more appealing. In
essence, glibc can just write a simple wrapper to switch from clone() to
clone3().
There has been some interest in this patchset already. We have received a
patch from the CRIU corner for clone3() that would set the PID/TID of a
restored process without /proc/sys/kernel/ns_last_pid to eliminate a race.
/* User visible differences to legacy clone() */
- CLONE_DETACHED will cause EINVAL with clone3()
- CSIGNAL is deprecated
It is superseeded by a dedicated "exit_signal" argument in struct
clone_args freeing up space for additional flags.
This is based on a suggestion from Andrei and Linus (cf. [9] and [10])
/* References */
[1]: b3e5838252
[2]: https://dxr.mozilla.org/mozilla-central/source/security/sandbox/linux/SandboxFilter.cpp#343
[3]: https://git.musl-libc.org/cgit/musl/tree/src/thread/pthread_create.c#n233
[4]: https://sources.debian.org/src/blcr/0.8.5-2.3/cr_module/cr_dump_self.c/?hl=740#L740
[5]: https://lore.kernel.org/lkml/20190425161416.26600-1-dima@arista.com/
[6]: https://lore.kernel.org/lkml/20190425161416.26600-2-dima@arista.com/
[7]: https://lore.kernel.org/lkml/CAHrFyr5HxpGXA2YrKza-oB-GGwJCqwPfyhD-Y5wbktWZdt0sGQ@mail.gmail.com/
[8]: https://lore.kernel.org/lkml/20190524102756.qjsjxukuq2f4t6bo@brauner.io/
[9]: https://lore.kernel.org/lkml/20190529222414.GA6492@gmail.com/
[10]: https://lore.kernel.org/lkml/CAHk-=whQP-Ykxi=zSYaV9iXsHsENa+2fdj-zYKwyeyed63Lsfw@mail.gmail.com/
[11]: https://lore.kernel.org/lkml/CAHk-=wieuV4hGwznPsX-8E0G2FKhx3NjZ9X3dTKh5zKd+iqOBw@mail.gmail.com/
Suggested-by: Linus Torvalds <torvalds@linux-foundation.org>
Signed-off-by: Christian Brauner <christian@brauner.io>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Serge Hallyn <serge@hallyn.com>
Cc: Kees Cook <keescook@chromium.org>
Cc: Pavel Emelyanov <xemul@virtuozzo.com>
Cc: Jann Horn <jannh@google.com>
Cc: David Howells <dhowells@redhat.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Cc: Adrian Reber <adrian@lisas.de>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Andrei Vagin <avagin@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Cc: Florian Weimer <fweimer@redhat.com>
Cc: linux-api@vger.kernel.org
251 lines
7.2 KiB
C
251 lines
7.2 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* sys_ia32.c: Conversion between 32bit and 64bit native syscalls. Based on
|
|
* sys_sparc32
|
|
*
|
|
* Copyright (C) 2000 VA Linux Co
|
|
* Copyright (C) 2000 Don Dugger <n0ano@valinux.com>
|
|
* Copyright (C) 1999 Arun Sharma <arun.sharma@intel.com>
|
|
* Copyright (C) 1997,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
|
|
* Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu)
|
|
* Copyright (C) 2000 Hewlett-Packard Co.
|
|
* Copyright (C) 2000 David Mosberger-Tang <davidm@hpl.hp.com>
|
|
* Copyright (C) 2000,2001,2002 Andi Kleen, SuSE Labs (x86-64 port)
|
|
*
|
|
* These routines maintain argument size conversion between 32bit and 64bit
|
|
* environment. In 2.5 most of this should be moved to a generic directory.
|
|
*
|
|
* This file assumes that there is a hole at the end of user address space.
|
|
*
|
|
* Some of the functions are LE specific currently. These are
|
|
* hopefully all marked. This should be fixed.
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
#include <linux/sched.h>
|
|
#include <linux/fs.h>
|
|
#include <linux/file.h>
|
|
#include <linux/signal.h>
|
|
#include <linux/syscalls.h>
|
|
#include <linux/times.h>
|
|
#include <linux/utsname.h>
|
|
#include <linux/mm.h>
|
|
#include <linux/uio.h>
|
|
#include <linux/poll.h>
|
|
#include <linux/personality.h>
|
|
#include <linux/stat.h>
|
|
#include <linux/rwsem.h>
|
|
#include <linux/compat.h>
|
|
#include <linux/vfs.h>
|
|
#include <linux/ptrace.h>
|
|
#include <linux/highuid.h>
|
|
#include <linux/sysctl.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/sched/task.h>
|
|
#include <asm/mman.h>
|
|
#include <asm/types.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/atomic.h>
|
|
#include <asm/vgtod.h>
|
|
#include <asm/ia32.h>
|
|
|
|
#define AA(__x) ((unsigned long)(__x))
|
|
|
|
|
|
COMPAT_SYSCALL_DEFINE3(x86_truncate64, const char __user *, filename,
|
|
unsigned long, offset_low, unsigned long, offset_high)
|
|
{
|
|
return ksys_truncate(filename,
|
|
((loff_t) offset_high << 32) | offset_low);
|
|
}
|
|
|
|
COMPAT_SYSCALL_DEFINE3(x86_ftruncate64, unsigned int, fd,
|
|
unsigned long, offset_low, unsigned long, offset_high)
|
|
{
|
|
return ksys_ftruncate(fd, ((loff_t) offset_high << 32) | offset_low);
|
|
}
|
|
|
|
/*
|
|
* Another set for IA32/LFS -- x86_64 struct stat is different due to
|
|
* support for 64bit inode numbers.
|
|
*/
|
|
static int cp_stat64(struct stat64 __user *ubuf, struct kstat *stat)
|
|
{
|
|
typeof(ubuf->st_uid) uid = 0;
|
|
typeof(ubuf->st_gid) gid = 0;
|
|
SET_UID(uid, from_kuid_munged(current_user_ns(), stat->uid));
|
|
SET_GID(gid, from_kgid_munged(current_user_ns(), stat->gid));
|
|
if (!access_ok(ubuf, sizeof(struct stat64)) ||
|
|
__put_user(huge_encode_dev(stat->dev), &ubuf->st_dev) ||
|
|
__put_user(stat->ino, &ubuf->__st_ino) ||
|
|
__put_user(stat->ino, &ubuf->st_ino) ||
|
|
__put_user(stat->mode, &ubuf->st_mode) ||
|
|
__put_user(stat->nlink, &ubuf->st_nlink) ||
|
|
__put_user(uid, &ubuf->st_uid) ||
|
|
__put_user(gid, &ubuf->st_gid) ||
|
|
__put_user(huge_encode_dev(stat->rdev), &ubuf->st_rdev) ||
|
|
__put_user(stat->size, &ubuf->st_size) ||
|
|
__put_user(stat->atime.tv_sec, &ubuf->st_atime) ||
|
|
__put_user(stat->atime.tv_nsec, &ubuf->st_atime_nsec) ||
|
|
__put_user(stat->mtime.tv_sec, &ubuf->st_mtime) ||
|
|
__put_user(stat->mtime.tv_nsec, &ubuf->st_mtime_nsec) ||
|
|
__put_user(stat->ctime.tv_sec, &ubuf->st_ctime) ||
|
|
__put_user(stat->ctime.tv_nsec, &ubuf->st_ctime_nsec) ||
|
|
__put_user(stat->blksize, &ubuf->st_blksize) ||
|
|
__put_user(stat->blocks, &ubuf->st_blocks))
|
|
return -EFAULT;
|
|
return 0;
|
|
}
|
|
|
|
COMPAT_SYSCALL_DEFINE2(x86_stat64, const char __user *, filename,
|
|
struct stat64 __user *, statbuf)
|
|
{
|
|
struct kstat stat;
|
|
int ret = vfs_stat(filename, &stat);
|
|
|
|
if (!ret)
|
|
ret = cp_stat64(statbuf, &stat);
|
|
return ret;
|
|
}
|
|
|
|
COMPAT_SYSCALL_DEFINE2(x86_lstat64, const char __user *, filename,
|
|
struct stat64 __user *, statbuf)
|
|
{
|
|
struct kstat stat;
|
|
int ret = vfs_lstat(filename, &stat);
|
|
if (!ret)
|
|
ret = cp_stat64(statbuf, &stat);
|
|
return ret;
|
|
}
|
|
|
|
COMPAT_SYSCALL_DEFINE2(x86_fstat64, unsigned int, fd,
|
|
struct stat64 __user *, statbuf)
|
|
{
|
|
struct kstat stat;
|
|
int ret = vfs_fstat(fd, &stat);
|
|
if (!ret)
|
|
ret = cp_stat64(statbuf, &stat);
|
|
return ret;
|
|
}
|
|
|
|
COMPAT_SYSCALL_DEFINE4(x86_fstatat, unsigned int, dfd,
|
|
const char __user *, filename,
|
|
struct stat64 __user *, statbuf, int, flag)
|
|
{
|
|
struct kstat stat;
|
|
int error;
|
|
|
|
error = vfs_fstatat(dfd, filename, &stat, flag);
|
|
if (error)
|
|
return error;
|
|
return cp_stat64(statbuf, &stat);
|
|
}
|
|
|
|
/*
|
|
* Linux/i386 didn't use to be able to handle more than
|
|
* 4 system call parameters, so these system calls used a memory
|
|
* block for parameter passing..
|
|
*/
|
|
|
|
struct mmap_arg_struct32 {
|
|
unsigned int addr;
|
|
unsigned int len;
|
|
unsigned int prot;
|
|
unsigned int flags;
|
|
unsigned int fd;
|
|
unsigned int offset;
|
|
};
|
|
|
|
COMPAT_SYSCALL_DEFINE1(x86_mmap, struct mmap_arg_struct32 __user *, arg)
|
|
{
|
|
struct mmap_arg_struct32 a;
|
|
|
|
if (copy_from_user(&a, arg, sizeof(a)))
|
|
return -EFAULT;
|
|
|
|
if (a.offset & ~PAGE_MASK)
|
|
return -EINVAL;
|
|
|
|
return ksys_mmap_pgoff(a.addr, a.len, a.prot, a.flags, a.fd,
|
|
a.offset>>PAGE_SHIFT);
|
|
}
|
|
|
|
/* warning: next two assume little endian */
|
|
COMPAT_SYSCALL_DEFINE5(x86_pread, unsigned int, fd, char __user *, ubuf,
|
|
u32, count, u32, poslo, u32, poshi)
|
|
{
|
|
return ksys_pread64(fd, ubuf, count,
|
|
((loff_t)AA(poshi) << 32) | AA(poslo));
|
|
}
|
|
|
|
COMPAT_SYSCALL_DEFINE5(x86_pwrite, unsigned int, fd, const char __user *, ubuf,
|
|
u32, count, u32, poslo, u32, poshi)
|
|
{
|
|
return ksys_pwrite64(fd, ubuf, count,
|
|
((loff_t)AA(poshi) << 32) | AA(poslo));
|
|
}
|
|
|
|
|
|
/*
|
|
* Some system calls that need sign extended arguments. This could be
|
|
* done by a generic wrapper.
|
|
*/
|
|
COMPAT_SYSCALL_DEFINE6(x86_fadvise64_64, int, fd, __u32, offset_low,
|
|
__u32, offset_high, __u32, len_low, __u32, len_high,
|
|
int, advice)
|
|
{
|
|
return ksys_fadvise64_64(fd,
|
|
(((u64)offset_high)<<32) | offset_low,
|
|
(((u64)len_high)<<32) | len_low,
|
|
advice);
|
|
}
|
|
|
|
COMPAT_SYSCALL_DEFINE4(x86_readahead, int, fd, unsigned int, off_lo,
|
|
unsigned int, off_hi, size_t, count)
|
|
{
|
|
return ksys_readahead(fd, ((u64)off_hi << 32) | off_lo, count);
|
|
}
|
|
|
|
COMPAT_SYSCALL_DEFINE6(x86_sync_file_range, int, fd, unsigned int, off_low,
|
|
unsigned int, off_hi, unsigned int, n_low,
|
|
unsigned int, n_hi, int, flags)
|
|
{
|
|
return ksys_sync_file_range(fd,
|
|
((u64)off_hi << 32) | off_low,
|
|
((u64)n_hi << 32) | n_low, flags);
|
|
}
|
|
|
|
COMPAT_SYSCALL_DEFINE5(x86_fadvise64, int, fd, unsigned int, offset_lo,
|
|
unsigned int, offset_hi, size_t, len, int, advice)
|
|
{
|
|
return ksys_fadvise64_64(fd, ((u64)offset_hi << 32) | offset_lo,
|
|
len, advice);
|
|
}
|
|
|
|
COMPAT_SYSCALL_DEFINE6(x86_fallocate, int, fd, int, mode,
|
|
unsigned int, offset_lo, unsigned int, offset_hi,
|
|
unsigned int, len_lo, unsigned int, len_hi)
|
|
{
|
|
return ksys_fallocate(fd, mode, ((u64)offset_hi << 32) | offset_lo,
|
|
((u64)len_hi << 32) | len_lo);
|
|
}
|
|
|
|
/*
|
|
* The 32-bit clone ABI is CONFIG_CLONE_BACKWARDS
|
|
*/
|
|
COMPAT_SYSCALL_DEFINE5(x86_clone, unsigned long, clone_flags,
|
|
unsigned long, newsp, int __user *, parent_tidptr,
|
|
unsigned long, tls_val, int __user *, child_tidptr)
|
|
{
|
|
struct kernel_clone_args args = {
|
|
.flags = (clone_flags & ~CSIGNAL),
|
|
.child_tid = child_tidptr,
|
|
.parent_tid = parent_tidptr,
|
|
.exit_signal = (clone_flags & CSIGNAL),
|
|
.stack = newsp,
|
|
.tls = tls_val,
|
|
};
|
|
|
|
return _do_fork(&args);
|
|
}
|