From c24bddd5aa7d8eee84467fc0a94c93a9f901a56f Mon Sep 17 00:00:00 2001 From: borysp Date: Wed, 30 Dec 2020 15:33:59 +0100 Subject: [PATCH] [LibOS] Rework signal handling and syscall emulation Change log (most important only): - unify CPU context structures - now we have only one version - `PAL_CONTEXT` - which is shared between LibOS and PALs and it should depend only on the host architecture (not OS), - syscalls emulation changed: - dedicated LibOS stack is now used for syscalls emulation, - removed one indirection level in syscalls table - now it stores `shim_do_*` functions directly, - signal handling - completely rewritten: - all signal queues use proper locking schemes now, - signals are handled *only* when returning to the user app from LibOS or PAL, - nested signals are now possible, - the app is allowed to jump out of signal handler with the same sematics as on normal Linux, - signal altstack is now fully supported, - syscall restarting is now supported, - doing a backtrace from the signal handler works properly, - disallow injecting host-level signals, with one exception, see `sys.enable_sigterm_injection` manifest option for more details. --- Documentation/devel/new-syscall.rst | 47 +- Documentation/libos/shim-init.rst | 2 +- Documentation/manifest-syntax.rst | 14 + Documentation/pal/host-abi.rst | 2 +- Examples/apache/httpd.manifest.template | 3 + Examples/lighttpd/lighttpd.manifest.template | 3 + .../memcached/memcached.manifest.template | 5 + Examples/nginx/nginx.manifest.template | 3 + .../nodejs.manifest.template | 3 + .../python-simple/python.manifest.template | 3 + Examples/r/R.manifest.template | 3 + .../ra-tls-mbedtls/server.manifest.template | 3 + Examples/redis/redis-server.manifest.template | 5 + .../libgomp-replace-futex-instruction.patch | 29 +- LibOS/glibc-patches/glibc-2.23.patch | 6 +- LibOS/glibc-patches/glibc-2.27.patch | 6 +- LibOS/glibc-patches/glibc-2.31.patch | 10 +- LibOS/glibc-patches/syscalldb-api.patch | 63 +- .../shim/include/arch/x86_64/shim_tcb-arch.h | 60 +- .../include/arch/x86_64/shim_ucontext-arch.h | 35 - LibOS/shim/include/shim_context.h | 14 +- LibOS/shim/include/shim_defs.h | 8 + LibOS/shim/include/shim_internal.h | 330 ++---- LibOS/shim/include/shim_lock.h | 7 - LibOS/shim/include/shim_signal.h | 39 +- LibOS/shim/include/shim_table.h | 356 +----- LibOS/shim/include/shim_tcb.h | 37 +- LibOS/shim/include/shim_thread.h | 56 +- LibOS/shim/src/Makefile | 3 + LibOS/shim/src/bookkeep/shim_signal.c | 978 ++++++++-------- LibOS/shim/src/bookkeep/shim_thread.c | 90 +- LibOS/shim/src/bookkeep/shim_vma.c | 56 +- LibOS/shim/src/elf/shim_rtld.c | 32 +- LibOS/shim/src/generated-offsets.c | 18 +- LibOS/shim/src/ipc/shim_ipc.c | 2 +- LibOS/shim/src/ipc/shim_ipc_helper.c | 2 - LibOS/shim/src/shim_arch_prctl-x86_64.c | 28 + LibOS/shim/src/shim_async.c | 2 - LibOS/shim/src/shim_context-x86_64.c | 290 +++-- LibOS/shim/src/shim_init.c | 18 +- LibOS/shim/src/shim_malloc.c | 3 - LibOS/shim/src/shim_syscalls.c | 1015 +---------------- LibOS/shim/src/shim_table-x86_64.c | 685 ++++++----- LibOS/shim/src/sys/shim_clone.c | 75 +- LibOS/shim/src/sys/shim_exec.c | 13 +- LibOS/shim/src/sys/shim_exit.c | 18 +- LibOS/shim/src/sys/shim_futex.c | 92 +- LibOS/shim/src/sys/shim_sigaction.c | 198 ++-- LibOS/shim/src/sys/shim_wait.c | 50 +- LibOS/shim/src/syscallas-x86_64.S | 378 +++--- .../shim/src/vdso/arch/x86_64/vdso_syscall.h | 21 + LibOS/shim/src/vdso/vdso-x86_64.lds | 5 +- LibOS/shim/src/vdso/vdso.c | 33 +- LibOS/shim/test/ltp/ltp.cfg | 5 - .../test/regression/openmp.manifest.template | 3 +- Pal/include/arch/x86_64/Linux/ucontext.h | 307 ++--- Pal/include/arch/x86_64/pal-arch.h | 86 +- Pal/include/lib/spinlock.h | 12 - Pal/include/pal/pal.h | 27 +- Pal/regression/Bootstrap.c | 2 - Pal/regression/Event.c | 5 +- Pal/regression/Exception.c | 17 +- Pal/regression/Exception2.c | 6 +- Pal/regression/Failure.c | 6 +- Pal/regression/Memory.c | 6 +- Pal/src/host/Linux-SGX/db_exception.c | 30 +- Pal/src/host/Linux-SGX/sgx_exception.c | 17 +- Pal/src/host/Linux/db_exception.c | 95 +- Pal/src/host/Linux/pal_host.h | 6 +- Pal/src/host/Linux/pal_linux.h | 4 - Pal/src/host/Skeleton/db_exception.c | 3 +- Scripts/Makefile.rules | 2 +- 72 files changed, 2307 insertions(+), 3589 deletions(-) delete mode 100644 LibOS/shim/include/arch/x86_64/shim_ucontext-arch.h create mode 100644 LibOS/shim/src/shim_arch_prctl-x86_64.c create mode 100644 LibOS/shim/src/vdso/arch/x86_64/vdso_syscall.h diff --git a/Documentation/devel/new-syscall.rst b/Documentation/devel/new-syscall.rst index c475c5fa..648bc123 100644 --- a/Documentation/devel/new-syscall.rst +++ b/Documentation/devel/new-syscall.rst @@ -3,41 +3,26 @@ Implementing new system call .. highlight:: c -1. Define interface of system call ----------------------------------- +1. Define interface of system call and add it to system call table +------------------------------------------------------------------ For example, assume we are implementing :manpage:`sched_setaffinity(2)`. You -must find the definition of ``sched_setaffinity`` in -:file:`shim_syscalls.c`, which will be the following code:: - - SHIM_SYSCALL_RETURN_ENOSYS(sched_setaffinity, 3, long, pid_t, pid, unsigned int, - len, unsigned long*, user_mask_ptr) - -Change this line to ``DEFINE_SHIM_SYSCALL(...)`` to name the function that -implements this system call: ``shim_do_sched_setaffinity`` (this is the naming -convention, please follow it):: - - DEFINE_SHIM_SYSCALL(sched_setaffinity, 3, shim_do_sched_setaffinity, long, pid_t, pid, - unsigned int, len, unsigned long*, user_mask_ptr) - - -2. Add definitions to system call table ---------------------------------------- - -To implement system call ``sched_setaffinity``, two functions need to be defined -in :file:`shim_table.h`: ``__shim_sched_setaffinity`` and -``shim_do_sched_setaffinity``. The first one should already be defined. Add the -second in respect to the system call you are implementing, with the same -prototype as defined in :file:`shim_syscalls.c`:: +must add the prototype of the function implementing it to :file:`shim_table.h`:: long shim_do_sched_setaffinity(pid_t pid, unsigned int len, unsigned long* user_mask_ptr); -3. Implement system call +Note that we use the following naming convetion: ``shim_do_`` followed by +an actual syscall name. Additionally this function should return ``long``. +Now you need to add an appropriate entry in the syscalls table in +:file:`shim_table-$(ARCH).c`:: + + [__NR_sched_setaffinity] = (shim_fp)shim_do_sched_setaffinity + +2. Implement system call ------------------------ -You can add the function body of ``shim_do_sysinfo`` (or the function name defined -earlier) in a new source file or any existing source file in -:file:`LibOS/shim/src/sys`. +You can add the function body of ``shim_do_sched_setaffinity`` in a new source +file or any existing source file in :file:`LibOS/shim/src/sys`. For example, in :file:`LibOS/shim/src/sys/shim_sched.c`:: @@ -45,7 +30,7 @@ For example, in :file:`LibOS/shim/src/sys/shim_sched.c`:: /* code for implementing the semantics of sched_setaffinity */ } -4. Add new PAL Calls (optional) +3. Add new PAL Calls (optional) ------------------------------- The concept of Graphene library OS is to keep the PAL interface as simple as @@ -66,14 +51,14 @@ Make sure you use the PAL-specific data types, including :type:`PAL_BOL`, with the ``Dk`` prefix, followed by a comprehensive name describing the purpose of the PAL call. -5. Export new PAL calls from PAL binaries (optional) +4. Export new PAL calls from PAL binaries (optional) ---------------------------------------------------- For each directory in :file:`PAL/host/`, there is a :file:`pal.map` file. This file lists all the symbols accessible to the library OS. The new PAL call needs to be listed here in order to be used by your system call implementation. -6. Implement new PAL calls (optional) +5. Implement new PAL calls (optional) ------------------------------------- .. todo:: diff --git a/Documentation/libos/shim-init.rst b/Documentation/libos/shim-init.rst index 3370a705..6e72c67b 100644 --- a/Documentation/libos/shim-init.rst +++ b/Documentation/libos/shim-init.rst @@ -7,5 +7,5 @@ LibOS documentation There is a |~| random function: -.. doxygenfunction:: handle_signals +.. doxygenfunction:: object_wait_with_retry :project: libos diff --git a/Documentation/manifest-syntax.rst b/Documentation/manifest-syntax.rst index 5ecf12e7..f6d986b0 100644 --- a/Documentation/manifest-syntax.rst +++ b/Documentation/manifest-syntax.rst @@ -231,6 +231,20 @@ This specifies whether to allow system calls `eventfd()` and `eventfd2()`. Since eventfd emulation currently relies on the host, these system calls are disallowed by default due to security concerns. +External SIGTERM injection +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + sys.enable_sigterm_injection = [1|0] + (Default: 0) + +This specifies whether to allow for a one-time injection of `SIGTERM` signal +into Graphene. Could be useful to handle graceful shutdown. +Be careful! In SGX environment, the untrusted host could inject that signal in +an arbitrary moment. Examine what your application's `SIGTERM` handler does and +whether it poses any security threat. + Root FS mount point ^^^^^^^^^^^^^^^^^^^ diff --git a/Documentation/pal/host-abi.rst b/Documentation/pal/host-abi.rst index c52264f5..f873b814 100644 --- a/Documentation/pal/host-abi.rst +++ b/Documentation/pal/host-abi.rst @@ -301,7 +301,7 @@ Exception handling .. doxygentypedef:: PAL_CONTEXT :project: pal -.. doxygenstruct:: PAL_CONTEXT_ +.. doxygenstruct:: PAL_CONTEXT :project: pal :members: diff --git a/Examples/apache/httpd.manifest.template b/Examples/apache/httpd.manifest.template index 56368960..56960189 100644 --- a/Examples/apache/httpd.manifest.template +++ b/Examples/apache/httpd.manifest.template @@ -14,6 +14,9 @@ loader.log_level = "$(GRAPHENE_LOG_LEVEL)" # Environment variables loader.env.LD_LIBRARY_PATH = "/lib:$(ARCH_LIBDIR):/usr/lib:/usr/$(ARCH_LIBDIR)" +# Allow for injecting SIGTERM signal from the host. +sys.enable_sigterm_injection = 1 + # Mounted FSes. The following "chroot" FSes mount a part of the host FS into the # guest. Other parts of the host FS will not be available in the guest. diff --git a/Examples/lighttpd/lighttpd.manifest.template b/Examples/lighttpd/lighttpd.manifest.template index ac90b9a2..1d83e4bd 100644 --- a/Examples/lighttpd/lighttpd.manifest.template +++ b/Examples/lighttpd/lighttpd.manifest.template @@ -20,6 +20,9 @@ loader.insecure__use_cmdline_argv = 1 # Environment variables for lighttpd loader.env.LD_LIBRARY_PATH = "/lib:$(ARCH_LIBDIR):$(INSTALL_DIR)/lib" +# Allow for injecting SIGTERM signal from the host. +sys.enable_sigterm_injection = 1 + # Mounted FSes. The following "chroot" FSes mount a part of the host FS into the # guest. Other parts of the host FS will not be available in the guest. diff --git a/Examples/memcached/memcached.manifest.template b/Examples/memcached/memcached.manifest.template index bbfd137e..1e14af09 100644 --- a/Examples/memcached/memcached.manifest.template +++ b/Examples/memcached/memcached.manifest.template @@ -53,6 +53,11 @@ loader.env.LD_LIBRARY_PATH = "/lib:$(ARCH_LIBDIR):/usr/$(ARCH_LIBDIR)" # All other environment variables are unset. This is the default Graphene # behavior. Memcached doesn't require any environment variables to be set. +################################## SIGNALS #################################### + +# Allow for injecting SIGTERM signal from the host. +sys.enable_sigterm_injection = 1 + ################################# MOUNT FS ################################### # General notes: diff --git a/Examples/nginx/nginx.manifest.template b/Examples/nginx/nginx.manifest.template index 6fab1d2d..f7c302b9 100644 --- a/Examples/nginx/nginx.manifest.template +++ b/Examples/nginx/nginx.manifest.template @@ -16,6 +16,9 @@ loader.insecure__use_cmdline_argv = 1 # Environment variables loader.env.LD_LIBRARY_PATH = "/lib:$(ARCH_LIBDIR):/usr/local/lib:/usr/lib:/usr/$(ARCH_LIBDIR)" +# Allow for injecting SIGTERM signal from the host. +sys.enable_sigterm_injection = 1 + # Mounted FSes. The following "chroot" FSes mount a part of the host FS into the # guest. Other parts of the host FS will not be available in the guest. diff --git a/Examples/nodejs-express-server/nodejs.manifest.template b/Examples/nodejs-express-server/nodejs.manifest.template index 8fe073f5..24a1dbfe 100644 --- a/Examples/nodejs-express-server/nodejs.manifest.template +++ b/Examples/nodejs-express-server/nodejs.manifest.template @@ -19,6 +19,9 @@ loader.insecure__use_cmdline_argv = 1 # fs.mount.xxx.uri). loader.env.LD_LIBRARY_PATH = "/lib:$(ARCH_LIBDIR):/usr/$(ARCH_LIBDIR)" +# Allow for injecting SIGTERM signal from the host. +sys.enable_sigterm_injection = 1 + # Mount host-OS directory to required libraries (in 'uri') into in-Graphene visible directory /lib # (in 'path'). fs.mount.lib.type = "chroot" diff --git a/Examples/python-simple/python.manifest.template b/Examples/python-simple/python.manifest.template index 4003888c..2adcd970 100644 --- a/Examples/python-simple/python.manifest.template +++ b/Examples/python-simple/python.manifest.template @@ -21,6 +21,9 @@ loader.env.PYTHONHOME = "$(PYTHONHOME)" loader.env.PYTHONPATH = "$(PYTHONHOME):$(PYTHONHOME)/plat-$(ARCH_LONG):$(PYTHONDISTHOME):$(PYTHONHOME)/lib-dynload" loader.env.HOME = "/home/user" +# Allow for injecting SIGTERM signal from the host. +sys.enable_sigterm_injection = 1 + # Mounted FSes. The following "chroot" FSes mount a part of the host FS into the # guest. Other parts of the host FS will not be available in the guest. diff --git a/Examples/r/R.manifest.template b/Examples/r/R.manifest.template index c80191cd..d852cb3a 100644 --- a/Examples/r/R.manifest.template +++ b/Examples/r/R.manifest.template @@ -13,6 +13,9 @@ loader.log_level = "$(GRAPHENE_LOG_LEVEL)" # Read application arguments directly from the command line. Don't use this on production! loader.insecure__use_cmdline_argv = 1 +# Allow for injecting SIGTERM signal from the host. +sys.enable_sigterm_injection = 1 + # Environment variables for R loader.env.LD_LIBRARY_PATH = "$(R_HOME)/lib:/lib:$(ARCH_LIBDIR):/usr/lib:/usr/$(ARCH_LIBDIR)" loader.env.PATH = "$(R_HOME)/bin:/usr/bin:/bin" diff --git a/Examples/ra-tls-mbedtls/server.manifest.template b/Examples/ra-tls-mbedtls/server.manifest.template index 0b20ea36..82a4b1c4 100644 --- a/Examples/ra-tls-mbedtls/server.manifest.template +++ b/Examples/ra-tls-mbedtls/server.manifest.template @@ -11,6 +11,9 @@ loader.env.LD_LIBRARY_PATH = "/lib:/lib/x86_64-linux-gnu" # Read application arguments directly from the command line. Don't use this on production! loader.insecure__use_cmdline_argv = 1 +# Allow for injecting SIGTERM signal from the host. +sys.enable_sigterm_injection = 1 + # Request remote attestation functionality from Graphene sgx.remote_attestation = 1 diff --git a/Examples/redis/redis-server.manifest.template b/Examples/redis/redis-server.manifest.template index 7ecd5ab8..cb75a25c 100644 --- a/Examples/redis/redis-server.manifest.template +++ b/Examples/redis/redis-server.manifest.template @@ -33,6 +33,11 @@ loader.insecure__use_cmdline_argv = 1 # - $(ARCH_LIBDIR) is searched for Name Service Switch (NSS) libraries loader.env.LD_LIBRARY_PATH = "/lib:$(ARCH_LIBDIR):/usr/$(ARCH_LIBDIR)" +################################## SIGNALS #################################### + +# Allow for injecting SIGTERM signal from the host. +sys.enable_sigterm_injection = 1 + ################################# MOUNT FS ################################### # General notes: diff --git a/LibOS/gcc-patches/libgomp-replace-futex-instruction.patch b/LibOS/gcc-patches/libgomp-replace-futex-instruction.patch index c964ab90..df2e486e 100644 --- a/LibOS/gcc-patches/libgomp-replace-futex-instruction.patch +++ b/LibOS/gcc-patches/libgomp-replace-futex-instruction.patch @@ -1,13 +1,20 @@ diff --git a/libgomp/config/linux/x86/futex.h b/libgomp/config/linux/x86/futex.h -index ead74d1496736a49694ef6b9b2b4da50f9852664..3c82859ad8b82a09ec95f720727937ee5a2863c1 100644 +index ead74d1496736a49694ef6b9b2b4da50f9852664..917412326135b9e9da3e14da3765868d217f1ed3 100644 --- a/libgomp/config/linux/x86/futex.h +++ b/libgomp/config/linux/x86/futex.h -@@ -30,13 +30,16 @@ +@@ -30,13 +30,23 @@ # define SYS_futex 202 # endif -+asm (".weak syscalldb\r\n" -+ ".type syscalldb, @function\r\n"); ++asm( ++".weak syscalldb\n" ++".type syscalldb, @function\n" ++".macro SYSCALLDB\n" ++"leaq .Lafter_syscalldb\\@(%rip), %rcx\n" ++"jmpq *syscalldb@GOTPCREL(%rip)\n" ++".Lafter_syscalldb\\@:\n" ++".endm\n" ++); + static inline void futex_wait (int *addr, int val) @@ -16,34 +23,34 @@ index ead74d1496736a49694ef6b9b2b4da50f9852664..3c82859ad8b82a09ec95f720727937ee register long r10 __asm__("%r10") = 0; - __asm volatile ("syscall" -+ __asm volatile ("subq $128, %%rsp; callq *syscalldb@GOTPCREL(%%rip); addq $128, %%rsp;" ++ __asm volatile ("SYSCALLDB" : "=a" (res) : "0" (SYS_futex), "D" (addr), "S" (gomp_futex_wait), "d" (val), "r" (r10) -@@ -45,7 +48,7 @@ futex_wait (int *addr, int val) +@@ -45,7 +55,7 @@ futex_wait (int *addr, int val) { gomp_futex_wait &= ~FUTEX_PRIVATE_FLAG; gomp_futex_wake &= ~FUTEX_PRIVATE_FLAG; - __asm volatile ("syscall" -+ __asm volatile ("subq $128, %%rsp; callq *syscalldb@GOTPCREL(%%rip); addq $128, %%rsp;" ++ __asm volatile ("SYSCALLDB" : "=a" (res) : "0" (SYS_futex), "D" (addr), "S" (gomp_futex_wait), "d" (val), "r" (r10) -@@ -58,7 +61,7 @@ futex_wake (int *addr, int count) +@@ -58,7 +68,7 @@ futex_wake (int *addr, int count) { long res; - __asm volatile ("syscall" -+ __asm volatile ("subq $128, %%rsp; callq *syscalldb@GOTPCREL(%%rip); addq $128, %%rsp;" ++ __asm volatile ("SYSCALLDB" : "=a" (res) : "0" (SYS_futex), "D" (addr), "S" (gomp_futex_wake), "d" (count) -@@ -67,7 +70,7 @@ futex_wake (int *addr, int count) +@@ -67,7 +77,7 @@ futex_wake (int *addr, int count) { gomp_futex_wait &= ~FUTEX_PRIVATE_FLAG; gomp_futex_wake &= ~FUTEX_PRIVATE_FLAG; - __asm volatile ("syscall" -+ __asm volatile ("subq $128, %%rsp; callq *syscalldb@GOTPCREL(%%rip); addq $128, %%rsp;" ++ __asm volatile ("SYSCALLDB" : "=a" (res) : "0" (SYS_futex), "D" (addr), "S" (gomp_futex_wake), "d" (count) diff --git a/LibOS/glibc-patches/glibc-2.23.patch b/LibOS/glibc-patches/glibc-2.23.patch index efdb1926..b64c82ba 100644 --- a/LibOS/glibc-patches/glibc-2.23.patch +++ b/LibOS/glibc-patches/glibc-2.23.patch @@ -1,5 +1,5 @@ diff --git a/Makeconfig b/Makeconfig -index 87a22e88bed375d073663063dd4a93444d72ba25..b69ff41077a5f279bde52aac11f03604fa61ae65 100644 +index 87a22e88bed375d073663063dd4a93444d72ba25..3e2cf93fad804b81e67a476398a9c86c93ffa3cd 100644 --- a/Makeconfig +++ b/Makeconfig @@ -841,7 +841,8 @@ endif # $(+cflags) == "" @@ -482,7 +482,7 @@ index 56e085685804f51e49eef4c21ad9efdf0d21e59e..81038f485616592419c5fb2f63c0c887 cfi_adjust_cfa_offset(-8) cmpq $-4095, %rax /* Check %rax for error. */ diff --git a/sysdeps/unix/sysv/linux/x86_64/sigaction.c b/sysdeps/unix/sysv/linux/x86_64/sigaction.c -index 71ac05c4bc01c560935f3bbd4306e3aeacb6d9be..c6e28bb2362b07cfa3f3ade189ae337195671830 100644 +index 71ac05c4bc01c560935f3bbd4306e3aeacb6d9be..2f5e622a88b40f5f175455fe64b254b70b36ff25 100644 --- a/sysdeps/unix/sysv/linux/x86_64/sigaction.c +++ b/sysdeps/unix/sysv/linux/x86_64/sigaction.c @@ -120,7 +120,7 @@ asm \ @@ -490,7 +490,7 @@ index 71ac05c4bc01c560935f3bbd4306e3aeacb6d9be..c6e28bb2362b07cfa3f3ade189ae3371 "__" #name ":\n" \ " movq $" #syscall ", %rax\n" \ - " syscall\n" \ -+ SYSCALLDB_ASM \ ++ SYSCALLDB \ ".LEND_" #name ":\n" \ ".section .eh_frame,\"a\",@progbits\n" \ ".LSTARTFRAME_" #name ":\n" \ diff --git a/LibOS/glibc-patches/glibc-2.27.patch b/LibOS/glibc-patches/glibc-2.27.patch index 551cc658..b160d80e 100644 --- a/LibOS/glibc-patches/glibc-2.27.patch +++ b/LibOS/glibc-patches/glibc-2.27.patch @@ -1,5 +1,5 @@ diff --git a/Makeconfig b/Makeconfig -index 86a71e580213f6e5de6e619d9f3370f4365b4ae2..114d4ee080dbccbb696f40469a8010663fec7a9a 100644 +index 86a71e580213f6e5de6e619d9f3370f4365b4ae2..515f8be0d13a14fe0588c357cfc5608503aca341 100644 --- a/Makeconfig +++ b/Makeconfig @@ -916,7 +916,8 @@ endif # $(+cflags) == "" @@ -290,7 +290,7 @@ index 4a9b662074ddbd62d958d8db7ec09b9d5741ad59..21bd14d778135102632952712a307453 cfi_adjust_cfa_offset(-8) cmpq $-4095, %rax /* Check %rax for error. */ diff --git a/sysdeps/unix/sysv/linux/x86_64/sigaction.c b/sysdeps/unix/sysv/linux/x86_64/sigaction.c -index 2f7459f6fc602d1a176f725bb2dd4f10364ae997..9c2191541be0b8855ee3789993004a03bf9eab0f 100644 +index 2f7459f6fc602d1a176f725bb2dd4f10364ae997..b4c35a8e494ce1a8a9faa6ffd517dc993465b8be 100644 --- a/sysdeps/unix/sysv/linux/x86_64/sigaction.c +++ b/sysdeps/unix/sysv/linux/x86_64/sigaction.c @@ -120,7 +120,7 @@ asm \ @@ -298,7 +298,7 @@ index 2f7459f6fc602d1a176f725bb2dd4f10364ae997..9c2191541be0b8855ee3789993004a03 "__" #name ":\n" \ " movq $" #syscall ", %rax\n" \ - " syscall\n" \ -+ SYSCALLDB_ASM \ ++ SYSCALLDB \ ".LEND_" #name ":\n" \ ".section .eh_frame,\"a\",@progbits\n" \ ".LSTARTFRAME_" #name ":\n" \ diff --git a/LibOS/glibc-patches/glibc-2.31.patch b/LibOS/glibc-patches/glibc-2.31.patch index 4113e6a9..8107981c 100644 --- a/LibOS/glibc-patches/glibc-2.31.patch +++ b/LibOS/glibc-patches/glibc-2.31.patch @@ -1,5 +1,5 @@ diff --git a/Makeconfig b/Makeconfig -index f252842979a1d777e0f0c2bdafa7a65aee0805cd..4e22deb595ad6e5dd36571aec01d5b1e39f10f16 100644 +index f252842979a1d777e0f0c2bdafa7a65aee0805cd..0a044a7b60ae305af56c47982e3bdf1e25769527 100644 --- a/Makeconfig +++ b/Makeconfig @@ -930,7 +930,8 @@ endif # $(+cflags) == "" @@ -246,7 +246,7 @@ index 31bbc9dbe4bec944bb0037d24900120535d08504..e1bfe2f13eb1a6f00e461973e09ef426 leaving RDI and RSI available for use later can avoid shuffling values. */ diff --git a/sysdeps/unix/sysv/linux/x86_64/sigaction.c b/sysdeps/unix/sysv/linux/x86_64/sigaction.c -index c58a77c5c6a3547a3bff5437c1a2b13368f2f48b..f1b413ea733966eb3e649bb30a49ffc3c65d2fa9 100644 +index c58a77c5c6a3547a3bff5437c1a2b13368f2f48b..fdaef663d6f9a69901a612970485d3673eb65e95 100644 --- a/sysdeps/unix/sysv/linux/x86_64/sigaction.c +++ b/sysdeps/unix/sysv/linux/x86_64/sigaction.c @@ -78,7 +78,7 @@ asm \ @@ -254,7 +254,7 @@ index c58a77c5c6a3547a3bff5437c1a2b13368f2f48b..f1b413ea733966eb3e649bb30a49ffc3 "__" #name ":\n" \ " movq $" #syscall ", %rax\n" \ - " syscall\n" \ -+ SYSCALLDB_ASM \ ++ SYSCALLDB \ ".LEND_" #name ":\n" \ ".section .eh_frame,\"a\",@progbits\n" \ ".LSTARTFRAME_" #name ":\n" \ @@ -391,7 +391,7 @@ index 776d2fc610751b7efdcf5b39aa42728504b14080..9fc94470d650f3c80e9c0a4ec04c8754 #if !SHSTK_ENABLED /* Push back the return PC. */ diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/times.c b/sysdeps/unix/sysv/linux/x86_64/x32/times.c -index fb93cb609c09831cbcee1c0cc57ff7a6bbe12ff5..c36fd5264edc96398e49ac98bcf213b7f761f4d5 100644 +index fb93cb609c09831cbcee1c0cc57ff7a6bbe12ff5..380f0bc4ede8249215c423430d43566a011943c9 100644 --- a/sysdeps/unix/sysv/linux/x86_64/x32/times.c +++ b/sysdeps/unix/sysv/linux/x86_64/x32/times.c @@ -26,7 +26,7 @@ @@ -399,7 +399,7 @@ index fb93cb609c09831cbcee1c0cc57ff7a6bbe12ff5..c36fd5264edc96398e49ac98bcf213b7 register TYPEFY (arg1, _a1) asm ("rdi") = __arg1; \ asm volatile ( \ - "syscall\n\t" \ -+ SYSCALLDB_ASM \ ++ SYSCALLDB \ : "=a" (resultvar) \ : "0" (number), "r" (_a1) \ : "memory", REGISTERS_CLOBBERED_BY_SYSCALL); \ diff --git a/LibOS/glibc-patches/syscalldb-api.patch b/LibOS/glibc-patches/syscalldb-api.patch index 22aba88c..4d41358d 100644 --- a/LibOS/glibc-patches/syscalldb-api.patch +++ b/LibOS/glibc-patches/syscalldb-api.patch @@ -16,12 +16,10 @@ index 0000000000000000000000000000000000000000..61dca527ee3bd1027a81a28264d82a96 \ No newline at end of file diff --git a/syscallas.S b/syscallas.S new file mode 100644 -index 0000000000000000000000000000000000000000..07c6ffce0f3b1bc57e5b1ca26086a567dfbf5a41 +index 0000000000000000000000000000000000000000..d5141abe154f3f79d97cdc60b5b2dcdf499f629f --- /dev/null +++ b/syscallas.S -@@ -0,0 +1,11 @@ -+#include -+ +@@ -0,0 +1,9 @@ +.weak syscalldb +.type syscalldb,@function + @@ -50,47 +48,40 @@ index 0000000000000000000000000000000000000000..47b17de10f831f28b645686995c5df00 +} diff --git a/syscalldb.h b/syscalldb.h new file mode 100644 -index 0000000000000000000000000000000000000000..4914e280d8cd6a62ab1ea4c9aa41601763167e7d +index 0000000000000000000000000000000000000000..bceb6225d53e51f882b6068aa540c0620c50684e --- /dev/null +++ b/syscalldb.h -@@ -0,0 +1,37 @@ +@@ -0,0 +1,16 @@ +#ifndef _SYSCALLDB_H_ +#define _SYSCALLDB_H_ + +#ifdef __ASSEMBLER__ ++ ++.include "syscalldb_macro.S" ++ ++#else /* !__ASSEMBLER__ */ ++ ++asm (".include \"syscalldb_macro.S\""); ++ ++#define SYSCALLDB "SYSCALLDB\n" ++ ++#endif /* __ASSEMBLER__ */ ++ ++#endif /* _SYSCALLDB_H_ */ +diff --git a/syscalldb_macro.S b/syscalldb_macro.S +new file mode 100644 +index 0000000000000000000000000000000000000000..2570ab7c457d246c3d9f1940706c038fc6aaf637 +--- /dev/null ++++ b/syscalldb_macro.S +@@ -0,0 +1,8 @@ +.weak syscalldb +.type syscalldb, @function + -+# if defined(PSEUDO) && defined(SYSCALL_NAME) && defined(SYSCALL_SYMBOL) -+# define SYSCALLDB \ -+ subq $128, %rsp; \ -+ .cfi_adjust_cfa_offset 128; \ -+ callq *syscalldb@GOTPCREL(%rip); \ -+ addq $128, %rsp; \ -+ .cfi_adjust_cfa_offset -128 -+# else -+# define SYSCALLDB \ -+ callq *syscalldb@GOTPCREL(%rip) -+# endif -+ -+#else /* !__ASSEMBLER__ */ -+asm ( -+".weak syscalldb\r\n" -+".type syscalldb, @function\r\n"); -+ -+#define SYSCALLDB \ -+ "subq $128, %%rsp\n\t" \ -+ ".cfi_adjust_cfa_offset 128\n\t" \ -+ "callq *syscalldb@GOTPCREL(%%rip)\n\t" \ -+ "addq $128, %%rsp\n\t" \ -+ ".cfi_adjust_cfa_offset -128\n\t" -+ -+#define SYSCALLDB_ASM \ -+ "callq *syscalldb@GOTPCREL(%rip)\n\t" -+ -+#endif /* Assembler */ -+ -+#endif /* _SYSCALLDB_H */ ++.macro SYSCALLDB ++leaq .Lafter_syscalldb\@(%rip), %rcx ++jmpq *syscalldb@GOTPCREL(%rip) ++.Lafter_syscalldb\@: ++.endm diff --git a/sysdeps/unix/sysv/linux/x86_64/syscalldb.h b/sysdeps/unix/sysv/linux/x86_64/syscalldb.h new file mode 120000 index 0000000000000000000000000000000000000000..55ab305032a38796e4abc8ce7a208bdaefe8639e diff --git a/LibOS/shim/include/arch/x86_64/shim_tcb-arch.h b/LibOS/shim/include/arch/x86_64/shim_tcb-arch.h index c0ede8d2..c931d716 100644 --- a/LibOS/shim/include/arch/x86_64/shim_tcb-arch.h +++ b/LibOS/shim/include/arch/x86_64/shim_tcb-arch.h @@ -5,27 +5,6 @@ #include "pal.h" -struct shim_regs { - uint64_t orig_rax; - uint64_t rsp; - uint64_t r15; - uint64_t r14; - uint64_t r13; - uint64_t r12; - uint64_t r11; - uint64_t r10; - uint64_t r9; - uint64_t r8; - uint64_t rcx; - uint64_t rdx; - uint64_t rsi; - uint64_t rdi; - uint64_t rbx; - uint64_t rbp; - uint64_t rflags; - uint64_t rip; -}; - /* adopt Linux x86-64 structs for FP layout: self-contained definition is needed for LibOS, so * define the exact same layout with `shim_` prefix; taken from * https://elixir.bootlin.com/linux/v5.9/source/arch/x86/include/uapi/asm/sigcontext.h */ @@ -105,26 +84,6 @@ struct shim_xstate { /* rest is filled with extended regs (YMM, ZMM, ...) by HW + 4B of MAGIC2 value by SW */ } __attribute__((aligned(SHIM_XSTATE_ALIGN))); -static inline uint64_t shim_regs_get_sp(struct shim_regs* sr) { - return sr->rsp; -} - -static inline void shim_regs_set_sp(struct shim_regs* sr, uint64_t sp) { - sr->rsp = sp; -} - -static inline uint64_t shim_regs_get_ip(struct shim_regs* sr) { - return sr->rip; -} - -static inline uint64_t shim_regs_get_syscallnr(struct shim_regs* sr) { - return sr->orig_rax; -} - -static inline void shim_regs_set_syscallnr(struct shim_regs* sr, uint64_t sc_num) { - sr->orig_rax = sc_num; -} - #define SHIM_TCB_GET(member) \ ({ \ shim_tcb_t* tcb; \ @@ -186,19 +145,18 @@ static inline void shim_regs_set_syscallnr(struct shim_regs* sr, uint64_t sc_num } \ } while (0) -static inline void shim_arch_update_tls_base(unsigned long tls_base) { - DkSegmentRegisterSet(PAL_SEGMENT_FS, (PAL_PTR)tls_base); +static inline void set_tls(unsigned long tls) { + DkSegmentRegisterSet(PAL_SEGMENT_FS, (PAL_PTR)tls); } -/* On x86_64 the tls_base (fs register) is the same as the tls parameter to 'clone' */ -static inline unsigned long tls_to_tls_base(unsigned long tls) { - return tls; +static inline void set_default_tls(void) { + set_tls(0); } -/* extended context */ -struct shim_ext_context { - uint16_t fpcw; /* FPU Control Word (for x87) */ - uint32_t mxcsr; /* MXCSR control/status register (for SSE/AVX/...) */ -}; +static inline unsigned long get_tls(void) { + void* addr = NULL; + (void)DkSegmentRegisterGet(PAL_SEGMENT_FS, &addr); + return (unsigned long)addr; +} #endif /* _SHIM_TCB_ARCH_H_ */ diff --git a/LibOS/shim/include/arch/x86_64/shim_ucontext-arch.h b/LibOS/shim/include/arch/x86_64/shim_ucontext-arch.h deleted file mode 100644 index 8898f14e..00000000 --- a/LibOS/shim/include/arch/x86_64/shim_ucontext-arch.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: LGPL-3.0-or-later */ - -#ifndef _SHIM_UCONTEXT_ARCH_H_ -#define _SHIM_UCONTEXT_ARCH_H_ - -#include "shim_types.h" -#include "ucontext.h" - -static inline void shim_regs_to_ucontext(ucontext_t* context, struct shim_regs* regs) { - context->uc_mcontext.gregs[REG_R8] = regs->r8; - context->uc_mcontext.gregs[REG_R9] = regs->r9; - context->uc_mcontext.gregs[REG_R10] = regs->r10; - context->uc_mcontext.gregs[REG_R11] = regs->r11; - context->uc_mcontext.gregs[REG_R12] = regs->r12; - context->uc_mcontext.gregs[REG_R13] = regs->r13; - context->uc_mcontext.gregs[REG_R14] = regs->r14; - context->uc_mcontext.gregs[REG_R15] = regs->r15; - context->uc_mcontext.gregs[REG_RDI] = regs->rdi; - context->uc_mcontext.gregs[REG_RSI] = regs->rsi; - context->uc_mcontext.gregs[REG_RBP] = regs->rbp; - context->uc_mcontext.gregs[REG_RBX] = regs->rbx; - context->uc_mcontext.gregs[REG_RDX] = regs->rdx; - context->uc_mcontext.gregs[REG_RAX] = regs->orig_rax; - context->uc_mcontext.gregs[REG_RCX] = regs->rcx; - context->uc_mcontext.gregs[REG_RSP] = regs->rsp; - context->uc_mcontext.gregs[REG_RIP] = regs->rip; - context->uc_mcontext.gregs[REG_EFL] = regs->rflags; - context->uc_mcontext.gregs[REG_CSGSFS] = 0; - context->uc_mcontext.gregs[REG_ERR] = 0; - context->uc_mcontext.gregs[REG_TRAPNO] = 0; - context->uc_mcontext.gregs[REG_OLDMASK] = 0; - context->uc_mcontext.gregs[REG_CR2] = 0; -} - -#endif /* _SHIM_UCONTEXT_ARCH_H_ */ diff --git a/LibOS/shim/include/shim_context.h b/LibOS/shim/include/shim_context.h index c7667caf..f6d299e5 100644 --- a/LibOS/shim/include/shim_context.h +++ b/LibOS/shim/include/shim_context.h @@ -7,20 +7,8 @@ #ifndef _SHIM_CONTEXT_H_ #define _SHIM_CONTEXT_H_ -#include - -#include "shim_tcb.h" - -extern bool g_shim_xsave_enabled; -extern uint64_t g_shim_xsave_features; -extern uint32_t g_shim_xsave_size; - void shim_xstate_init(void); -void shim_xstate_save(void* xstate_extended); +uint64_t shim_xstate_size(void); void shim_xstate_restore(const void* xstate_extended); -void shim_xstate_reset(void); - -noreturn void restore_child_context_after_clone(struct shim_context* context); -void fixup_child_context(struct shim_regs* regs); #endif /* _SHIM_CONTEXT_H_ */ diff --git a/LibOS/shim/include/shim_defs.h b/LibOS/shim/include/shim_defs.h index 57ebc631..3dc67cd3 100644 --- a/LibOS/shim/include/shim_defs.h +++ b/LibOS/shim/include/shim_defs.h @@ -3,6 +3,14 @@ #include "shim_syscalls.h" +/* Names and values are taken from the Linux kernel. */ +#define ERESTARTSYS 512 /* Usual case - restart if SA_RESTART is set. */ +#define ERESTARTNOINTR 513 /* Always restart. */ +#define ERESTARTNOHAND 514 /* Restart if no signal handler. */ + +/* Internal LibOS stack size: 3 pages + one guard page. */ +#define SHIM_THREAD_LIBOS_STACK_SIZE (3 * PAGE_SIZE + PAGE_SIZE) + #define DEFAULT_BRK_MAX_SIZE (256 * 1024) /* 256KB */ #define DEFAULT_SYS_STACK_SIZE (256 * 1024) /* 256KB */ diff --git a/LibOS/shim/include/shim_internal.h b/LibOS/shim/include/shim_internal.h index a529d37b..63351004 100644 --- a/LibOS/shim/include/shim_internal.h +++ b/LibOS/shim/include/shim_internal.h @@ -5,6 +5,7 @@ #define _SHIM_INTERNAL_H_ #include +#include #include "api.h" #include "assert.h" @@ -77,236 +78,123 @@ void debug_vprintf(const char* fmt, va_list ap) __attribute__((format(printf, 1, debug("%s (" __FILE__ ":%d)\n", __func__, __LINE__); \ } while (0) -/* definition for syscall table */ -void handle_signals(void); +/*! + * \brief LibOS syscall emulation entrypoint. + * + * Actual implementation and ABI are architecture-specific, but generally should dump the CPU + * context and call `shim_emulate_syscall`. + */ +void syscalldb(void); +/*! + * \brief High-level syscall emulation entrypoint. + * + * \param context CPU context at syscall entry. + * + * Emulates the syscall given the entry \p context. + */ +noreturn void shim_emulate_syscall(PAL_CONTEXT* context); +/*! + * \brief Restore the CPU context. + * + * \param context CPU context to restore. + * + * This function restores the given \p context. It is only called on returning from a syscall, so + * it does not need to be reentrant (there is no such thing as nested syscalls), but it cannot + * assume that the CPU context is the same as at the entry to the syscall (e.g. sigreturn, or signal + * handling may change it). + */ +noreturn void return_from_syscall(PAL_CONTEXT* context); +/*! + * \brief Restore the context after clone/fork. + * + * \param context LibOS context to restore. + * + * Restores LibOS \p context after a successful clone or fork. + */ +noreturn void restore_child_context_after_clone(struct shim_context* context); +/*! + * \brief Creates a signal frame + * + * \param context CPU context + * \param siginfo signal to be delivered + * \param handler pointer to the user app signal handler + * \param restorer pointer to the restorer function + * \param should_use_altstack `true` - use alternative stack if possible, `false` - never use it + * \param old_mask old signal mask (to be stored in the signal frame) + * + * Creates a signal frame on the user app stack (either normal or alternative stack, depending on + * \p use_altstack and the currently used stack). Arranges \p context so that restoring it jumps + * to \p handler with appropriate arguments and returning from \p handler will jump to \p restorer, + * (which usually just calls `sigreturn` syscall). On most (all?) architectures old \p context, + * \p siginfo and \p old_mask are saved into the signal frame. + */ +void prepare_sigframe(PAL_CONTEXT* context, siginfo_t* siginfo, void* handler, void* restorer, + bool should_use_altstack, __sigset_t* old_mask); +/*! + * \brief Restart a syscall + * + * \param context CPU context + * \param syscall_nr syscall number + * + * Arranges \p context so that upon return to it redoes the \p syscall_nr syscall. + */ +void restart_syscall(PAL_CONTEXT* context, uint64_t syscall_nr); +/*! + * \brief Restores a sigreturn context + * + * \param context original CPU context + * \param[out] new_mask new signal mask + * + * Restores CPU context in an architecture-specific way. On entry to this function \p context holds + * initial CPU context and this function extracts signal frame (generated by `prepare_sigframe`) + * and restores it into \p context. The signal mask extracted from the signal frame is written into + * \p new_mask. + */ +void restore_sigreturn_context(PAL_CONTEXT* context, __sigset_t* new_mask); +/*! + * \brief Emulate a syscall + * + * \param context CPU context + * + * If the current instruction pointer in \p context points to a syscall instruction, arrange + * \p context so that the syscall is emulated. + * Returns `true` if it was a syscall instruction (hence a syscall will be emulated). + * Note that this function merely changes context, so the actual emulation is done upon returning + * to that context. + * Used e.g. in Linux-SGX Pal to handle `syscall` instruction. + */ +bool maybe_emulate_syscall(PAL_CONTEXT* context); +/*! + * \brief Handle a signal + * + * \param context CPU context + * \param old_mask_ptr pointer to the old signal mask + * + * If there is a signal to be handled, this function arranges its delivery using `prepare_sigframe`. + * If \p old_mask_ptr is not `NULL`, it is stored into the signal frame, otherwise the current + * signal mask is used. + * Returns `true` if a not-ignored signal was handled (hence \p context was changed), `false` + * otherwise. + * + * XXX: Signals are delivered only during transition from LibOS to the user app, so a situation is + * possible when a signal is queued, but is not delivered for an arbitrary amount of time. There are + * two distinct situations when this can happen: + * 1) A blocking host-level syscall was issued and the signal arrived at any point before it and + * after the thread entered LibOS code. In such case the host syscall can block indefinitely. + * 2) The signal arrived in the middle of or after `handle_signal`. In such case delivery of this + * signal is delayed until the next syscall is issued or another signal arrives. + */ +bool handle_signal(PAL_CONTEXT* context, __sigset_t* old_mask_ptr); + long convert_pal_errno(long err); -void syscall_wrapper(void); -void syscall_wrapper_after_syscalldb(void); #define PAL_ERRNO() convert_pal_errno(PAL_NATIVE_ERRNO()) -#define SHIM_ARG_TYPE long - -static inline int64_t get_cur_preempt(void) { - shim_tcb_t* tcb = shim_get_tcb(); - assert(tcb); - return __atomic_load_n(&tcb->context.preempt.counter, __ATOMIC_SEQ_CST); -} - -#define BEGIN_SHIM(name, args...) \ - SHIM_ARG_TYPE __shim_##name(args) { \ - SHIM_ARG_TYPE ret = 0; \ - int64_t preempt = get_cur_preempt(); \ - __UNUSED(preempt); - -#define END_SHIM(name) \ - handle_signals(); \ - assert(preempt == get_cur_preempt()); \ - return ret; \ - } - -#define DEFINE_SHIM_SYSCALL(name, n, func, ...) \ - SHIM_SYSCALL_##n(name, func, __VA_ARGS__) - -#define PROTO_ARGS_0() void -#define PROTO_ARGS_1(t, a) t a -#define PROTO_ARGS_2(t, a, rest...) t a, PROTO_ARGS_1(rest) -#define PROTO_ARGS_3(t, a, rest...) t a, PROTO_ARGS_2(rest) -#define PROTO_ARGS_4(t, a, rest...) t a, PROTO_ARGS_3(rest) -#define PROTO_ARGS_5(t, a, rest...) t a, PROTO_ARGS_4(rest) -#define PROTO_ARGS_6(t, a, rest...) t a, PROTO_ARGS_5(rest) - -#define CAST_ARGS_0() -#define CAST_ARGS_1(t, a) (SHIM_ARG_TYPE)a -#define CAST_ARGS_2(t, a, rest...) (SHIM_ARG_TYPE)a, CAST_ARGS_1(rest) -#define CAST_ARGS_3(t, a, rest...) (SHIM_ARG_TYPE)a, CAST_ARGS_2(rest) -#define CAST_ARGS_4(t, a, rest...) (SHIM_ARG_TYPE)a, CAST_ARGS_3(rest) -#define CAST_ARGS_5(t, a, rest...) (SHIM_ARG_TYPE)a, CAST_ARGS_4(rest) -#define CAST_ARGS_6(t, a, rest...) (SHIM_ARG_TYPE)a, CAST_ARGS_5(rest) - -#define DEFINE_SHIM_FUNC(func, n, r, args...) \ - r func(PROTO_ARGS_##n(args)); - -#define PARSE_SYSCALL1(name, ...) \ - debug_print_syscall_before(__NR_##name, ##__VA_ARGS__); - -#define PARSE_SYSCALL2(name, ret_val, ...) \ - debug_print_syscall_after(__NR_##name, ret_val, ##__VA_ARGS__); - void debug_print_syscall_before(int sysno, ...); void debug_print_syscall_after(int sysno, ...); -#define SHIM_SYSCALL_0(name, func, r) \ - BEGIN_SHIM(name, void) \ - PARSE_SYSCALL1(name); \ - r __ret = (func)(); \ - PARSE_SYSCALL2(name, __ret); \ - ret = (SHIM_ARG_TYPE)__ret; \ - END_SHIM(name) - -#define SHIM_SYSCALL_1(name, func, r, t1, a1) \ - BEGIN_SHIM(name, SHIM_ARG_TYPE __arg1) \ - t1 a1 = (t1)__arg1; \ - PARSE_SYSCALL1(name, a1); \ - r __ret = (func)(a1); \ - PARSE_SYSCALL2(name, __ret, a1); \ - ret = (SHIM_ARG_TYPE)__ret; \ - END_SHIM(name) - -#define SHIM_SYSCALL_2(name, func, r, t1, a1, t2, a2) \ - BEGIN_SHIM(name, SHIM_ARG_TYPE __arg1, SHIM_ARG_TYPE __arg2) \ - t1 a1 = (t1)__arg1; \ - t2 a2 = (t2)__arg2; \ - PARSE_SYSCALL1(name, a1, a2); \ - r __ret = (func)(a1, a2); \ - PARSE_SYSCALL2(name, __ret, a1, a2); \ - ret = (SHIM_ARG_TYPE)__ret; \ - END_SHIM(name) - -#define SHIM_SYSCALL_3(name, func, r, t1, a1, t2, a2, t3, a3) \ - BEGIN_SHIM(name, SHIM_ARG_TYPE __arg1, SHIM_ARG_TYPE __arg2, SHIM_ARG_TYPE __arg3) \ - t1 a1 = (t1)__arg1; \ - t2 a2 = (t2)__arg2; \ - t3 a3 = (t3)__arg3; \ - PARSE_SYSCALL1(name, a1, a2, a3); \ - r __ret = (func)(a1, a2, a3); \ - PARSE_SYSCALL2(name, __ret, a1, a2, a3); \ - ret = (SHIM_ARG_TYPE)__ret; \ - END_SHIM(name) - -#define SHIM_SYSCALL_4(name, func, r, t1, a1, t2, a2, t3, a3, t4, a4) \ - BEGIN_SHIM(name, SHIM_ARG_TYPE __arg1, SHIM_ARG_TYPE __arg2, SHIM_ARG_TYPE __arg3, \ - SHIM_ARG_TYPE __arg4) \ - t1 a1 = (t1)__arg1; \ - t2 a2 = (t2)__arg2; \ - t3 a3 = (t3)__arg3; \ - t4 a4 = (t4)__arg4; \ - PARSE_SYSCALL1(name, a1, a2, a3, a4); \ - r __ret = (func)(a1, a2, a3, a4); \ - PARSE_SYSCALL2(name, __ret, a1, a2, a3, a4); \ - ret = (SHIM_ARG_TYPE)__ret; \ - END_SHIM(name) - -#define SHIM_SYSCALL_5(name, func, r, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5) \ - BEGIN_SHIM(name, SHIM_ARG_TYPE __arg1, SHIM_ARG_TYPE __arg2, SHIM_ARG_TYPE __arg3, \ - SHIM_ARG_TYPE __arg4, SHIM_ARG_TYPE __arg5) \ - t1 a1 = (t1)__arg1; \ - t2 a2 = (t2)__arg2; \ - t3 a3 = (t3)__arg3; \ - t4 a4 = (t4)__arg4; \ - t5 a5 = (t5)__arg5; \ - PARSE_SYSCALL1(name, a1, a2, a3, a4, a5); \ - r __ret = (func)(a1, a2, a3, a4, a5); \ - PARSE_SYSCALL2(name, __ret, a1, a2, a3, a4, a5); \ - ret = (SHIM_ARG_TYPE)__ret; \ - END_SHIM(name) - -#define SHIM_SYSCALL_6(name, func, r, t1, a1, t2, a2, t3, a3, t4, a4, t5, a5, t6, a6) \ - BEGIN_SHIM(name, SHIM_ARG_TYPE __arg1, SHIM_ARG_TYPE __arg2, SHIM_ARG_TYPE __arg3, \ - SHIM_ARG_TYPE __arg4, SHIM_ARG_TYPE __arg5, SHIM_ARG_TYPE __arg6) \ - t1 a1 = (t1)__arg1; \ - t2 a2 = (t2)__arg2; \ - t3 a3 = (t3)__arg3; \ - t4 a4 = (t4)__arg4; \ - t5 a5 = (t5)__arg5; \ - t6 a6 = (t6)__arg6; \ - PARSE_SYSCALL1(name, a1, a2, a3, a4, a5, a6); \ - r __ret = (func)(a1, a2, a3, a4, a5, a6); \ - PARSE_SYSCALL2(name, __ret, a1, a2, a3, a4, a5, a6); \ - ret = (SHIM_ARG_TYPE)__ret; \ - END_SHIM(name) - -#define SHIM_PROTO_ARGS_0 void -#define SHIM_PROTO_ARGS_1 SHIM_ARG_TYPE __arg1 -#define SHIM_PROTO_ARGS_2 SHIM_PROTO_ARGS_1, SHIM_ARG_TYPE __arg2 -#define SHIM_PROTO_ARGS_3 SHIM_PROTO_ARGS_2, SHIM_ARG_TYPE __arg3 -#define SHIM_PROTO_ARGS_4 SHIM_PROTO_ARGS_3, SHIM_ARG_TYPE __arg4 -#define SHIM_PROTO_ARGS_5 SHIM_PROTO_ARGS_4, SHIM_ARG_TYPE __arg5 -#define SHIM_PROTO_ARGS_6 SHIM_PROTO_ARGS_5, SHIM_ARG_TYPE __arg6 - -#define SHIM_UNUSED_ARGS_0() - -#define SHIM_UNUSED_ARGS_1() \ - do { \ - __UNUSED(__arg1); \ - } while (0) -#define SHIM_UNUSED_ARGS_2() \ - do { \ - SHIM_UNUSED_ARGS_1(); \ - __UNUSED(__arg2); \ - } while (0) -#define SHIM_UNUSED_ARGS_3() \ - do { \ - SHIM_UNUSED_ARGS_2(); \ - __UNUSED(__arg3); \ - } while (0) -#define SHIM_UNUSED_ARGS_4() \ - do { \ - SHIM_UNUSED_ARGS_3(); \ - __UNUSED(__arg4); \ - } while (0) - -#define SHIM_UNUSED_ARGS_5() \ - do { \ - SHIM_UNUSED_ARGS_4(); \ - __UNUSED(__arg5); \ - } while (0) - -#define SHIM_UNUSED_ARGS_6() \ - do { \ - SHIM_UNUSED_ARGS_5(); \ - __UNUSED(__arg6); \ - } while (0) - -#define SHIM_SYSCALL_RETURN_ENOSYS(name, n, ...) \ - BEGIN_SHIM(name, SHIM_PROTO_ARGS_##n) \ - debug("WARNING: syscall " #name " not implemented. Returning -ENOSYS.\n"); \ - SHIM_UNUSED_ARGS_##n(); \ - ret = -ENOSYS; \ - END_SHIM(name) - #define PAL_CB(member) (pal_control.member) -static inline int64_t __disable_preempt(shim_tcb_t* tcb) { - // tcb->context.syscall_nr += SYSCALL_NR_PREEMPT_INC; - int64_t preempt = __atomic_add_fetch(&tcb->context.preempt.counter, 1, __ATOMIC_SEQ_CST); - /* Assert if this counter overflows */ - assert(preempt != 0); - // debug("disable preempt: %d\n", preempt); - return preempt; -} - -static inline void disable_preempt(shim_tcb_t* tcb) { - if (!tcb && !(tcb = shim_get_tcb())) - return; - - __disable_preempt(tcb); -} - -static inline void __enable_preempt(shim_tcb_t* tcb) { - int64_t preempt = __atomic_sub_fetch(&tcb->context.preempt.counter, 1, __ATOMIC_SEQ_CST); - /* Assert if this counter underflows */ - __UNUSED(preempt); - assert(preempt >= 0); - // debug("enable preempt: %d\n", preempt); -} - -void __handle_signals(shim_tcb_t* tcb); - -static inline void enable_preempt(shim_tcb_t* tcb) { - if (!tcb && !(tcb = shim_get_tcb())) - return; - - int64_t preempt = __atomic_load_n(&tcb->context.preempt.counter, __ATOMIC_SEQ_CST); - if (!preempt) - return; - - if (preempt == 1) - __handle_signals(tcb); - - __enable_preempt(tcb); -} - /* * These events have counting semaphore semantics: * - `set_event(e, n)` increases value of the semaphore by `n`, diff --git a/LibOS/shim/include/shim_lock.h b/LibOS/shim/include/shim_lock.h index a4eb6444..a04ad6ad 100644 --- a/LibOS/shim/include/shim_lock.h +++ b/LibOS/shim/include/shim_lock.h @@ -10,7 +10,6 @@ #include "pal.h" #include "pal_debug.h" #include "shim_internal.h" -#include "shim_tcb.h" #include "shim_thread.h" #include "shim_types.h" @@ -59,9 +58,6 @@ static void lock(struct shim_lock* l) { __abort(); } - shim_tcb_t* tcb = shim_get_tcb(); - disable_preempt(tcb); - while (!DkSynchronizationObjectWait(l->lock, NO_TIMEOUT)) /* nop */; @@ -84,11 +80,8 @@ static inline void unlock(struct shim_lock* l) { __abort(); } - shim_tcb_t* tcb = shim_get_tcb(); - l->owner = 0; DkMutexRelease(l->lock); - enable_preempt(tcb); } static inline bool locked(struct shim_lock* l) { diff --git a/LibOS/shim/include/shim_signal.h b/LibOS/shim/include/shim_signal.h index 861e6691..2191d0d0 100644 --- a/LibOS/shim/include/shim_signal.h +++ b/LibOS/shim/include/shim_signal.h @@ -1,14 +1,15 @@ #ifndef _SHIM_SIGNAL_H_ #define _SHIM_SIGNAL_H_ +#include + #include "shim_defs.h" #include "shim_types.h" -#include "ucontext.h" #define __WCOREDUMP_BIT 0x80 void sigaction_make_defaults(struct __kernel_sigaction* sig_action); -void thread_sigaction_reset_on_execve(struct shim_thread* thread); +void thread_sigaction_reset_on_execve(void); #define BITS_PER_WORD (8 * sizeof(unsigned long)) /* The standard def of this macro is dumb */ @@ -102,24 +103,38 @@ __SIGSETFN(shim_sigdelset, ((__set->__val[__word] &= ~__mask), 0), ) void clear_illegal_signals(__sigset_t* set); -/* NB: Check shim_signal.c if this changes. Some memset(0) elision*/ struct shim_signal { - siginfo_t info; - bool context_stored; - ucontext_t context; - PAL_CONTEXT* pal_context; + siginfo_t siginfo; }; -void get_pending_signals(struct shim_thread* thread, __sigset_t* set); +void get_all_pending_signals(__sigset_t* set); +bool have_pending_signals(void); + +/*! + * \brief Return stack pointer to use in a signal handler. + * + * \param sp Current stack pointer value. + * \param use_altstack True if alternative stack should be used. + * + * Returns value to be used as a new stack pointer in the signal handler, depending on the current + * thread's settings. + */ +uintptr_t get_stack_for_sighandler(uintptr_t sp, bool use_altstack); +/*! + * \brief Check whether address is on alternative stack. + * + * \param sp Stack pointer value. + * \param alt_stack Pointer to the alternative stack. + * + * Returns `true` if \p sp is on \p alt_stack. + */ +bool is_on_altstack(uintptr_t sp, stack_t* alt_stack); struct shim_thread; -int init_signal(void); - -void __store_context(shim_tcb_t* tcb, PAL_CONTEXT* pal_context, struct shim_signal* signal); +int init_signal_handling(void); int append_signal(struct shim_thread* thread, siginfo_t* info); -void deliver_signal(siginfo_t* info, PAL_CONTEXT* context); void get_sig_mask(struct shim_thread* thread, __sigset_t* mask); void set_sig_mask(struct shim_thread* thread, const __sigset_t* new_set); diff --git a/LibOS/shim/include/shim_table.h b/LibOS/shim/include/shim_table.h index 57f07df6..08878cb7 100644 --- a/LibOS/shim/include/shim_table.h +++ b/LibOS/shim/include/shim_table.h @@ -2,357 +2,22 @@ /* Copyright (C) 2014 Stony Brook University * Copyright (C) 2020 Intel Corporation * Michał Kowalczyk + * Borys Popławski */ #ifndef _SHIM_TABLE_H_ #define _SHIM_TABLE_H_ -#include #if defined(__i386__) || defined(__x86_64__) #include #endif #include "shim_types.h" -void debug_unsupp(int num); - typedef void (*shim_fp)(void); extern shim_fp shim_table[]; -/* syscall entries */ -long __shim_read(long, long, long); -long __shim_write(long, long, long); -long __shim_open(long, long, long); -long __shim_close(long); -long __shim_stat(long, long); -long __shim_fstat(long, long); -long __shim_lstat(long, long); -long __shim_poll(long, long, long); -long __shim_lseek(long, long, long); -long __shim_mmap(long, long, long, long, long, long); -long __shim_mprotect(long, long, long); -long __shim_munmap(long, long); -long __shim_brk(long); -long __shim_rt_sigaction(long, long, long, long); -long __shim_rt_sigprocmask(long, long, long); -long __shim_rt_sigreturn(long); -long __shim_ioctl(long, long, long); -long __shim_pread64(long, long, long, long); -long __shim_pwrite64(long, long, long, long); -long __shim_readv(long, long, long); -long __shim_writev(long, long, long); -long __shim_access(long, long); -long __shim_pipe(long); -long __shim_select(long, long, long, long, long); -long __shim_sched_yield(void); -long __shim_mremap(long, long, long, long, long); -long __shim_msync(long, long, long); -long __shim_mincore(long, long, long); -long __shim_madvise(long, long, long); -long __shim_shmget(long, long, long); -long __shim_shmat(long, long, long); -long __shim_shmctl(long, long, long); -long __shim_dup(long); -long __shim_dup2(long, long); -long __shim_pause(void); -long __shim_nanosleep(long, long); -long __shim_getitimer(long, long); -long __shim_alarm(long); -long __shim_setitimer(long, long, long); -long __shim_getpid(void); -long __shim_sendfile(long, long, long, long); -long __shim_socket(long, long, long); -long __shim_connect(long, long, long); -long __shim_accept(long, long, long); -long __shim_sendto(long, long, long, long, long, long); -long __shim_recvfrom(long, long, long, long, long, long); -long __shim_sendmsg(long, long, long); -long __shim_recvmsg(long, long, long); -long __shim_shutdown(long, long); -long __shim_bind(long, long, long); -long __shim_listen(long, long); -long __shim_getsockname(long, long, long); -long __shim_getpeername(long, long, long); -long __shim_socketpair(long, long, long, long); -long __shim_setsockopt(long, long, long, long, long); -long __shim_getsockopt(long, long, long, long, long); -long __shim_clone(long, long, long, long, long); -long __shim_fork(void); -long __shim_vfork(void); -long __shim_execve(long, long, long); -long __shim_exit(long); -long __shim_wait4(long, long, long, long); -long __shim_kill(long, long); -long __shim_uname(long); -long __shim_semget(long, long, long); -long __shim_semop(long, long, long); -long __shim_semctl(long, long, long, long); -long __shim_shmdt(long); -long __shim_msgget(long, long); -long __shim_msgsnd(long, long, long, long); -long __shim_msgrcv(long, long, long, long, long); -long __shim_msgctl(long, long, long); -long __shim_fcntl(long, long, long); -long __shim_flock(long, long); -long __shim_fsync(long); -long __shim_fdatasync(long); -long __shim_truncate(long, long); -long __shim_ftruncate(long, long); -long __shim_getdents(long, long, long); -long __shim_getcwd(long, long); -long __shim_chdir(long); -long __shim_fchdir(long); -long __shim_rename(long, long); -long __shim_mkdir(long, long); -long __shim_rmdir(long); -long __shim_creat(long, long); -long __shim_link(long, long); -long __shim_unlink(long); -long __shim_symlink(long, long); -long __shim_readlink(long, long, long); -long __shim_chmod(long, long); -long __shim_fchmod(long, long); -long __shim_chown(long, long, long); -long __shim_fchown(long, long, long); -long __shim_lchown(long, long, long); -long __shim_umask(long); -long __shim_gettimeofday(long, long); -long __shim_getrlimit(long, long); -long __shim_getrusage(long, long); -long __shim_sysinfo(long); -long __shim_times(long); -long __shim_ptrace(long, long, long, long); -long __shim_getuid(void); -long __shim_syslog(long, long, long); -long __shim_getgid(void); -long __shim_setuid(long); -long __shim_setgid(long); -long __shim_geteuid(void); -long __shim_getegid(void); -long __shim_setpgid(long, long); -long __shim_getppid(void); -long __shim_getpgrp(void); -long __shim_setsid(void); -long __shim_setreuid(long, long); -long __shim_setregid(long, long); -long __shim_getgroups(long, long); -long __shim_setgroups(long, long); -long __shim_setresuid(long, long, long); -long __shim_getresuid(long, long, long); -long __shim_setresgid(long, long, long); -long __shim_getresgid(long, long, long); -long __shim_getpgid(long); -long __shim_setfsuid(long); -long __shim_setfsgid(long); -long __shim_getsid(long); -long __shim_capget(long, long); -long __shim_capset(long, long); -long __shim_rt_sigpending(long, long); -long __shim_rt_sigtimedwait(long, long, long, long); -long __shim_rt_sigqueueinfo(long, long, long); -long __shim_rt_sigsuspend(long); -long __shim_sigaltstack(long, long); -long __shim_utime(long, long); -long __shim_mknod(long, long, long); -long __shim_uselib(long); -long __shim_personality(long); -long __shim_ustat(long, long); -long __shim_statfs(long, long); -long __shim_fstatfs(long, long); -long __shim_sysfs(long, long, long); -long __shim_getpriority(long, long); -long __shim_setpriority(long, long, long); -long __shim_sched_setparam(long, long); -long __shim_sched_getparam(long, long); -long __shim_sched_setscheduler(long, long, long); -long __shim_sched_getscheduler(long); -long __shim_sched_get_priority_max(long); -long __shim_sched_get_priority_min(long); -long __shim_sched_rr_get_interval(long, long); -long __shim_mlock(long, long); -long __shim_munlock(long, long); -long __shim_mlockall(long); -long __shim_munlockall(void); -long __shim_vhangup(void); -long __shim_modify_ldt(long, long, long); -long __shim_pivot_root(long, long); -long __shim__sysctl(long); -long __shim_prctl(long, long, long, long, long); -long __shim_arch_prctl(long, long); -long __shim_adjtimex(long); -long __shim_setrlimit(long, long); -long __shim_chroot(long); -long __shim_sync(void); -long __shim_acct(long); -long __shim_settimeofday(long, long); -long __shim_mount(long, long, long, long, long); -long __shim_umount2(long, long); -long __shim_swapon(long, long); -long __shim_swapoff(long); -long __shim_reboot(long, long, long, long); -long __shim_sethostname(long, long); -long __shim_setdomainname(long, long); -long __shim_iopl(long); -long __shim_ioperm(long, long, long); -long __shim_create_module(long, long); -long __shim_init_module(long, long, long); -long __shim_delete_module(long, long); -long __shim_get_kernel_syms(long); -long __shim_query_module(long, long, long, long, long); -long __shim_quotactl(long, long, long, long); -long __shim_nfsservctl(long, long, long); -long __shim_gettid(void); -long __shim_readahead(long, long, long); -long __shim_setxattr(long, long, long, long, long); -long __shim_lsetxattr(long, long, long, long, long); -long __shim_fsetxattr(long, long, long, long, long); -long __shim_getxattr(long, long, long, long); -long __shim_lgetxattr(long, long, long, long); -long __shim_fgetxattr(long, long, long, long); -long __shim_listxattr(long, long, long); -long __shim_llistxattr(long, long, long); -long __shim_flistxattr(long, long, long); -long __shim_removexattr(long, long); -long __shim_lremovexattr(long, long); -long __shim_fremovexattr(long, long); -long __shim_tkill(long, long); -long __shim_time(long); -long __shim_futex(long, long, long, long, long, long); -long __shim_sched_setaffinity(long, long, long); -long __shim_sched_getaffinity(long, long, long); -long __shim_set_thread_area(long); -long __shim_io_setup(long, long); -long __shim_io_destroy(long); -long __shim_io_getevents(long, long, long, long, long); -long __shim_io_submit(long, long, long); -long __shim_io_cancel(long, long, long); -long __shim_get_thread_area(long); -long __shim_lookup_dcookie(long, long, long); -long __shim_epoll_create(long); -long __shim_remap_file_pages(long, long, long, long, long); -long __shim_getdents64(long, long, long); -long __shim_set_tid_address(long); -long __shim_restart_syscall(void); -long __shim_semtimedop(long, long, long, long); -long __shim_fadvise64(long, long, long, long); -long __shim_timer_create(long, long, long); -long __shim_timer_settime(long, long, long, long); -long __shim_timer_gettime(long, long); -long __shim_timer_getoverrun(long); -long __shim_timer_delete(long); -long __shim_clock_settime(long, long); -long __shim_clock_gettime(long, long); -long __shim_clock_getres(long, long); -long __shim_clock_nanosleep(long, long, long, long); -long __shim_exit_group(long); -long __shim_epoll_wait(long, long, long, long); -long __shim_epoll_ctl(long, long, long, long); -long __shim_tgkill(long, long, long); -long __shim_utimes(long, long); -long __shim_mbind(long, long, long, long, long, long); -long __shim_set_mempolicy(long, long, long); -long __shim_get_mempolicy(long, long, long, long, long); -long __shim_mq_open(long, long, long, long); -long __shim_mq_unlink(long); -long __shim_mq_timedsend(long, long, long, long, long); -long __shim_mq_timedreceive(long, long, long, long, long); -long __shim_mq_notify(long, long); -long __shim_mq_getsetattr(long, long, long); -long __shim_kexec_load(long, long, long, long); -long __shim_waitid(long, long, long, long, long); -long __shim_add_key(long, long, long, long, long); -long __shim_request_key(long, long, long, long); -long __shim_keyctl(long, long, long, long, long); -long __shim_ioprio_set(long, long, long); -long __shim_ioprio_get(long, long); -long __shim_inotify_init(void); -long __shim_inotify_add_watch(long, long, long); -long __shim_inotify_rm_watch(long, long); -long __shim_migrate_pages(long, long, long, long); -long __shim_openat(long, long, long, long); -long __shim_mkdirat(long, long, long); -long __shim_mknodat(long, long, long, long); -long __shim_fchownat(long, long, long, long, long); -long __shim_futimesat(long, long, long); -long __shim_newfstatat(long, long, long, long); -long __shim_unlinkat(long, long, long); -long __shim_renameat(long, long, long, long); -long __shim_linkat(long, long, long, long, long); -long __shim_symlinkat(long, long, long); -long __shim_readlinkat(long, long, long, long); -long __shim_fchmodat(long, long, long); -long __shim_faccessat(long, long, long); -long __shim_pselect6(long, long, long, long, long, long); -long __shim_ppoll(long, long, long, long, long); -long __shim_unshare(long); -long __shim_set_robust_list(long, long); -long __shim_get_robust_list(long, long, long); -long __shim_splice(long, long, long, long, long, long); -long __shim_tee(long, long, long, long); -long __shim_sync_file_range(long, long, long, long); -long __shim_vmsplice(long, long, long, long); -long __shim_move_pages(long, long, long, long, long, long); -long __shim_utimensat(long, long, long, long); -long __shim_epoll_pwait(long, long, long, long, long, long); -long __shim_signalfd(long, long, long); -long __shim_timerfd_create(long, long); -long __shim_eventfd(long); -long __shim_fallocate(long, long, long, long); -long __shim_timerfd_settime(long, long, long, long); -long __shim_timerfd_gettime(long, long); -long __shim_accept4(long, long, long, long); -long __shim_signalfd4(long, long, long, long); -long __shim_eventfd2(long, long); -long __shim_epoll_create1(long); -long __shim_dup3(long, long, long); -long __shim_pipe2(long, long); -long __shim_inotify_init1(long); -long __shim_preadv(long, long, long, long, long); -long __shim_pwritev(long, long, long, long, long); -long __shim_rt_tgsigqueueinfo(long, long, long, long); -long __shim_perf_event_open(long, long, long, long, long); -long __shim_recvmmsg(long, long, long, long, long); -long __shim_fanotify_init(long, long); -long __shim_fanotify_mark(long, long, long, long, long); -long __shim_prlimit64(long, long, long, long); -long __shim_name_to_handle_at(long, long, long, long, long); -long __shim_open_by_handle_at(long, long, long); -long __shim_clock_adjtime(long, long); -long __shim_syncfs(long); -long __shim_sendmmsg(long, long, long, long); -long __shim_setns(long, long); -long __shim_getcpu(long, long, long); -long __shim_process_vm_readv(long, long, long, long, long, long); -long __shim_process_vm_writev(long, long, long, long, long, long); -long __shim_kcmp(long, long, long, long, long); -long __shim_finit_module(long, long, long); -long __shim_sched_setattr(long, long, long); -long __shim_sched_getattr(long, long, long, long); -long __shim_renameat2(long, long, long, long, long); -long __shim_seccomp(long, long, long); -long __shim_getrandom(long, long, long); -long __shim_memfd_create(long, long); -long __shim_kexec_file_load(long, long, long, long, long); -long __shim_bpf(long, long, long); -long __shim_execveat(long, long, long, long, long); -long __shim_userfaultfd(long); -long __shim_membarrier(long, long); -long __shim_mlock2(long, long, long); -long __shim_copy_file_range(long, long, long, long, long, long); -long __shim_preadv2(long, long, long, long, long, long); -long __shim_pwritev2(long, long, long, long, long, long); -long __shim_pkey_mprotect(long, long, long, long); -long __shim_pkey_alloc(long, long); -long __shim_pkey_free(long); -long __shim_statx(long, long, long, long, long); -long __shim_io_pgetevents(long, long, long, long, long, long); -long __shim_rseq(long, long, long, long); -long __shim_pidfd_send_signal(long, long, long, long); -long __shim_io_uring_setup(long, long); -long __shim_io_uring_enter(long, long, long, long, long, long); -long __shim_io_uring_register(long, long, long, long); - /* syscall implementation */ long shim_do_read(int fd, void* buf, size_t count); long shim_do_write(int fd, const void* buf, size_t count); @@ -369,10 +34,10 @@ void* shim_do_mmap(void* addr, size_t length, int prot, int flags, int fd, off_t long shim_do_mprotect(void* addr, size_t len, int prot); long shim_do_munmap(void* addr, size_t len); void* shim_do_brk(void* brk); -long shim_do_sigaction(int signum, const struct __kernel_sigaction* act, - struct __kernel_sigaction* oldact, size_t sigsetsize); -long shim_do_sigprocmask(int how, const __sigset_t* set, __sigset_t* oldset); -long shim_do_sigreturn(int __unused); +long shim_do_rt_sigaction(int signum, const struct __kernel_sigaction* act, + struct __kernel_sigaction* oldact, size_t sigsetsize); +long shim_do_rt_sigprocmask(int how, const __sigset_t* set, __sigset_t* oldset); +long shim_do_rt_sigreturn(void); long shim_do_ioctl(unsigned int fd, unsigned int cmd, unsigned long arg); long shim_do_pread64(int fd, char* buf, size_t count, loff_t pos); long shim_do_pwrite64(int fd, char* buf, size_t count, loff_t pos); @@ -383,8 +48,6 @@ long shim_do_pipe(int* fildes); long shim_do_select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct __kernel_timeval* timeout); long shim_do_sched_yield(void); -void* shim_do_mremap(void* addr, size_t old_len, size_t new_len, int flags, void* new_addr); -long shim_do_msync(void* start, size_t len, int flags); long shim_do_mincore(void* start, size_t len, unsigned char* vec); long shim_do_madvise(unsigned long start, size_t len_in, int behavior); long shim_do_dup(unsigned int fd); @@ -419,7 +82,7 @@ long shim_do_clone(unsigned long flags, unsigned long user_stack_addr, int* pare long shim_do_fork(void); long shim_do_vfork(void); long shim_do_execve(const char* file, const char** argv, const char** envp); -noreturn long shim_do_exit(int error_code); +long shim_do_exit(int error_code); long shim_do_waitid(int which, pid_t id, siginfo_t* infop, int options, struct __kernel_rusage* ru); long shim_do_wait4(pid_t pid, int* stat_addr, int options, struct __kernel_rusage* ru); long shim_do_kill(pid_t pid, int sig); @@ -453,7 +116,6 @@ long shim_do_fchown(int fd, uid_t user, gid_t group); long shim_do_umask(mode_t mask); long shim_do_gettimeofday(struct __kernel_timeval* tv, struct __kernel_timezone* tz); long shim_do_getrlimit(int resource, struct __kernel_rlimit* rlim); -long shim_do_getrusage(int who, struct __kernel_rusage* ru); long shim_do_getuid(void); long shim_do_getgid(void); long shim_do_setuid(uid_t uid); @@ -468,7 +130,7 @@ long shim_do_getpgrp(void); long shim_do_setsid(void); long shim_do_getpgid(pid_t pid); long shim_do_getsid(pid_t pid); -long shim_do_sigpending(__sigset_t* set, size_t sigsetsize); +long shim_do_rt_sigpending(__sigset_t* set, size_t sigsetsize); long shim_do_sigaltstack(const stack_t* ss, stack_t* oss); long shim_do_setpriority(int which, int who, int niceval); long shim_do_getpriority(int which, int who); @@ -479,7 +141,7 @@ long shim_do_sched_getscheduler(pid_t pid); long shim_do_sched_get_priority_max(int policy); long shim_do_sched_get_priority_min(int policy); long shim_do_sched_rr_get_interval(pid_t pid, struct timespec* interval); -long shim_do_sigsuspend(const __sigset_t* mask); +long shim_do_rt_sigsuspend(const __sigset_t* mask, size_t setsize); long shim_do_arch_prctl(int code, void* addr); long shim_do_setrlimit(int resource, struct __kernel_rlimit* rlim); long shim_do_chroot(const char* filename); @@ -503,7 +165,7 @@ long shim_do_clock_gettime(clockid_t which_clock, struct timespec* tp); long shim_do_clock_getres(clockid_t which_clock, struct timespec* tp); long shim_do_clock_nanosleep(clockid_t clock_id, int flags, const struct __kernel_timespec* rqtp, struct __kernel_timespec* rmtp); -noreturn long shim_do_exit_group(int error_code); +long shim_do_exit_group(int error_code); long shim_do_tgkill(int tgid, int pid, int sig); long shim_do_mbind(void* start, unsigned long len, int mode, unsigned long* nmask, unsigned long maxnode, int flags); diff --git a/LibOS/shim/include/shim_tcb.h b/LibOS/shim/include/shim_tcb.h index 2834c948..2af29944 100644 --- a/LibOS/shim/include/shim_tcb.h +++ b/LibOS/shim/include/shim_tcb.h @@ -10,28 +10,11 @@ #define SHIM_TCB_CANARY 0xdeadbeef struct shim_context { - struct shim_regs* regs; - struct shim_ext_context ext_ctx; - uint64_t tls_base; - struct atomic_int preempt; + PAL_CONTEXT* regs; + long syscall_nr; + unsigned long tls; /* Used only in clone. */ }; -static inline unsigned long shim_context_get_sp(struct shim_context* sc) { - return shim_regs_get_sp(sc->regs); -} - -static inline void shim_context_set_sp(struct shim_context* sc, unsigned long sp) { - shim_regs_set_sp(sc->regs, sp); -} - -static inline unsigned long shim_context_get_syscallnr(struct shim_context* sc) { - return shim_regs_get_syscallnr(sc->regs); -} - -static inline void shim_context_set_syscallnr(struct shim_context* sc, unsigned long sc_num) { - shim_regs_set_syscallnr(sc->regs, sc_num); -} - struct debug_buf; typedef struct shim_tcb shim_tcb_t; @@ -39,7 +22,11 @@ struct shim_tcb { uint64_t canary; shim_tcb_t* self; struct shim_thread* tp; + void* libos_stack_bottom; struct shim_context context; + /* Scratch space to temporarily store a register. On some architectures (e.g. x86_64 inside + * an SGX enclave) we lack a way to restore all (or at least some) registers atomically. */ + void* syscall_scratch_pc; int pal_errno; struct debug_buf* debug_buf; void* vma_cache; @@ -58,12 +45,15 @@ struct shim_tcb { static inline void __shim_tcb_init(shim_tcb_t* shim_tcb) { shim_tcb->canary = SHIM_TCB_CANARY; shim_tcb->self = shim_tcb; + shim_tcb->context.syscall_nr = -1; shim_tcb->vma_cache = NULL; } /* Call this function at the beginning of thread execution. */ static inline void shim_tcb_init(void) { PAL_TCB* tcb = pal_get_tcb(); + static_assert(sizeof(shim_tcb_t) <= sizeof(((PAL_TCB*)0)->libos_tcb), + "Not enough space for LibOS TCB inside Pal TCB"); shim_tcb_t* shim_tcb = (shim_tcb_t*)tcb->libos_tcb; memset(shim_tcb, 0, sizeof(*shim_tcb)); __shim_tcb_init(shim_tcb); @@ -77,11 +67,4 @@ static inline bool shim_tcb_check_canary(void) { return SHIM_TCB_GET(canary) == SHIM_TCB_CANARY; } -static inline void update_tls_base(unsigned long tls_base) { - shim_tcb_t* shim_tcb = shim_get_tcb(); - shim_tcb->context.tls_base = tls_base; - shim_arch_update_tls_base(tls_base); - assert(shim_tcb_check_canary()); -} - #endif /* _SHIM_H_ */ diff --git a/LibOS/shim/include/shim_thread.h b/LibOS/shim/include/shim_thread.h index f0e6480c..486ba18c 100644 --- a/LibOS/shim/include/shim_thread.h +++ b/LibOS/shim/include/shim_thread.h @@ -45,8 +45,15 @@ struct shim_rt_signal_queue { struct shim_signal* queue[MAX_SIGNAL_LOG]; }; +/* + * We store standard signals directly inside queue and real-time signals as pointers to objects + * obtained via `malloc`. + * `pending_mask` stores mask of signals present in this queue. + * Accesses to this queue should be protected by a lock. + */ struct shim_signal_queue { - struct shim_signal* standard_signals[SIGRTMIN - 1]; + __sigset_t pending_mask; + struct shim_signal standard_signals[SIGRTMIN - 1]; struct shim_rt_signal_queue rt_signal_queues[NUM_SIGS - SIGRTMIN + 1]; }; @@ -56,6 +63,9 @@ struct shim_thread { /* Field for inserting threads on global `g_thread_list`. */ LIST_TYPE(shim_thread) list; + /* Pointer to the bottom of the internal LibOS stack. */ + void* libos_stack_bottom; + /* thread identifier */ IDTYPE tid; @@ -74,23 +84,21 @@ struct shim_thread { /* signal handling */ __sigset_t signal_mask; + /* If you need both locks, take `thread->signal_dispositions->lock` before `thread->lock`. */ struct shim_signal_dispositions* signal_dispositions; struct shim_signal_queue signal_queue; /* For the field below, see the explanation in "LibOS/shim/src/bookkeep/shim_signal.c" near - * `process_pending_signals_cnt`. */ + * `g_process_pending_signals_cnt`. */ uint64_t pending_signals; /* - * This field is used for checking whether we handled a signal (e.g. if we want to sleep and - * make some decision after wakeup based on whether we handled a signal, see `sigsuspend`) - * and can have following values: - * - `SIGNAL_NOT_HANDLED` - usually initialized to this - no signals were handled, - * - `SIGNAL_HANDLED` - at least one signal was handled, - * - `SIGNAL_HANDLED_RESTART` - same as above, but the signal had `SA_RESTART` flag. - * `SIGNAL_HANDLED` has priority over `SIGNAL_HANDLED_RESTART`, i.e. if we handle multiple - * signals, some with `SA_RESTART`, some without it, this field will be set to `SIGNAL_HANDLED`. + * Space to store a forced, synchronous signal. Needed to handle e.g. `SIGSEGV` caused by + * referencing an invalid address, which we need to handle before any user-generated `SIGSEGV` + * (via `kill`), hence we cannot use a normal signal queue in such case. */ - unsigned char signal_handled; + struct shim_signal forced_signal; + + /* This field can be accessed without any locks, but each thread can access only its own. */ stack_t signal_altstack; /* futex robust list */ @@ -120,20 +128,13 @@ struct shim_thread_queue { bool in_use; }; -/* See the explanation in `shim_thread`. */ -enum { - SIGNAL_NOT_HANDLED = 0, - SIGNAL_HANDLED, - SIGNAL_HANDLED_RESTART, -}; - int init_threading(void); static inline bool is_internal(struct shim_thread* thread) { return thread->tid >= INTERNAL_TID_BASE; } -void clear_signal_queue(struct shim_signal_queue* queue); +void free_signal_queue(struct shim_signal_queue* queue); void get_signal_dispositions(struct shim_signal_dispositions* dispositions); void put_signal_dispositions(struct shim_signal_dispositions* dispositions); @@ -185,6 +186,7 @@ static inline void set_cur_thread(struct shim_thread* thread) { } tcb->tp = thread; + tcb->libos_stack_bottom = thread->libos_stack_bottom; thread->shim_tcb = tcb; if (tcb->debug_buf) @@ -201,7 +203,7 @@ static inline void thread_setwait(struct shim_thread** queue, struct shim_thread } } -static inline int thread_sleep(uint64_t timeout_us) { +static inline int thread_sleep(uint64_t timeout_us, bool ignore_pending_signals) { struct shim_thread* cur_thread = get_cur_thread(); if (!cur_thread) @@ -211,6 +213,10 @@ static inline int thread_sleep(uint64_t timeout_us) { if (!event) return -EINVAL; + if (!ignore_pending_signals && have_pending_signals()) { + return -EINTR; + } + if (!DkSynchronizationObjectWait(event, timeout_us)) return -PAL_ERRNO(); @@ -273,6 +279,16 @@ struct shim_thread* lookup_thread(IDTYPE tid); struct shim_thread* get_new_thread(void); struct shim_thread* get_new_internal_thread(void); +/*! + * \brief Allocate a new stack for LibOS calls (emulated syscalls). + * + * \param thread Thread for which to allocate a new stack. + * + * On success returns `0`, on failure - negative error code. + * Should be called only once per thread. + */ +int alloc_thread_libos_stack(struct shim_thread* thread); + /* Adds `thread` to global thread list. */ void add_thread(struct shim_thread* thread); diff --git a/LibOS/shim/src/Makefile b/LibOS/shim/src/Makefile index 2ca508a5..2a5b65b7 100644 --- a/LibOS/shim/src/Makefile +++ b/LibOS/shim/src/Makefile @@ -49,6 +49,7 @@ CFLAGS += $(defs) ASFLAGS += $(defs) objs = \ + shim_arch_prctl-$(ARCH).o \ shim_async.o \ shim_checkpoint.o \ shim_context-$(ARCH).o \ @@ -190,6 +191,8 @@ syscallas-$(ARCH).S shim_context-$(ARCH).c: asm-offsets.h include ../../../Scripts/Makefile.rules +vdso/vdso.o: CFLAGS += -Ivdso/arch/$(ARCH)/ + LDFLAGS-vdso/vdso.so.dbg = -nostdlib -shared \ --hash-style=both --build-id -Bsymbolic \ -m elf_x86_64 --no-undefined \ diff --git a/LibOS/shim/src/bookkeep/shim_signal.c b/LibOS/shim/src/bookkeep/shim_signal.c index bbd93c45..8ee85e06 100644 --- a/LibOS/shim/src/bookkeep/shim_signal.c +++ b/LibOS/shim/src/bookkeep/shim_signal.c @@ -26,18 +26,12 @@ #include "shim_table.h" #include "shim_thread.h" #include "shim_types.h" -#include "shim_ucontext-arch.h" #include "shim_utils.h" #include "shim_vma.h" +#include "toml.h" static bool g_check_invalid_ptrs = true; -// __rt_sighandler_t is different from __sighandler_t in : -// typedef void __signalfn_t(int); -// typedef __signalfn_t *__sighandler_t - -typedef void (*__rt_sighandler_t)(int, siginfo_t*, void*); - void sigaction_make_defaults(struct __kernel_sigaction* sig_action) { sig_action->k_sa_handler = (void*)SIG_DFL; sig_action->sa_flags = 0; @@ -45,10 +39,12 @@ void sigaction_make_defaults(struct __kernel_sigaction* sig_action) { __sigemptyset(&sig_action->sa_mask); } -void thread_sigaction_reset_on_execve(struct shim_thread* thread) { - lock(&thread->signal_dispositions->lock); - for (size_t i = 0; i < ARRAY_SIZE(thread->signal_dispositions->actions); i++) { - struct __kernel_sigaction* sig_action = &thread->signal_dispositions->actions[i]; +void thread_sigaction_reset_on_execve(void) { + struct shim_thread* current = get_cur_thread(); + + lock(¤t->signal_dispositions->lock); + for (size_t i = 0; i < ARRAY_SIZE(current->signal_dispositions->actions); i++) { + struct __kernel_sigaction* sig_action = ¤t->signal_dispositions->actions[i]; __sighandler_t handler = sig_action->k_sa_handler; if (handler == (void*)SIG_DFL || handler == (void*)SIG_IGN) { @@ -60,257 +56,260 @@ void thread_sigaction_reset_on_execve(struct shim_thread* thread) { /* app installed its own signal handler, reset it to default */ sigaction_make_defaults(sig_action); } - unlock(&thread->signal_dispositions->lock); + unlock(¤t->signal_dispositions->lock); } -static __rt_sighandler_t default_sighandler[NUM_SIGS]; +static noreturn void sighandler_kill(int sig) { + debug("killed by signal %d\n", sig & ~__WCOREDUMP_BIT); + process_exit(0, sig); +} -static struct shim_signal_queue process_signal_queue = {0}; -/* This is just an optimization, not to have to check the queue for pending signals. A thread will - * be woken up after signal is appended to its queue and will handle all unblocked pending signals - * no matter what is the relative ordering of increasing this variable vs. appending signal to - * the queue. */ -static uint64_t process_pending_signals_cnt = 0; +static noreturn void sighandler_core(int sig) { + /* NOTE: This implementation only indicates the core dump for wait4() + * and friends. No actual core-dump file is created. */ + sig = __WCOREDUMP_BIT | sig; + sighandler_kill(sig); +} + +typedef enum { + SIGHANDLER_NONE, + SIGHANDLER_KILL, + SIGHANDLER_CORE, +} SIGHANDLER_T; + +static const SIGHANDLER_T default_sighandler[NUM_SIGS] = { + [SIGHUP - 1] = SIGHANDLER_KILL, + [SIGINT - 1] = SIGHANDLER_KILL, + [SIGQUIT - 1] = SIGHANDLER_CORE, + [SIGILL - 1] = SIGHANDLER_CORE, + [SIGTRAP - 1] = SIGHANDLER_CORE, + [SIGABRT - 1] = SIGHANDLER_CORE, + [SIGBUS - 1] = SIGHANDLER_CORE, + [SIGFPE - 1] = SIGHANDLER_CORE, + [SIGKILL - 1] = SIGHANDLER_KILL, + [SIGUSR1 - 1] = SIGHANDLER_KILL, + [SIGSEGV - 1] = SIGHANDLER_CORE, + [SIGUSR2 - 1] = SIGHANDLER_KILL, + [SIGPIPE - 1] = SIGHANDLER_KILL, + [SIGALRM - 1] = SIGHANDLER_KILL, + [SIGTERM - 1] = SIGHANDLER_KILL, + [SIGSTKFLT - 1] = SIGHANDLER_KILL, + [SIGCHLD - 1] = SIGHANDLER_NONE, + [SIGCONT - 1] = SIGHANDLER_NONE, + [SIGSTOP - 1] = SIGHANDLER_NONE, + [SIGTSTP - 1] = SIGHANDLER_NONE, + [SIGTTIN - 1] = SIGHANDLER_NONE, + [SIGTTOU - 1] = SIGHANDLER_NONE, + [SIGURG - 1] = SIGHANDLER_NONE, + [SIGXCPU - 1] = SIGHANDLER_CORE, + [SIGXFSZ - 1] = SIGHANDLER_CORE, + [SIGVTALRM - 1] = SIGHANDLER_KILL, + [SIGPROF - 1] = SIGHANDLER_KILL, + [SIGWINCH - 1] = SIGHANDLER_NONE, + [SIGIO - 1] = SIGHANDLER_KILL, + [SIGPWR - 1] = SIGHANDLER_KILL, + [SIGSYS - 1] = SIGHANDLER_CORE, +}; + + +static struct shim_signal_queue g_process_signal_queue = {0}; +/* This lock should always be taken after thread lock (if both are needed). */ +static struct shim_lock g_process_signal_queue_lock; +/* + * This is just an optimization, not to have to check the queue for pending signals. This field can + * be read atomically without any locks, to get approximate value, but to get exact you need to take + * appropriate lock. Every store should be both atomic and behind a lock. + */ +static uint64_t g_process_pending_signals_cnt = 0; /* - * These checks are racy, but we can't do better anyway: signal can be delivered in any moment. - * Worst case scenario we report a real-time signal queue being empty just when a signal is being - * appended. - * - * TODO: we need to consider removing the ability of outside world to deliver signals to a app - * running inside Graphene (this might be important on Linux-SGX PAL). In such case it would be - * probably impossible for an app to be preempted while appending a signal and needing to append - * another one. This would allow for using proper locking scheme here. + * If host signal injection is enabled, this stores the injected signal. Note that we currently + * support injecting only 1 instance of 1 signal only once, as this feature is meant only for + * graceful termination of the user application (e.g. via SIGTERM). */ +static int g_host_injected_signal = 0; +static bool g_inject_host_signal_enabled = false; + static bool is_rt_sq_empty(struct shim_rt_signal_queue* queue) { - return __atomic_load_n(&queue->get_idx, __ATOMIC_ACQUIRE) - == __atomic_load_n(&queue->put_idx, __ATOMIC_ACQUIRE); + return queue->get_idx == queue->put_idx; } -static bool has_standard_signal(struct shim_signal** queue) { - return !!__atomic_load_n(queue, __ATOMIC_ACQUIRE); +static bool has_standard_signal(struct shim_signal* signal_slot) { + return signal_slot->siginfo.si_signo != 0; } -void get_pending_signals(struct shim_thread* thread, __sigset_t* set) { +static void recalc_pending_mask(struct shim_signal_queue* queue, int sig) { + if (sig < SIGRTMIN) { + if (!has_standard_signal(&queue->standard_signals[sig - 1])) { + __sigdelset(&queue->pending_mask, sig); + } + } else { + if (is_rt_sq_empty(&queue->rt_signal_queues[sig - SIGRTMIN])) { + __sigdelset(&queue->pending_mask, sig); + } + } +} + +void get_all_pending_signals(__sigset_t* set) { + struct shim_thread* current = get_cur_thread(); + __sigemptyset(set); - if (__atomic_load_n(&thread->pending_signals, __ATOMIC_ACQUIRE) == 0 - && __atomic_load_n(&process_pending_signals_cnt, __ATOMIC_ACQUIRE) == 0) { + if (__atomic_load_n(¤t->pending_signals, __ATOMIC_ACQUIRE) == 0 + && __atomic_load_n(&g_process_pending_signals_cnt, __ATOMIC_ACQUIRE) == 0) { return; } - for (int sig = 1; sig < SIGRTMIN; sig++) { - if (has_standard_signal(&thread->signal_queue.standard_signals[sig - 1]) - || has_standard_signal(&process_signal_queue.standard_signals[sig - 1])) { - __sigaddset(set, sig); - } - } + lock(¤t->lock); + lock(&g_process_signal_queue_lock); - for (int sig = SIGRTMIN; sig <= NUM_SIGS; sig++) { - if (!is_rt_sq_empty(&thread->signal_queue.rt_signal_queues[sig - SIGRTMIN]) - || !is_rt_sq_empty(&process_signal_queue.rt_signal_queues[sig - SIGRTMIN])) { - __sigaddset(set, sig); - } - } + __sigorset(set, ¤t->signal_queue.pending_mask, &g_process_signal_queue.pending_mask); + + unlock(&g_process_signal_queue_lock); + unlock(¤t->lock); } -static bool append_standard_signal(struct shim_signal** signal_slot, struct shim_signal* signal) { - struct shim_signal* old = NULL; - return __atomic_compare_exchange_n(signal_slot, &old, signal, /*weak=*/false, __ATOMIC_RELEASE, - __ATOMIC_ACQUIRE); +bool have_pending_signals(void) { + struct shim_thread* current = get_cur_thread(); + __sigset_t set; + get_all_pending_signals(&set); + + lock(¤t->lock); + __signotset(&set, &set, ¤t->signal_mask); + unlock(¤t->lock); + + return !__sigisemptyset(&set) || __atomic_load_n(¤t->time_to_die, __ATOMIC_ACQUIRE); } -/* In theory `get_idx` and `put_idx` could overflow, but adding signals with 1GHz (10**9 signals - * per second) gives a 544 years running time before overflow, which we consider a "safe margin" - * for now. */ -static bool append_rt_signal(struct shim_rt_signal_queue* queue, struct shim_signal* signal) { - uint64_t get_idx; - uint64_t put_idx = __atomic_load_n(&queue->put_idx, __ATOMIC_ACQUIRE); - do { - get_idx = __atomic_load_n(&queue->get_idx, __ATOMIC_ACQUIRE); - assert(put_idx >= get_idx); +static bool append_standard_signal(struct shim_signal* queue_slot, struct shim_signal* signal) { + if (has_standard_signal(queue_slot)) { + return false; + } - /* This is a bit racy i.e. it might report full queue, when it's just being emptied, but - * it's the best we can do. Note that `get_idx` can only be increased, but never past - * `put_idx`. */ - if (put_idx - get_idx >= ARRAY_SIZE(queue->queue)) { - return false; - } - } while (!__atomic_compare_exchange_n(&queue->put_idx, &put_idx, put_idx + 1, /*weak=*/false, - __ATOMIC_RELEASE, __ATOMIC_ACQUIRE)); - - queue->queue[put_idx % ARRAY_SIZE(queue->queue)] = signal; + *queue_slot = *signal; return true; } -static bool queue_append_signal(struct shim_signal_queue* queue, struct shim_signal* signal) { - int sig = signal->info.si_signo; - - if (sig < 1 || sig > NUM_SIGS) { - return false; - } else if (sig < SIGRTMIN) { - return append_standard_signal(&queue->standard_signals[sig - 1], signal); - } else { - return append_rt_signal(&queue->rt_signal_queues[sig - SIGRTMIN], signal); +static bool append_rt_signal(struct shim_rt_signal_queue* queue, struct shim_signal** signal) { + assert(queue->get_idx <= queue->put_idx); + if (queue->get_idx >= ARRAY_SIZE(queue->queue)) { + queue->get_idx -= ARRAY_SIZE(queue->queue); + queue->put_idx -= ARRAY_SIZE(queue->queue); } + + if (queue->put_idx - queue->get_idx >= ARRAY_SIZE(queue->queue)) { + return false; + } + + queue->queue[queue->put_idx % ARRAY_SIZE(queue->queue)] = *signal; + *signal = NULL; + queue->put_idx++; + return true; } -static bool append_thread_signal(struct shim_thread* thread, struct shim_signal* signal) { +static bool queue_append_signal(struct shim_signal_queue* queue, struct shim_signal** signal) { + int sig = (*signal)->siginfo.si_signo; + + bool ret = false; + if (sig < 1 || sig > NUM_SIGS) { + ret = false; + } else if (sig < SIGRTMIN) { + ret = append_standard_signal(&queue->standard_signals[sig - 1], *signal); + } else { + ret = append_rt_signal(&queue->rt_signal_queues[sig - SIGRTMIN], signal); + } + + if (ret) { + __sigaddset(&queue->pending_mask, sig); + } + + return ret; +} + +static bool append_thread_signal(struct shim_thread* thread, struct shim_signal** signal) { + lock(&thread->lock); bool ret = queue_append_signal(&thread->signal_queue, signal); if (ret) { (void)__atomic_add_fetch(&thread->pending_signals, 1, __ATOMIC_RELEASE); } + unlock(&thread->lock); return ret; } -static bool append_process_signal(struct shim_signal* signal) { - bool ret = queue_append_signal(&process_signal_queue, signal); +static bool append_process_signal(struct shim_signal** signal) { + lock(&g_process_signal_queue_lock); + bool ret = queue_append_signal(&g_process_signal_queue, signal); if (ret) { - (void)__atomic_add_fetch(&process_pending_signals_cnt, 1, __ATOMIC_RELEASE); + (void)__atomic_add_fetch(&g_process_pending_signals_cnt, 1, __ATOMIC_RELEASE); } + unlock(&g_process_signal_queue_lock); return ret; } -static struct shim_signal* pop_standard_signal(struct shim_signal** signal_slot) { - return __atomic_exchange_n(signal_slot, NULL, __ATOMIC_ACQ_REL); -} - -static struct shim_signal* pop_rt_signal(struct shim_rt_signal_queue* queue) { - uint64_t put_idx; - uint64_t get_idx = __atomic_load_n(&queue->get_idx, __ATOMIC_ACQUIRE); - do { - put_idx = __atomic_load_n(&queue->put_idx, __ATOMIC_ACQUIRE); - assert(put_idx >= get_idx); - - if (put_idx == get_idx) { - return NULL; - } - } while (!__atomic_compare_exchange_n(&queue->get_idx, &get_idx, get_idx + 1, /*weak=*/false, - __ATOMIC_RELEASE, __ATOMIC_ACQUIRE)); - - return queue->queue[get_idx % ARRAY_SIZE(queue->queue)]; -} - -static struct shim_signal* queue_pop_signal(struct shim_signal_queue* queue, int sig) { - if (sig < 1 || sig > NUM_SIGS) { - return NULL; - } else if (sig < SIGRTMIN) { - return pop_standard_signal(&queue->standard_signals[sig - 1]); - } else { - return pop_rt_signal(&queue->rt_signal_queues[sig - SIGRTMIN]); +static bool pop_standard_signal(struct shim_signal* queue_slot, struct shim_signal* signal) { + if (!has_standard_signal(queue_slot)) { + return false; } + + /* Some signal is set, copy it. */ + *signal = *queue_slot; + + /* Mark slot as empty. */ + queue_slot->siginfo.si_signo = 0; + + return true; } -static struct shim_signal* thread_pop_signal(struct shim_thread* thread, int sig) { - struct shim_signal* signal = queue_pop_signal(&thread->signal_queue, sig); - if (signal) { - (void)__atomic_sub_fetch(&thread->pending_signals, 1, __ATOMIC_ACQUIRE); +static bool pop_rt_signal(struct shim_rt_signal_queue* queue, struct shim_signal** signal) { + assert(queue->get_idx <= queue->put_idx); + + if (queue->get_idx < queue->put_idx) { + *signal = queue->queue[queue->get_idx % ARRAY_SIZE(queue->queue)]; + queue->get_idx++; + return true; } - return signal; + return false; } -static struct shim_signal* process_pop_signal(int sig) { - struct shim_signal* signal = queue_pop_signal(&process_signal_queue, sig); - if (signal) { - (void)__atomic_sub_fetch(&process_pending_signals_cnt, 1, __ATOMIC_ACQUIRE); - } - return signal; -} +void free_signal_queue(struct shim_signal_queue* queue) { + /* We ignore standard signals - they are stored by value. */ -void clear_signal_queue(struct shim_signal_queue* queue) { - for (int sig = 1; sig <= NUM_SIGS; sig++) { + for (int sig = SIGRTMIN; sig <= NUM_SIGS; sig++) { struct shim_signal* signal; - while ((signal = queue_pop_signal(queue, sig))) { + while (pop_rt_signal(&queue->rt_signal_queues[sig - SIGRTMIN], &signal)) { free(signal); } } } -static void __handle_one_signal(shim_tcb_t* tcb, struct shim_signal* signal); +static void force_signal(siginfo_t* info) { + struct shim_thread* current = get_cur_thread(); -static void __store_info(siginfo_t* info, struct shim_signal* signal) { - if (info) - signal->info = *info; + current->forced_signal.siginfo = *info; } -void __store_context(shim_tcb_t* tcb, PAL_CONTEXT* pal_context, struct shim_signal* signal) { - ucontext_t* context = &signal->context; - - if (tcb && tcb->context.regs && shim_context_get_syscallnr(&tcb->context)) { - struct shim_context* ct = &tcb->context; - - if (ct->regs) - shim_regs_to_ucontext(context, ct->regs); - - signal->context_stored = true; - return; - } - - if (pal_context) { - pal_context_to_ucontext(context, pal_context); - signal->context_stored = true; - } +static bool have_forced_signal(void) { + struct shim_thread* current = get_cur_thread(); + return current->forced_signal.siginfo.si_signo != 0; } -void deliver_signal(siginfo_t* info, PAL_CONTEXT* context) { - shim_tcb_t* tcb = shim_get_tcb(); - assert(tcb); - - struct shim_thread* cur_thread = (struct shim_thread*)tcb->tp; - assert(cur_thread); - - int sig = info->si_signo; - - int64_t preempt = __disable_preempt(tcb); - - struct shim_signal* signal = __alloca(sizeof(struct shim_signal)); - /* save in signal */ - memset(signal, 0, sizeof(struct shim_signal)); - __store_info(info, signal); - __store_context(tcb, context, signal); - signal->pal_context = context; - - if (preempt > 1 || __sigismember(&cur_thread->signal_mask, sig)) { - signal = malloc_copy(signal, sizeof(struct shim_signal)); - if (signal) { - if (!append_thread_signal(cur_thread, signal)) { - debug("Signal %d queue of thread %u is full, dropping the incoming signal\n", sig, - cur_thread->tid); - free(signal); - } - } - } else { - __handle_one_signal(tcb, signal); - __handle_signals(tcb); - } - - __enable_preempt(tcb); +static void get_forced_signal(struct shim_signal* signal) { + struct shim_thread* current = get_cur_thread(); + *signal = current->forced_signal; + current->forced_signal.siginfo.si_signo = 0; } -#define ALLOC_SIGINFO(signo, code, member, value) \ - ({ \ - siginfo_t* _info = __alloca(sizeof(siginfo_t)); \ - memset(_info, 0, sizeof(siginfo_t)); \ - _info->si_signo = (signo); \ - _info->si_code = (code); \ - _info->member = (value); \ - _info; \ - }) +static bool context_is_libos(PAL_CONTEXT* context) { + uintptr_t ip = pal_context_get_ip(context); -static inline bool context_is_internal(PAL_CONTEXT* context) { - if (!context) - return false; - - void* ip = (void*)pal_context_get_ip(context); - - return (void*)&__load_address <= ip && ip < (void*)&__load_address_end; + return (uintptr_t)&__load_address <= ip && ip < (uintptr_t)&__load_address_end; } static noreturn void internal_fault(const char* errstr, PAL_NUM addr, PAL_CONTEXT* context) { IDTYPE tid = get_cur_tid(); PAL_NUM ip = pal_context_get_ip(context); - if (context_is_internal(context)) + if (context_is_libos(context)) warn("%s at 0x%08lx (IP = +0x%lx, VMID = %u, TID = %u)\n", errstr, addr, (void*)ip - (void*)&__load_address, g_process_ipc_info.vmid, is_internal_tid(tid) ? 0 : tid); @@ -322,76 +321,82 @@ static noreturn void internal_fault(const char* errstr, PAL_NUM addr, PAL_CONTEX DkProcessExit(1); } -static void arithmetic_error_upcall(PAL_NUM arg, PAL_CONTEXT* context) { - if (is_internal_tid(get_cur_tid()) || context_is_internal(context)) { - internal_fault("Internal arithmetic fault", arg, context); - } else { - if (context) - debug("arithmetic fault at 0x%08lx\n", pal_context_get_ip(context)); +static void arithmetic_error_upcall(bool is_in_pal, PAL_NUM addr, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); + assert(!is_in_pal); + assert(context); - deliver_signal(ALLOC_SIGINFO(SIGFPE, FPE_INTDIV, si_addr, (void*)arg), context); + if (is_internal_tid(get_cur_tid()) || context_is_libos(context)) { + internal_fault("Internal arithmetic fault", addr, context); + } else { + debug("arithmetic fault at 0x%08lx\n", pal_context_get_ip(context)); + siginfo_t info = { + .si_signo = SIGFPE, + .si_code = FPE_INTDIV, + .si_addr = (void*)addr, + }; + force_signal(&info); + handle_signal(context, /*old_mask_ptr=*/NULL); } } -static void memfault_upcall(PAL_NUM arg, PAL_CONTEXT* context) { +static void memfault_upcall(bool is_in_pal, PAL_NUM addr, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); + assert(!is_in_pal); + assert(context); + shim_tcb_t* tcb = shim_get_tcb(); assert(tcb); - if (tcb->test_range.cont_addr && (void*)arg >= tcb->test_range.start && - (void*)arg <= tcb->test_range.end) { - assert(context); + if (tcb->test_range.cont_addr && (void*)addr >= tcb->test_range.start && + (void*)addr <= tcb->test_range.end) { + assert(context_is_libos(context)); tcb->test_range.has_fault = true; pal_context_set_ip(context, (PAL_NUM)tcb->test_range.cont_addr); return; } - if (is_internal_tid(get_cur_tid()) || context_is_internal(context)) { - internal_fault("Internal memory fault", arg, context); + if (is_internal_tid(get_cur_tid()) || context_is_libos(context)) { + internal_fault("Internal memory fault", addr, context); } - if (context) - debug("memory fault at 0x%08lx (IP = 0x%08lx)\n", arg, pal_context_get_ip(context)); + debug("memory fault at 0x%08lx (IP = 0x%08lx)\n", addr, pal_context_get_ip(context)); + siginfo_t info = { + .si_addr = (void*)addr, + }; struct shim_vma_info vma_info; - int signo = SIGSEGV; - int code; - if (!arg) { - code = SEGV_MAPERR; - } else if (!lookup_vma((void*)arg, &vma_info)) { + if (!lookup_vma((void*)addr, &vma_info)) { if (vma_info.flags & VMA_INTERNAL) { - internal_fault("Internal memory fault with VMA", arg, context); + internal_fault("Internal memory fault with VMA", addr, context); } struct shim_handle* file = vma_info.file; if (file && file->type == TYPE_FILE) { - /* DEP 3/3/17: If the mapping exceeds end of a file (but is in the VMA) - * then return a SIGBUS. */ - uintptr_t eof_in_vma = (uintptr_t)vma_info.addr + vma_info.file_offset - + file->info.file.size; - if (arg > eof_in_vma) { - signo = SIGBUS; - code = BUS_ADRERR; - } else if (pal_context_has_user_pagefault(context) && !(vma_info.flags & PROT_WRITE)) { - /* DEP 3/3/17: If the page fault gives a write error, and - * the VMA is read-only, return SIGSEGV+SEGV_ACCERR */ - signo = SIGSEGV; - code = SEGV_ACCERR; + /* If the mapping exceeds end of a file then return a SIGBUS. */ + uintptr_t eof_in_vma = (uintptr_t)vma_info.addr + + (file->info.file.size - vma_info.file_offset); + if (addr > eof_in_vma) { + info.si_signo = SIGBUS; + info.si_code = BUS_ADRERR; } else { - /* XXX: need more sophisticated judgement */ - signo = SIGBUS; - code = BUS_ADRERR; + info.si_signo = SIGSEGV; + info.si_code = SEGV_ACCERR; } } else { - code = SEGV_ACCERR; + info.si_signo = SIGSEGV; + info.si_code = SEGV_ACCERR; } if (file) { put_handle(file); } } else { - code = SEGV_MAPERR; + info.si_signo = SIGSEGV; + info.si_code = SEGV_MAPERR; } - deliver_signal(ALLOC_SIGINFO(signo, code, si_addr, (void*)arg), context); + force_signal(&info); + handle_signal(context, /*old_mask_ptr=*/NULL); } /* @@ -412,17 +417,17 @@ static void memfault_upcall(PAL_NUM arg, PAL_CONTEXT* context) { * The second option is faster in fault-free case but cannot be used under * SGX PAL. We use the best option for each PAL for now. */ static bool is_sgx_pal(void) { - static struct atomic_int sgx_pal = {.counter = 0}; - static struct atomic_int inited = {.counter = 0}; + static int sgx_pal = 0; + static int inited = 0; - if (!__atomic_load_n(&inited.counter, __ATOMIC_SEQ_CST)) { - /* Ensure that is_sgx_pal is updated before initialized */ - __atomic_store_n(&sgx_pal.counter, !strcmp(PAL_CB(host_type), "Linux-SGX"), - __ATOMIC_SEQ_CST); - __atomic_store_n(&inited.counter, 1, __ATOMIC_SEQ_CST); + if (!__atomic_load_n(&inited, __ATOMIC_RELAXED)) { + /* Ensure that `sgx_pal` is updated before `inited`. */ + __atomic_store_n(&sgx_pal, !strcmp(PAL_CB(host_type), "Linux-SGX"), __ATOMIC_RELAXED); + COMPILER_BARRIER(); + __atomic_store_n(&inited, 1, __ATOMIC_RELAXED); } - return __atomic_load_n(&sgx_pal.counter, __ATOMIC_SEQ_CST) != 0; + return __atomic_load_n(&sgx_pal, __ATOMIC_RELAXED) != 0; } /* @@ -455,7 +460,6 @@ bool test_user_memory(void* addr, size_t size, bool write) { * a byte of each page; invalid access will be caught in memfault_upcall */ shim_tcb_t* tcb = shim_get_tcb(); assert(tcb && tcb->tp); - __disable_preempt(tcb); /* Add the memory region to the watch list. This is not racy because * each thread has its own record. */ @@ -488,7 +492,6 @@ ret_fault: tcb->test_range.has_fault = false; tcb->test_range.cont_addr = NULL; tcb->test_range.start = tcb->test_range.end = NULL; - __enable_preempt(tcb); return has_fault; } @@ -531,7 +534,6 @@ bool test_user_string(const char* addr) { * a byte of each page; invalid access will be caught in memfault_upcall. */ shim_tcb_t* tcb = shim_get_tcb(); assert(tcb && tcb->tp); - __disable_preempt(tcb); assert(!tcb->test_range.cont_addr); tcb->test_range.has_fault = false; @@ -566,110 +568,82 @@ ret_fault: tcb->test_range.has_fault = false; tcb->test_range.cont_addr = NULL; tcb->test_range.start = tcb->test_range.end = NULL; - __enable_preempt(tcb); return has_fault; } -static void illegal_upcall(PAL_NUM arg, PAL_CONTEXT* context) { +static void illegal_upcall(bool is_in_pal, PAL_NUM addr, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); + assert(!is_in_pal); + assert(context); + struct shim_vma_info vma_info = {.file = NULL}; - - if (!is_internal_tid(get_cur_tid()) && !context_is_internal(context) && - !(lookup_vma((void*)arg, &vma_info)) && !(vma_info.flags & VMA_INTERNAL)) { - assert(context); - - uint8_t* rip = (uint8_t*)pal_context_get_ip(context); - /* - * Emulate syscall instruction (opcode 0x0f 0x05); - * syscall instruction is prohibited in - * Linux-SGX PAL and raises a SIGILL exception and - * Linux PAL with seccomp and raise SIGSYS exception. - */ -#if 0 - if (rip[-2] == 0x0f && rip[-1] == 0x05) { - /* TODO: once finished, remove "#if 0" above. */ - /* - * SIGSYS case (can happen with Linux PAL with seccomp) - * rip points to the address after syscall instruction - * %rcx: syscall instruction must put an - * instruction-after-syscall in rcx - */ - context->rax = siginfo->si_syscall; /* PAL_CONTEXT doesn't - * include a member - * corresponding to - * siginfo_t::si_syscall yet. - */ - context->rcx = (long)rip; - context->r11 = context->efl; - context->rip = (long)&syscall_wrapper; - } else -#endif - if (rip[0] == 0x0f && rip[1] == 0x05) { - /* - * SIGILL case (can happen in Linux-SGX PAL) - * %rcx: syscall instruction must put an instruction-after-syscall - * in rcx. See the syscall_wrapper in syscallas.S - * TODO: check SIGILL and ILL_ILLOPN - */ - context->rcx = (long)rip + 2; - context->r11 = context->efl; - context->rip = (long)&syscall_wrapper; - } else { - debug("Illegal instruction during app execution at 0x%08lx; delivering to app\n", - (unsigned long)rip); - deliver_signal(ALLOC_SIGINFO(SIGILL, ILL_ILLOPC, si_addr, (void*)arg), context); - } - } else { - internal_fault("Illegal instruction during Graphene internal execution", arg, context); + if (is_internal(get_cur_thread()) || context_is_libos(context) + || lookup_vma((void*)addr, &vma_info) || (vma_info.flags & VMA_INTERNAL)) { + internal_fault("Illegal instruction during Graphene internal execution", addr, context); } if (vma_info.file) { put_handle(vma_info.file); } -} -static void quit_upcall(PAL_NUM arg, PAL_CONTEXT* context) { - __UNUSED(arg); - __UNUSED(context); - siginfo_t info = { - .si_signo = SIGTERM, - .si_pid = 0, - .si_code = SI_USER, - }; - if (kill_current_proc(&info) < 0) { - debug("quit_upcall: failed to deliver a signal\n"); + /* Emulate syscall instruction, which is prohibited in Linux-SGX PAL and raises a SIGILL. */ + if (!maybe_emulate_syscall(context)) { + void* rip = (void*)pal_context_get_ip(context); + debug("Illegal instruction during app execution at %p; delivering to app\n", rip); + siginfo_t info = { + .si_signo = SIGILL, + .si_code = ILL_ILLOPC, + .si_addr = (void*)addr, + }; + force_signal(&info); + handle_signal(context, /*old_mask_ptr=*/NULL); } + /* else syscall was emulated. */ } -static void suspend_upcall(PAL_NUM arg, PAL_CONTEXT* context) { - __UNUSED(arg); - __UNUSED(context); - siginfo_t info = { - .si_signo = SIGINT, - .si_pid = 0, - .si_code = SI_USER, - }; - if (kill_current_proc(&info) < 0) { - debug("suspend_upcall: failed to deliver a signal\n"); - } -} +static void quit_upcall(bool is_in_pal, PAL_NUM addr, PAL_CONTEXT* context) { + __UNUSED(addr); -static void resume_upcall(PAL_NUM arg, PAL_CONTEXT* context) { - __UNUSED(arg); - __UNUSED(context); - shim_tcb_t* tcb = shim_get_tcb(); - if (!tcb || !tcb->tp) + if (!g_inject_host_signal_enabled) { return; - - if (!is_internal_tid(get_cur_tid())) { - int64_t preempt = __disable_preempt(tcb); - if (preempt <= 1) - __handle_signals(tcb); - __enable_preempt(tcb); } + + int sig = 0; + static_assert(SAME_TYPE(g_host_injected_signal, sig), "types must match"); + if (!__atomic_compare_exchange_n(&g_host_injected_signal, &sig, SIGTERM, + /*weak=*/false, __ATOMIC_RELAXED, __ATOMIC_RELAXED)) { + /* We already have 1 injected signal, bail out. */ + return; + } + + if (is_internal(get_cur_thread()) || context_is_libos(context) || is_in_pal) { + return; + } + handle_signal(context, /*old_mask_ptr=*/NULL); } -int init_signal(void) { - int ret; +static void interrupted_upcall(bool is_in_pal, PAL_NUM addr, PAL_CONTEXT* context) { + __UNUSED(addr); + + if (is_internal(get_cur_thread()) || context_is_libos(context) || is_in_pal) { + return; + } + handle_signal(context, /*old_mask_ptr=*/NULL); +} + +int init_signal_handling(void) { + if (!create_lock(&g_process_signal_queue_lock)) { + return -ENOMEM; + } + + int64_t allow_injection = 0; + int ret = toml_int_in(g_manifest_root, "sys.enable_sigterm_injection", /*defaultval=*/0, + &allow_injection); + if (ret < 0 || (allow_injection != 0 && allow_injection != 1)) { + debug("Cannot parse 'sys.enable_sigterm_injection' (the value must be 0 or 1)\n"); + return -EINVAL; + } + g_inject_host_signal_enabled = !!allow_injection; int64_t check_invalid_ptrs_int; ret = toml_int_in(g_manifest_root, "libos.check_invalid_pointers", @@ -684,8 +658,7 @@ int init_signal(void) { DkSetExceptionHandler(&memfault_upcall, PAL_EVENT_MEMFAULT); DkSetExceptionHandler(&illegal_upcall, PAL_EVENT_ILLEGAL); DkSetExceptionHandler(&quit_upcall, PAL_EVENT_QUIT); - DkSetExceptionHandler(&suspend_upcall, PAL_EVENT_SUSPEND); - DkSetExceptionHandler(&resume_upcall, PAL_EVENT_RESUME); + DkSetExceptionHandler(&interrupted_upcall, PAL_EVENT_INTERRUPTED); return 0; } @@ -708,144 +681,200 @@ void set_sig_mask(struct shim_thread* thread, const __sigset_t* set) { thread->signal_mask = *set; } -static void get_sighandler(struct shim_thread* thread, int sig, bool allow_reset, - __rt_sighandler_t* handler_ptr, unsigned long* sa_flags_ptr) { - lock(&thread->signal_dispositions->lock); - struct __kernel_sigaction* sig_action = &thread->signal_dispositions->actions[sig - 1]; - +/* XXX: This function assumes that the stack is growing towards lower addresses. */ +bool is_on_altstack(uintptr_t sp, stack_t* alt_stack) { + uintptr_t alt_sp = (uintptr_t)alt_stack->ss_sp; + uintptr_t alt_sp_end = alt_sp + alt_stack->ss_size; /* - * on amd64, sa_handler can be treated as sa_sigaction - * because 1-3 arguments are passed by register and - * sa_handler simply ignores 2nd and 3rd argument. + * If `alt_sp == sp` then either the alternative stack is full or we have another stack + * allocated just above it (at lower address), which is empty and about to be used. Let's + * pretend it is always the second case: overflowing the alternative stack is undefined behavior + * and chances for reaching exactly the top of the stack without overflowing it are minimal. */ -#ifndef __x86_64__ -#error "get_sighandler: see the comment above" -#endif - - __rt_sighandler_t handler = (void*)sig_action->k_sa_handler; - if ((void*)handler == (void*)SIG_IGN) { - handler = NULL; - } else if ((void*)handler == (void*)SIG_DFL) { - handler = default_sighandler[sig - 1]; + if (alt_sp < sp && sp <= alt_sp_end) { + return true; } - - unsigned long sa_flags = sig_action->sa_flags; - - if (allow_reset && handler && sa_flags & SA_RESETHAND) { - sigaction_make_defaults(sig_action); - } - - unlock(&thread->signal_dispositions->lock); - - *handler_ptr = handler; - *sa_flags_ptr = sa_flags; + return false; } -static void __handle_one_signal(shim_tcb_t* tcb, struct shim_signal* signal) { - struct shim_thread* thread = (struct shim_thread*)tcb->tp; - __rt_sighandler_t handler = NULL; - unsigned long sa_flags = 0; +/* XXX: This function assumes that the stack is growing towards lower addresses. */ +uintptr_t get_stack_for_sighandler(uintptr_t sp, bool use_altstack) { + struct shim_thread* current = get_cur_thread(); + stack_t* alt_stack = ¤t->signal_altstack; - int sig = signal->info.si_signo; - - get_sighandler(thread, sig, /*allow_reset=*/true, &handler, &sa_flags); - - if (!handler) - return; - - debug("signal %d handled\n", sig); - - // If the context is never stored in the signal, it means the signal is handled during - // system calls, and before the thread is resumed. - if (!signal->context_stored) - __store_context(tcb, NULL, signal); - - struct shim_context* context = NULL; - - if (tcb->context.regs && shim_context_get_syscallnr(&tcb->context)) { - context = __alloca(sizeof(struct shim_context)); - *context = tcb->context; - shim_context_set_syscallnr(&tcb->context, 0); + if (!use_altstack || alt_stack->ss_flags & SS_DISABLE || alt_stack->ss_size == 0) { + /* No alternative stack. */ + return sp - RED_ZONE_SIZE; } - debug("run signal handler %p (%d, %p, %p)\n", handler, sig, &signal->info, &signal->context); - - (*handler)(sig, &signal->info, &signal->context); - - if (sa_flags & SA_RESTART) { - unsigned char signal_handled = __atomic_load_n(&thread->signal_handled, __ATOMIC_ACQUIRE); - /* Do not overwrite `SIGNAL_HANDLED`, as we want to keep information about signals that do - * not cause syscall restarts. */ - while (signal_handled != SIGNAL_HANDLED) { - if (__atomic_compare_exchange_n(&thread->signal_handled, &signal_handled, - SIGNAL_HANDLED_RESTART, /*weak=*/true, - __ATOMIC_RELEASE, __ATOMIC_ACQUIRE)) { - break; - } - } - } else { - __atomic_store_n(&thread->signal_handled, SIGNAL_HANDLED, __ATOMIC_RELEASE); + if (is_on_altstack(sp, alt_stack)) { + /* We are currently running on alternative stack - just reuse it. */ + return sp - RED_ZONE_SIZE; } - if (context) - tcb->context = *context; - - if (signal->pal_context) - ucontext_to_pal_context(signal->pal_context, &signal->context); + return (uintptr_t)alt_stack->ss_sp + alt_stack->ss_size; } -void __handle_signals(shim_tcb_t* tcb) { - struct shim_thread* thread = tcb->tp; - assert(thread); +/* + * XXX(borysp): This function handles one pending, non-blocked, non-ignored signal at a time, while, + * I believe, normal Linux creates sigframes for all pending, non-blocked, non-ignored signals at + * once. + * Note: each signal handler (at least on Linux x86_64) issues a `rt_sigreturn` syscall to return + * back to the normal context; upon intercepting this syscall by LibOS, `handle_signal` will be + * called again. This way all pending, non-blocked, non-ignored signals will be handled one by one, + * unless the user app changes context in any other way (e.g. `swapcontext`), in which case the next + * signal might be delayed until the next issued syscall. + */ +bool handle_signal(PAL_CONTEXT* context, __sigset_t* old_mask_ptr) { + struct shim_thread* current = get_cur_thread(); + assert(current); + assert(!is_internal(current)); + assert(!context_is_libos(context) || pal_context_get_ip(context) == (uint64_t)&syscalldb); - if (is_internal(thread)) { - return; - } - - if (thread->time_to_die) { + if (__atomic_load_n(¤t->time_to_die, __ATOMIC_ACQUIRE)) { thread_exit(/*error_code=*/0, /*term_signal=*/0); } - while (__atomic_load_n(&thread->pending_signals, __ATOMIC_ACQUIRE) - || __atomic_load_n(&process_pending_signals_cnt, __ATOMIC_ACQUIRE)) { - struct shim_signal* signal = NULL; - + struct shim_signal signal = { 0 }; + if (have_forced_signal()) { + get_forced_signal(&signal); + } else if (__atomic_load_n(¤t->pending_signals, __ATOMIC_ACQUIRE) + || __atomic_load_n(&g_process_pending_signals_cnt, __ATOMIC_ACQUIRE)) { + lock(¤t->lock); + lock(&g_process_signal_queue_lock); for (int sig = 1; sig <= NUM_SIGS; sig++) { - if (!__sigismember(&thread->signal_mask, sig)) { - if ((signal = thread_pop_signal(thread, sig))) { - break; + if (!__sigismember(¤t->signal_mask, sig)) { + bool got = false; + bool was_process = false; + /* First try to handle signals targeted at this thread, then processwide. */ + if (sig < SIGRTMIN) { + got = pop_standard_signal(¤t->signal_queue.standard_signals[sig - 1], + &signal); + if (!got) { + got = pop_standard_signal(&g_process_signal_queue.standard_signals[sig - 1], + &signal); + was_process = true; + } + } else { + struct shim_signal* signal_ptr = NULL; + got = pop_rt_signal(¤t->signal_queue.rt_signal_queues[sig - SIGRTMIN], + &signal_ptr); + if (!got) { + assert(signal_ptr == NULL); + got = + pop_rt_signal(&g_process_signal_queue.rt_signal_queues[sig - SIGRTMIN], + &signal_ptr); + was_process = true; + } + if (signal_ptr) { + assert(got); + signal = *signal_ptr; + free(signal_ptr); + } } - if ((signal = process_pop_signal(sig))) { + + if (got) { + if (was_process) { + (void)__atomic_sub_fetch(&g_process_pending_signals_cnt, 1, + __ATOMIC_RELEASE); + recalc_pending_mask(&g_process_signal_queue, sig); + } else { + (void)__atomic_sub_fetch(¤t->pending_signals, 1, __ATOMIC_RELEASE); + recalc_pending_mask(¤t->signal_queue, sig); + } break; } } } + unlock(&g_process_signal_queue_lock); + unlock(¤t->lock); + } else if (__atomic_load_n(&g_host_injected_signal, __ATOMIC_RELAXED) != 0) { + static_assert(NUM_SIGS < 0xff, "This code requires 0xff to be an invalid signal number"); + int sig = __atomic_exchange_n(&g_host_injected_signal, 0xff, __ATOMIC_RELAXED); + if (sig != 0xff) { + signal.siginfo.si_signo = sig; + signal.siginfo.si_code = SI_USER; + } + } - if (!signal) { - break; + int sig = signal.siginfo.si_signo; + if (!sig) { + return false; + } + + bool ret = false; + lock(¤t->signal_dispositions->lock); + struct __kernel_sigaction* sa = ¤t->signal_dispositions->actions[sig - 1]; + + void* handler = sa->k_sa_handler; + if (handler == SIG_DFL) { + if (default_sighandler[sig - 1] == SIGHANDLER_KILL) { + unlock(¤t->signal_dispositions->lock); + sighandler_kill(sig); + /* Unreachable. */ + } else if (default_sighandler[sig - 1] == SIGHANDLER_CORE) { + unlock(¤t->signal_dispositions->lock); + sighandler_core(sig); + /* Unreachable. */ + } + assert(default_sighandler[sig - 1] == SIGHANDLER_NONE); + handler = SIG_IGN; + } + if (handler != SIG_IGN) { + /* User provided handler. */ + assert(sa->sa_flags & SA_RESTORER); + + long sysnr = shim_get_tcb()->context.syscall_nr; + if (sysnr >= 0) { + switch (pal_context_get_retval(context)) { + case -ERESTARTNOHAND: + pal_context_set_retval(context, -EINTR); + break; + case -ERESTARTSYS: + if (!(sa->sa_flags & SA_RESTART)) { + pal_context_set_retval(context, -EINTR); + break; + } + /* Fallthrough */ + case -ERESTARTNOINTR: + restart_syscall(context, (uint64_t)sysnr); + break; + default: + break; + } } - if (!signal->context_stored) { - __store_context(tcb, NULL, signal); + __sigset_t new_mask = sa->sa_mask; + if (!(sa->sa_flags & SA_NODEFER)) { + __sigaddset(&new_mask, sig); + } + clear_illegal_signals(&new_mask); + + __sigset_t old_mask; + lock(¤t->lock); + get_sig_mask(current, &old_mask); + set_sig_mask(current, &new_mask); + unlock(¤t->lock); + + prepare_sigframe(context, &signal.siginfo, handler, sa->sa_restorer, + !!(sa->sa_flags & SA_ONSTACK), old_mask_ptr ?: &old_mask); + + if (sa->sa_flags & SA_RESETHAND) { + /* borysp: In my opinion it should be `sigaction_make_defaults(sa);`, but Linux does + * this and LTP explicitly tests for this ... */ + sa->k_sa_handler = SIG_DFL; } - __handle_one_signal(tcb, signal); - free(signal); + ret = true; } -} + unlock(¤t->signal_dispositions->lock); -void handle_signals(void) { - shim_tcb_t* tcb = shim_get_tcb(); - assert(tcb); - - int64_t preempt_level = __disable_preempt(tcb); - if (preempt_level == 1) { - /* upon entering this function, preempt level was 0 and thus we can handle signals now - * (otherwise preempt level was 1+, indicating that we are in signal handler; we don't - * support nested sighandling so we defer such signals until preempt level is 0 again) */ - __handle_signals(tcb); + if (!ret) { + /* We have seen an ignored signal, retry. */ + return handle_signal(context, old_mask_ptr); } - __enable_preempt(tcb); + + return true; } int append_signal(struct shim_thread* thread, siginfo_t* info) { @@ -853,23 +882,22 @@ int append_signal(struct shim_thread* thread, siginfo_t* info) { // TODO: ignore SIGCHLD even if it's masked, when handler is set to SIG_IGN (probably not here) + /* For real-time signal we save a pointer to a signal object, so we need to allocate it here. + * If this is a standard signal, this will be freed at return from this function. */ struct shim_signal* signal = malloc(sizeof(*signal)); if (!signal) { return -ENOMEM; } - /* save in signal */ - __store_info(info, signal); - signal->context_stored = false; - signal->pal_context = NULL; + signal->siginfo = *info; if (thread) { - if (append_thread_signal(thread, signal)) { - return 0; + if (append_thread_signal(thread, &signal)) { + goto out; } } else { - if (append_process_signal(signal)) { - return 0; + if (append_process_signal(&signal)) { + goto out; } } @@ -880,57 +908,9 @@ int append_signal(struct shim_thread* thread, siginfo_t* info) { debug("process"); } debug(" is full, dropping the incoming signal\n"); - free(signal); /* This is counter-intuitive, but we report success here: after all signal was successfully * delivered, just the queue was full. */ +out: + free(signal); return 0; } - -static void sighandler_kill(int sig, siginfo_t* info, void* ucontext) { - __UNUSED(info); - __UNUSED(ucontext); - debug("killed by signal %d\n", sig & ~__WCOREDUMP_BIT); - - process_exit(0, sig); -} - -static void sighandler_core(int sig, siginfo_t* info, void* ucontext) { - /* NOTE: This implementation only indicates the core dump for wait4() - * and friends. No actual core-dump file is created. */ - sig = __WCOREDUMP_BIT | sig; - sighandler_kill(sig, info, ucontext); -} - -static __rt_sighandler_t default_sighandler[NUM_SIGS] = { - [SIGHUP - 1] = &sighandler_kill, - [SIGINT - 1] = &sighandler_kill, - [SIGQUIT - 1] = &sighandler_core, - [SIGILL - 1] = &sighandler_core, - [SIGTRAP - 1] = &sighandler_core, - [SIGABRT - 1] = &sighandler_core, - [SIGBUS - 1] = &sighandler_core, - [SIGFPE - 1] = &sighandler_core, - [SIGKILL - 1] = &sighandler_kill, - [SIGUSR1 - 1] = &sighandler_kill, - [SIGSEGV - 1] = &sighandler_core, - [SIGUSR2 - 1] = &sighandler_kill, - [SIGPIPE - 1] = &sighandler_kill, - [SIGALRM - 1] = &sighandler_kill, - [SIGTERM - 1] = &sighandler_kill, - [SIGSTKFLT - 1] = &sighandler_kill, - [SIGCHLD - 1] = NULL, - [SIGCONT - 1] = NULL, - [SIGSTOP - 1] = NULL, - [SIGTSTP - 1] = NULL, - [SIGTTIN - 1] = NULL, - [SIGTTOU - 1] = NULL, - [SIGURG - 1] = NULL, - [SIGXCPU - 1] = &sighandler_core, - [SIGXFSZ - 1] = &sighandler_core, - [SIGVTALRM - 1] = &sighandler_kill, - [SIGPROF - 1] = &sighandler_kill, - [SIGWINCH - 1] = NULL, - [SIGIO - 1] = &sighandler_kill, - [SIGPWR - 1] = &sighandler_kill, - [SIGSYS - 1] = &sighandler_core, - }; diff --git a/LibOS/shim/src/bookkeep/shim_thread.c b/LibOS/shim/src/bookkeep/shim_thread.c index bb7df3da..b2e9e553 100644 --- a/LibOS/shim/src/bookkeep/shim_thread.c +++ b/LibOS/shim/src/bookkeep/shim_thread.c @@ -14,6 +14,8 @@ #include "list.h" #include "pal.h" #include "shim_checkpoint.h" +#include "shim_defs.h" +#include "shim_flags_conv.h" #include "shim_handle.h" #include "shim_internal.h" #include "shim_ipc.h" @@ -21,6 +23,7 @@ #include "shim_process.h" #include "shim_signal.h" #include "shim_thread.h" +#include "shim_vma.h" static IDTYPE g_tid_alloc_idx = 0; @@ -103,6 +106,51 @@ static struct shim_thread* alloc_new_thread(void) { return thread; } +int alloc_thread_libos_stack(struct shim_thread* thread) { + assert(thread->libos_stack_bottom == NULL); + + void* addr = NULL; + int prot = PROT_READ | PROT_WRITE; + int flags = MAP_PRIVATE | MAP_ANONYMOUS | VMA_INTERNAL; + int ret = bkeep_mmap_any(SHIM_THREAD_LIBOS_STACK_SIZE, prot, flags, /*file=*/NULL, /*offset=*/0, + "libos_stack", &addr); + if (ret < 0) { + return ret; + } + + bool need_mem_free = false; + if (DkVirtualMemoryAlloc(addr, SHIM_THREAD_LIBOS_STACK_SIZE, 0, LINUX_PROT_TO_PAL(prot, flags)) + != addr) { + ret = -PAL_ERRNO(); + goto unmap; + } + need_mem_free = true; + + /* Create a stack guard page. */ + if (!DkVirtualMemoryProtect(addr, PAGE_SIZE, PAL_PROT_NONE)) { + ret = -PAL_ERRNO(); + goto unmap; + } + + thread->libos_stack_bottom = (char*)addr + SHIM_THREAD_LIBOS_STACK_SIZE; + + return 0; + +unmap:; + void* tmp_vma = NULL; + if (bkeep_munmap(addr, SHIM_THREAD_LIBOS_STACK_SIZE, /*is_internal=*/true, &tmp_vma) < 0) { + log_error("[alloc_thread_libos_stack]" + " Failed to remove bookkeeped memory that was not allocated at %p-%p!\n", + addr, (char*)addr + SHIM_THREAD_LIBOS_STACK_SIZE); + BUG(); + } + if (need_mem_free) { + DkVirtualMemoryFree(addr, SHIM_THREAD_LIBOS_STACK_SIZE); + } + bkeep_remove_tmp_vma(tmp_vma); + return ret; +} + static int init_main_thread(void) { struct shim_thread* cur_thread = get_cur_thread(); if (cur_thread) { @@ -145,6 +193,14 @@ static int init_main_thread(void) { return -ENOMEM; } + /* TODO: I believe there is some Pal allocated initial stack which could be reused by the first + * thread. Tracked: https://github.com/oscarlab/graphene/issues/2140 */ + int ret = alloc_thread_libos_stack(cur_thread); + if (ret < 0) { + put_thread(cur_thread); + return ret; + } + cur_thread->pal_handle = PAL_CB(first_thread); set_cur_thread(cur_thread); @@ -284,6 +340,18 @@ void put_thread(struct shim_thread* thread) { if (!ref_count) { assert(LIST_EMPTY(thread, list)); + if (thread->libos_stack_bottom) { + void* tmp_vma = NULL; + char* addr = (char*)thread->libos_stack_bottom - SHIM_THREAD_LIBOS_STACK_SIZE; + if (bkeep_munmap(addr, SHIM_THREAD_LIBOS_STACK_SIZE, /*is_internal=*/true, &tmp_vma) < 0) { + debug("[put_thread] Failed to remove bookkeeped memory at %p-%p!\n", + addr, (char*)addr + SHIM_THREAD_LIBOS_STACK_SIZE); + BUG(); + } + DkVirtualMemoryFree(addr, SHIM_THREAD_LIBOS_STACK_SIZE); + bkeep_remove_tmp_vma(tmp_vma); + } + if (thread->pal_handle && thread->pal_handle != PAL_CB(first_thread)) DkObjectClose(thread->pal_handle); @@ -295,7 +363,7 @@ void put_thread(struct shim_thread* thread) { put_signal_dispositions(thread->signal_dispositions); } - clear_signal_queue(&thread->signal_queue); + free_signal_queue(&thread->signal_queue); /* `signal_altstack` is provided by the user, no need for a clean up. */ @@ -487,6 +555,8 @@ BEGIN_CP_FUNC(thread) { INIT_LIST_HEAD(new_thread, list); + new_thread->libos_stack_bottom = NULL; + new_thread->pal_handle = NULL; new_thread->handle_map = NULL; @@ -510,6 +580,10 @@ BEGIN_CP_FUNC(thread) { new_tcb->tp = NULL; new_tcb->debug_buf = NULL; new_tcb->vma_cache = NULL; + + size_t roff = ADD_CP_OFFSET(sizeof(*thread->shim_tcb->context.regs)); + new_thread->shim_tcb->context.regs = (void*)(base + roff); + pal_context_copy(new_thread->shim_tcb->context.regs, thread->shim_tcb->context.regs); } } else { new_thread = (struct shim_thread*)(base + off); @@ -552,22 +626,26 @@ BEGIN_RS_FUNC(thread) { assert(!get_cur_thread()); + int ret = alloc_thread_libos_stack(thread); + if (ret < 0) { + return ret; + } + CP_REBASE(thread->shim_tcb); + CP_REBASE(thread->shim_tcb->context.regs); shim_tcb_t* tcb = shim_get_tcb(); *tcb = *thread->shim_tcb; __shim_tcb_init(tcb); - assert(tcb->context.regs && shim_context_get_sp(&tcb->context)); - update_tls_base(tcb->context.tls_base); - /* Temporarily disable preemption until the thread resumes. */ - __disable_preempt(tcb); + assert(tcb->context.regs); + set_tls(tcb->context.tls); thread->pal_handle = PAL_CB(first_thread); set_cur_thread(thread); - int ret = debug_setbuf(thread->shim_tcb, NULL); + ret = debug_setbuf(thread->shim_tcb, NULL); if (ret < 0) { return ret; } diff --git a/LibOS/shim/src/bookkeep/shim_vma.c b/LibOS/shim/src/bookkeep/shim_vma.c index cf843594..28f3184c 100644 --- a/LibOS/shim/src/bookkeep/shim_vma.c +++ b/LibOS/shim/src/bookkeep/shim_vma.c @@ -298,11 +298,11 @@ static void* _vma_malloc(size_t size) { if (DkVirtualMemoryAlloc(addr, size, 0, PAL_PROT_WRITE | PAL_PROT_READ) != addr) { struct shim_vma* vmas_to_free = NULL; - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); /* Since we are freeing a range we just created, additional vma is not needed. */ int ret = _vma_bkeep_remove((uintptr_t)addr, (uintptr_t)addr + size, /*is_internal=*/true, NULL, &vmas_to_free); - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); if (ret < 0) { debug("Removing a vma we just created failed with %d!\n", ret); BUG(); @@ -456,7 +456,7 @@ static struct shim_vma* alloc_vma(void) { BUG(); } - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); /* Currently `tmp_vma` is always used (added to `vma_tree`), but this assumption could * easily be changed (e.g. if we implement VMAs merging).*/ struct avl_tree_node* node = &tmp_vma.tree_node; @@ -466,7 +466,7 @@ static struct shim_vma* alloc_vma(void) { avl_tree_swap_node(&vma_tree, node, &vma_migrate->tree_node); vma_migrate = NULL; } - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); if (vma_migrate) { free_mem_obj_to_mgr(vma_mgr, vma_migrate); @@ -547,7 +547,7 @@ int init_vma(void) { }, }; - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); int ret = 0; /* First of init_vmas is reserved for later usage. */ for (size_t i = 1; i < ARRAY_SIZE(init_vmas); i++) { @@ -573,7 +573,7 @@ int init_vma(void) { debug("Initial VMA region 0x%lx-0x%lx (%s) bookkeeped\n", init_vmas[i].begin, init_vmas[i].end, init_vmas[i].comment); } - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); /* From now on if we return with an error we might leave a structure local to this function in * vma_tree. We do not bother with removing them - this is initialization of VMA subsystem, if * it fails the whole application startup fails and we should never call any of functions in @@ -630,7 +630,7 @@ int init_vma(void) { } } - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); for (size_t i = 0; i < ARRAY_SIZE(init_vmas); i++) { /* Skip empty areas. */ if (init_vmas[i].begin == init_vmas[i].end) { @@ -640,7 +640,7 @@ int init_vma(void) { avl_tree_swap_node(&vma_tree, &init_vmas[i].tree_node, &vmas_to_migrate_to[i]->tree_node); vmas_to_migrate_to[i] = NULL; } - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); for (size_t i = 0; i < ARRAY_SIZE(vmas_to_migrate_to); i++) { if (vmas_to_migrate_to[i]) { @@ -682,7 +682,7 @@ int bkeep_munmap(void* addr, size_t length, bool is_internal, void** tmp_vma_ptr struct shim_vma* vmas_to_free = NULL; - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); int ret = _vma_bkeep_remove((uintptr_t)addr, (uintptr_t)addr + length, is_internal, vma2 ? &vma2 : NULL, &vmas_to_free); if (ret >= 0) { @@ -690,7 +690,7 @@ int bkeep_munmap(void* addr, size_t length, bool is_internal, void** tmp_vma_ptr *tmp_vma_ptr = (void*)vma1; vma1 = NULL; } - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); free_vmas_freelist(vmas_to_free); if (vma1) { @@ -709,9 +709,9 @@ void bkeep_remove_tmp_vma(void* _vma) { assert(vma->flags == (VMA_INTERNAL | VMA_UNMAPPED)); - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); avl_tree_delete(&vma_tree, &vma->tree_node); - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); free_vma(vma); } @@ -748,7 +748,7 @@ int bkeep_mmap_fixed(void* addr, size_t length, int prot, int flags, struct shim struct shim_vma* vmas_to_free = NULL; - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); int ret = 0; if (flags & MAP_FIXED_NOREPLACE) { struct shim_vma* tmp_vma = _lookup_vma(new_vma->begin); @@ -762,7 +762,7 @@ int bkeep_mmap_fixed(void* addr, size_t length, int prot, int flags, struct shim if (ret >= 0) { avl_tree_insert(&vma_tree, &new_vma->tree_node); } - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); free_vmas_freelist(vmas_to_free); if (vma1) { @@ -912,10 +912,10 @@ int bkeep_mprotect(void* addr, size_t length, int prot, bool is_internal) { return -ENOMEM; } - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); int ret = _vma_bkeep_change((uintptr_t)addr, (uintptr_t)addr + length, prot, is_internal, &vma1, &vma2); - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); if (vma1) { free_vma(vma1); @@ -975,7 +975,7 @@ int bkeep_mmap_any_in_range(void* _bottom_addr, void* _top_addr, size_t length, new_vma->offset = file ? offset : 0; copy_comment(new_vma, comment ?: ""); - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); struct shim_vma* vma = _lookup_vma(top_addr); uintptr_t max_addr; @@ -1013,7 +1013,7 @@ out_found: new_vma = NULL; out: - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); if (new_vma) { free_vma(new_vma); } @@ -1059,7 +1059,7 @@ int lookup_vma(void* addr, struct shim_vma_info* vma_info) { assert(vma_info); int ret = 0; - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); struct shim_vma* vma = _lookup_vma((uintptr_t)addr); if (!vma || !is_addr_in_vma((uintptr_t)addr, vma)) { ret = -ENOENT; @@ -1069,7 +1069,7 @@ int lookup_vma(void* addr, struct shim_vma_info* vma_info) { dump_vma(vma_info, vma); out: - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); return ret; } @@ -1078,7 +1078,7 @@ bool is_in_adjacent_user_vmas(void* addr, size_t length) { uintptr_t end = begin + length; bool ret = false; - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); struct shim_vma* vma = _lookup_vma(begin); if (!vma || begin < vma->begin || (vma->flags & (VMA_INTERNAL | VMA_UNMAPPED))) { goto out; @@ -1094,7 +1094,7 @@ bool is_in_adjacent_user_vmas(void* addr, size_t length) { ret = true; out: - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); return ret; } @@ -1103,7 +1103,7 @@ static size_t dump_all_vmas_with_buf(struct shim_vma_info* infos, size_t max_cou size_t size = 0; struct shim_vma_info* vma_info = infos; - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); struct shim_vma* vma; for (vma = _get_first_vma(); vma; vma = _get_next_vma(vma)) { @@ -1117,7 +1117,7 @@ static size_t dump_all_vmas_with_buf(struct shim_vma_info* infos, size_t max_cou size++; } - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); return size; } @@ -1196,9 +1196,9 @@ int madvise_dontneed_range(uintptr_t begin, uintptr_t end) { .error = 0, }; - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); bool is_continuous = _traverse_vmas_in_range(begin, end, madvise_dontneed_visitor, &ctx); - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); if (!is_continuous) return -ENOMEM; @@ -1372,7 +1372,7 @@ static void debug_print_vma(struct shim_vma* vma) { } void debug_print_all_vmas(void) { - spinlock_lock_signal_off(&vma_tree_lock); + spinlock_lock(&vma_tree_lock); struct shim_vma* vma = _get_first_vma(); while (vma) { @@ -1380,5 +1380,5 @@ void debug_print_all_vmas(void) { vma = _get_next_vma(vma); } - spinlock_unlock_signal_on(&vma_tree_lock); + spinlock_unlock(&vma_tree_lock); } diff --git a/LibOS/shim/src/elf/shim_rtld.c b/LibOS/shim/src/elf/shim_rtld.c index 67cda9c4..7a25ae3b 100644 --- a/LibOS/shim/src/elf/shim_rtld.c +++ b/LibOS/shim/src/elf/shim_rtld.c @@ -1334,35 +1334,17 @@ int remove_loaded_libraries(void) { * parent. Just treat vdso page as user-program data and adjust function pointers for vdso * functions after migration. */ -static void* vdso_addr __attribute_migratable = NULL; -static ElfW(Addr)* __vdso_shim_clock_gettime __attribute_migratable = NULL; -static ElfW(Addr)* __vdso_shim_gettimeofday __attribute_migratable = NULL; -static ElfW(Addr)* __vdso_shim_time __attribute_migratable = NULL; -static ElfW(Addr)* __vdso_shim_getcpu __attribute_migratable = NULL; +static void* vdso_addr __attribute_migratable = NULL; +static ElfW(Addr)* __vdso_syscalldb __attribute_migratable = NULL; static const struct { const char* name; ElfW(Addr) value; ElfW(Addr)** func; } vsyms[] = {{ - .name = "__vdso_shim_clock_gettime", - .value = (ElfW(Addr))&__shim_clock_gettime, - .func = &__vdso_shim_clock_gettime, - }, - { - .name = "__vdso_shim_gettimeofday", - .value = (ElfW(Addr))&__shim_gettimeofday, - .func = &__vdso_shim_gettimeofday, - }, - { - .name = "__vdso_shim_time", - .value = (ElfW(Addr))&__shim_time, - .func = &__vdso_shim_time, - }, - { - .name = "__vdso_shim_getcpu", - .value = (ElfW(Addr))&__shim_getcpu, - .func = &__vdso_shim_getcpu, + .name = "__vdso_syscalldb", + .value = (ElfW(Addr))&syscalldb, + .func = &__vdso_syscalldb, }}; static int vdso_map_init(void) { @@ -1580,10 +1562,6 @@ noreturn void execute_elf_object(struct shim_handle* exec, void* argp, ElfW(auxv /* We are done with using this handle. */ put_handle(exec); - /* Ready to start execution, re-enable preemption. */ - shim_tcb_t* tcb = shim_get_tcb(); - __enable_preempt(tcb); - CALL_ELF_ENTRY(entry, argp); die_or_inf_loop(); diff --git a/LibOS/shim/src/generated-offsets.c b/LibOS/shim/src/generated-offsets.c index 383e47a9..4c46e867 100644 --- a/LibOS/shim/src/generated-offsets.c +++ b/LibOS/shim/src/generated-offsets.c @@ -5,15 +5,15 @@ #include "shim_tcb.h" __attribute__((__used__)) static void dummy(void) { - OFFSET_T(SHIM_TCB_OFFSET, PAL_TCB, libos_tcb); - OFFSET_T(TCB_REGS, shim_tcb_t, context.regs); - OFFSET_T(TCB_FPCW, shim_tcb_t, context.ext_ctx.fpcw); - OFFSET_T(TCB_MXCSR, shim_tcb_t, context.ext_ctx.mxcsr); - OFFSET(SHIM_REGS_RSP, shim_regs, rsp); - OFFSET(SHIM_REGS_R15, shim_regs, r15); - OFFSET(SHIM_REGS_RIP, shim_regs, rip); - DEFINE(SHIM_REGS_SIZE, sizeof(struct shim_regs)); + OFFSET_T(SHIM_TCB_OFF, PAL_TCB, libos_tcb); + OFFSET_T(SHIM_TCB_LIBOS_STACK_OFF, shim_tcb_t, libos_stack_bottom); + OFFSET_T(SHIM_TCB_SCRATCH_PC_OFF, shim_tcb_t, syscall_scratch_pc); - /* definitions */ + OFFSET_T(PAL_CONTEXT_FPREGS_OFF, struct PAL_CONTEXT, fpregs); + OFFSET_T(PAL_CONTEXT_MXCSR_OFF, struct PAL_CONTEXT, mxcsr); + OFFSET_T(PAL_CONTEXT_FPCW_OFF, struct PAL_CONTEXT, fpcw); + OFFSET_T(PAL_CONTEXT_FPREGS_USED_OFF, struct PAL_CONTEXT, is_fpregs_used); + + DEFINE(SHIM_XSTATE_ALIGN, SHIM_XSTATE_ALIGN); DEFINE(RED_ZONE_SIZE, RED_ZONE_SIZE); } diff --git a/LibOS/shim/src/ipc/shim_ipc.c b/LibOS/shim/src/ipc/shim_ipc.c index f6fdaca7..8b7ef091 100644 --- a/LibOS/shim/src/ipc/shim_ipc.c +++ b/LibOS/shim/src/ipc/shim_ipc.c @@ -326,7 +326,7 @@ int send_ipc_message_with_ack(struct shim_ipc_msg_with_ack* msg, struct shim_ipc /* force thread which will send the message to wait for response; * ignore unrelated interrupts but fail on actual errors */ do { - ret = thread_sleep(NO_TIMEOUT); + ret = thread_sleep(NO_TIMEOUT, /*ignore_pending_signals=*/true); if (ret < 0 && ret != -EINTR && ret != -EAGAIN) goto out; } while (ret != 0); diff --git a/LibOS/shim/src/ipc/shim_ipc_helper.c b/LibOS/shim/src/ipc/shim_ipc_helper.c index e500ec9e..856caf5a 100644 --- a/LibOS/shim/src/ipc/shim_ipc_helper.c +++ b/LibOS/shim/src/ipc/shim_ipc_helper.c @@ -808,7 +808,6 @@ noreturn static void shim_ipc_helper(void* dummy) { free(pals); free(pal_events); - __disable_preempt(self->shim_tcb); put_thread(self); debug("IPC helper thread terminated\n"); @@ -830,7 +829,6 @@ static void shim_ipc_helper_prepare(void* arg) { shim_tcb_init(); set_cur_thread(self); - update_tls_base(0); struct debug_buf debug_buf; (void)debug_setbuf(shim_get_tcb(), &debug_buf); diff --git a/LibOS/shim/src/shim_arch_prctl-x86_64.c b/LibOS/shim/src/shim_arch_prctl-x86_64.c new file mode 100644 index 00000000..0150a897 --- /dev/null +++ b/LibOS/shim/src/shim_arch_prctl-x86_64.c @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* Copyright (C) 2014 Stony Brook University + * Copyright (C) 2020 Intel Corporation + * Michał Kowalczyk + * Borys Popławski + */ + +#include + +#include "pal.h" +#include "shim_internal.h" +#include "shim_table.h" +#include "shim_tcb.h" + +long shim_do_arch_prctl(int code, void* addr) { + switch (code) { + case ARCH_SET_FS: + set_tls((unsigned long)addr); + return 0; + + case ARCH_GET_FS: + return DkSegmentRegisterGet(PAL_SEGMENT_FS, addr) ? 0 : -PAL_ERRNO(); + + default: + log_warning("Not supported flag (0x%x) passed to arch_prctl\n", code); + return -ENOSYS; + } +} diff --git a/LibOS/shim/src/shim_async.c b/LibOS/shim/src/shim_async.c index 1d6e72c7..24a2689f 100644 --- a/LibOS/shim/src/shim_async.c +++ b/LibOS/shim/src/shim_async.c @@ -145,7 +145,6 @@ static void shim_async_helper(void* arg) { shim_tcb_init(); set_cur_thread(self); - update_tls_base(0); struct debug_buf debug_buf; (void)debug_setbuf(shim_get_tcb(), &debug_buf); @@ -346,7 +345,6 @@ static void shim_async_helper(void* arg) { } } - __disable_preempt(self->shim_tcb); put_thread(self); debug("Async helper thread terminated\n"); diff --git a/LibOS/shim/src/shim_context-x86_64.c b/LibOS/shim/src/shim_context-x86_64.c index 88113ddd..9b764fc7 100644 --- a/LibOS/shim/src/shim_context-x86_64.c +++ b/LibOS/shim/src/shim_context-x86_64.c @@ -1,32 +1,32 @@ /* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* Copyright (C) 2020 Intel Corporation + * Borys Popławski + */ /* * This file contains code for x86_64-specific CPU context manipulation. */ +#include #include #include "asm-offsets.h" #include "pal.h" #include "shim_context.h" #include "shim_internal.h" +#include "shim_thread.h" +#include "ucontext.h" -/* 512 for legacy regs, 64 for xsave header */ -#define XSTATE_RESET_SIZE (512 + 64) +#define XSTATE_RESET_SIZE (sizeof(struct shim_fpstate)) -bool g_shim_xsave_enabled = false; -uint64_t g_shim_xsave_features = 0; -uint32_t g_shim_xsave_size = 0; +/* By default fall back to old-style FXSAVE. */ +static bool g_shim_xsave_enabled = false; +static uint64_t g_shim_xsave_features = SHIM_XFEATURE_MASK_FPSSE; +static uint32_t g_shim_xsave_size = XSTATE_RESET_SIZE; -const uint32_t g_shim_xstate_reset_state[XSTATE_RESET_SIZE / sizeof(uint32_t)] +static const uint32_t g_shim_xstate_reset_state[XSTATE_RESET_SIZE / sizeof(uint32_t)] __attribute__((aligned(SHIM_XSTATE_ALIGN))) = { - 0x037F, 0, 0, 0, 0, 0, 0x1F80, 0xFFFF, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0x80000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - // XCOMP_BV[63] = 1, compaction mode + 0x037F, 0, 0, 0, 0, 0, 0x1F80, 0xFFFF, }; #define CPUID_FEATURE_XSAVE (1UL << 26) @@ -35,12 +35,11 @@ __attribute__((aligned(SHIM_XSTATE_ALIGN))) = { #define CPUID_LEAF_PROCINFO 0x00000001 #define CPUID_LEAF_XSAVE 0x0000000d -void shim_xstate_init(void) { - /* by default, fall back to old-style FXSAVE (if cannot deduce from CPUID below) */ - g_shim_xsave_enabled = false; - g_shim_xsave_features = SHIM_XFEATURE_MASK_FPSSE; - g_shim_xsave_size = XSTATE_RESET_SIZE; +uint64_t shim_xstate_size(void) { + return g_shim_xsave_size + (g_shim_xsave_enabled ? SHIM_FP_XSTATE_MAGIC2_SIZE : 0); +} +void shim_xstate_init(void) { unsigned int value[4]; if (!DkCpuIdRetrieve(CPUID_LEAF_PROCINFO, 0, value)) goto out; @@ -73,6 +72,8 @@ out: g_shim_xsave_enabled, g_shim_xsave_size, g_shim_xsave_size, g_shim_xsave_features); } +#if 0 +/* Currently not used. */ void shim_xstate_save(void* xstate_extended) { assert(IS_ALIGNED_PTR(xstate_extended, SHIM_XSTATE_ALIGN)); @@ -106,90 +107,203 @@ void shim_xstate_save(void* xstate_extended) { * check FXSAVE/XSAVE size calculations */ *((__typeof__(SHIM_FP_XSTATE_MAGIC2)*)bytes_after_xstate) = SHIM_FP_XSTATE_MAGIC2; } +#endif -void shim_xstate_restore(const void* xstate_extended) { - assert(IS_ALIGNED_PTR(xstate_extended, SHIM_XSTATE_ALIGN)); +__attribute__((used)) static int is_xstate_extended(const struct shim_xstate* xstate) { + assert(IS_ALIGNED_PTR(xstate, SHIM_XSTATE_ALIGN)); - struct shim_xstate* xstate = (struct shim_xstate*)xstate_extended; - char* bytes_after_xstate = (char*)xstate_extended + g_shim_xsave_size; + if (!g_shim_xsave_enabled) { + return 0; + } - struct shim_fpx_sw_bytes* fpx_sw = &xstate->fpstate.sw_reserved; - assert(fpx_sw->magic1 == SHIM_FP_XSTATE_MAGIC1); - assert(fpx_sw->extended_size == g_shim_xsave_size + SHIM_FP_XSTATE_MAGIC2_SIZE); - assert(fpx_sw->xfeatures == g_shim_xsave_features); - assert(fpx_sw->xstate_size == g_shim_xsave_size); - assert(*((__typeof__(SHIM_FP_XSTATE_MAGIC2)*)bytes_after_xstate) == SHIM_FP_XSTATE_MAGIC2); - - __UNUSED(bytes_after_xstate); - __UNUSED(fpx_sw); - - if (g_shim_xsave_enabled) - __builtin_ia32_xrstor64(xstate, /*mask=*/-1LL); - else - __builtin_ia32_fxrstor64(xstate); + const struct shim_fpx_sw_bytes* fpx_sw = &xstate->fpstate.sw_reserved; + if (fpx_sw->magic1 != SHIM_FP_XSTATE_MAGIC1) { + return 0; + } + if (fpx_sw->extended_size > shim_xstate_size()) { + return 0; + } + if (fpx_sw->xfeatures & ~g_shim_xsave_features) { + return 0; + } + if (fpx_sw->xstate_size < sizeof(struct shim_xstate)) { + return 0; + } + if (fpx_sw->xstate_size > g_shim_xsave_size) { + return 0; + } + if (fpx_sw->xstate_size > fpx_sw->extended_size) { + return 0; + } + const void* bytes_after_xstate = (const char*)xstate + fpx_sw->xstate_size; + if (*(const uint32_t*)bytes_after_xstate != SHIM_FP_XSTATE_MAGIC2) { + return 0; + } + return 1; } -void shim_xstate_reset(void) { - shim_xstate_restore(g_shim_xstate_reset_state); +/* Written in asm because we need to make sure it does not touch FPU/SSE after restoring their + * state. Older gcc versions do not support `naked` attribute on x86, hence: */ +__asm__( +".global shim_xstate_restore\n" +".type shim_xstate_restore, @function\n" +"shim_xstate_restore:\n" + "push %rdi\n" + "call is_xstate_extended\n" + "test %eax, %eax\n" + "pop %rdi\n" + "je .Lnot_xstate\n" + + "mov $-1, %eax\n" + "mov %eax, %edx\n" + "xrstor64 (%rdi)\n" + "ret\n" + + ".Lnot_xstate:\n" + "fxrstor64 (%rdi)\n" + "ret\n" +); + +/* Copies FPU state. Returns whether the copied state was xsave-made. */ +static bool shim_xstate_copy(struct shim_xstate* dst, const struct shim_xstate* src) { + if (src == NULL) { + src = (const struct shim_xstate*)g_shim_xstate_reset_state; + } + + size_t copy_size = sizeof(struct shim_fpstate); + int src_is_xstate = is_xstate_extended(src); + if (src_is_xstate) { + copy_size = src->fpstate.sw_reserved.xstate_size + SHIM_FP_XSTATE_MAGIC2_SIZE; + } + + memcpy(dst, src, copy_size); + + if (!src_is_xstate) { + memset(&dst->fpstate.sw_reserved, 0, sizeof(dst->fpstate.sw_reserved)); + } + + return src_is_xstate; } noreturn void restore_child_context_after_clone(struct shim_context* context) { assert(context->regs); - struct shim_regs regs = *context->regs; - debug("restore context: SP = 0x%08lx, IP = 0x%08lx\n", regs.rsp, regs.rip); - /* don't clobber redzone. If sigaltstack is used, - * this area won't be clobbered by signal context */ - *(unsigned long*)(regs.rsp - RED_ZONE_SIZE - 8) = regs.rip; + /* Set 0 as child return value. */ + context->regs->rax = 0; + context->syscall_nr = -1; + + set_tls(context->tls); + + PAL_CONTEXT* regs = context->regs; context->regs = NULL; - /* Ready to resume execution, re-enable preemption. */ - shim_tcb_t* tcb = shim_get_tcb(); - __enable_preempt(tcb); - - __asm__ volatile("fldcw (%0)\r\n" /* restore FP (fpcw) and SSE/AVX/... (mxcsr) control words */ - "ldmxcsr (%1)\r\n" - "movq %2, %%rsp\r\n" - "addq $2 * 8, %%rsp\r\n" /* skip orig_rax and rsp */ - "popq %%r15\r\n" - "popq %%r14\r\n" - "popq %%r13\r\n" - "popq %%r12\r\n" - "popq %%r11\r\n" - "popq %%r10\r\n" - "popq %%r9\r\n" - "popq %%r8\r\n" - "popq %%rcx\r\n" - "popq %%rdx\r\n" - "popq %%rsi\r\n" - "popq %%rdi\r\n" - "popq %%rbx\r\n" - "popq %%rbp\r\n" - "popfq\r\n" - "movq "XSTRINGIFY(SHIM_REGS_RSP)" - "XSTRINGIFY(SHIM_REGS_RIP)"(%%rsp), %%rsp\r\n" - "movq $0, %%rax\r\n" - "jmp *-"XSTRINGIFY(RED_ZONE_SIZE)"-8(%%rsp)\r\n" - :: "g"(&context->ext_ctx.fpcw), "g"(&context->ext_ctx.mxcsr), "g"(®s) : "memory"); - - __builtin_unreachable(); + return_from_syscall(regs); } -/* - * See syscall_wrapper @ syscalldb.S and illegal_upcall() @ shim_signal.c - * for details. - * child thread can _not_ use parent stack. So return right after syscall - * instruction as if syscall_wrapper is executed. - */ -void fixup_child_context(struct shim_regs* regs) { - if (regs->rip == (unsigned long)&syscall_wrapper_after_syscalldb) { - /* - * we don't need to emulate stack pointer change because %rsp is - * initialized to new child user stack passed to clone() system call. - * See the caller of fixup_child_context(). - */ - /* regs->rsp += RED_ZONE_SIZE; */ - regs->rflags = regs->r11; - regs->rip = regs->rcx; +struct sigframe { + ucontext_t uc; + siginfo_t siginfo; +}; + +void prepare_sigframe(PAL_CONTEXT* context, siginfo_t* siginfo, void* handler, void* restorer, + bool should_use_altstack, __sigset_t* old_mask) { + struct shim_thread* current = get_cur_thread(); + + uint64_t stack = get_stack_for_sighandler(context->rsp, should_use_altstack); + + struct shim_xstate* xstate = NULL; + stack = ALIGN_DOWN(stack - shim_xstate_size(), alignof(*xstate)); + xstate = (struct shim_xstate*)stack; + + struct sigframe* sigframe = NULL; + stack = ALIGN_DOWN(stack - sizeof(*sigframe), alignof(*sigframe)); + /* x64 SysV ABI requires that stack is aligned to 8 mod 0x10 after function call, so we have to + * mimic that in signal handler. `sigframe` will be aligned to 0x10 and we will push a return + * value (restorer address) on top of that later on. */ + stack = ALIGN_DOWN(stack, 0x10); + /* Make sure `stack` is now aligned to both `alignof(*sigframe)` and 0x10. */ + static_assert(alignof(*sigframe) % 0x10 == 0 || 0x10 % alignof(*sigframe) == 0, + "Incorrect sigframe alignment"); + + sigframe = (struct sigframe*)stack; + /* This could probably be omited as we set all fields explicitly below. */ + memset(sigframe, 0, sizeof(*sigframe)); + + sigframe->siginfo = *siginfo; + + /* Graphene does not change SS (stack segment register) and assumes that it is constant so + * these flags are not strictly needed, but we do store SS in ucontext so let's just set them. */ + sigframe->uc.uc_flags = UC_SIGCONTEXT_SS | UC_STRICT_RESTORE_SS; + sigframe->uc.uc_link = NULL; + /* TODO: add support for SA_AUTODISARM + * Tracked: https://github.com/oscarlab/graphene/issues/2140 */ + sigframe->uc.uc_stack = current->signal_altstack; + + pal_context_to_ucontext(&sigframe->uc, context); + + /* XXX: Currently we assume that `struct shim_xstate`, `PAL_XREGS_STATE` and `struct _fpstate` + * (just the header) are the very same structure. This mess needs to be fixed. */ + static_assert(sizeof(struct shim_xstate) == sizeof(PAL_XREGS_STATE), + "SSE state structs differ"); + static_assert(sizeof(struct shim_fpstate) == sizeof(struct _fpstate), + "SSE state structs differ"); + if (shim_xstate_copy(xstate, (struct shim_xstate*)sigframe->uc.uc_mcontext.fpstate)) { + /* This is a xsave-made xstate - it has the extended state info. */ + sigframe->uc.uc_flags |= UC_FP_XSTATE; } + sigframe->uc.uc_mcontext.fpstate = (struct _fpstate*)xstate; + + sigframe->uc.uc_sigmask = *old_mask; + + /* We always set all 3 arguments, even if it is not `SA_SIGINFO` handler. We can and it is + * easier this way. */ + context->rdi = (long)siginfo->si_signo; + context->rsi = (uint64_t)&sigframe->siginfo; + context->rdx = (uint64_t)&sigframe->uc; + + stack -= 8; + *(uint64_t*)stack = (uint64_t)restorer; + + context->rip = (uint64_t)handler; + context->rsp = stack; + /* x64 SysV ABI mandates that DF flag is cleared and states that rest of flags is *not* + * preserved across function calls, hence we just set flags to a default value (IF). */ + context->efl = 0x202; + /* If handler was defined as variadic/without prototype it would expect the number of vector + * register arguments in `rax`. */ + context->rax = 0; + + debug("Created sigframe for sig: %d at %p (handler: %p, restorer: %p)\n", + siginfo->si_signo, sigframe, handler, restorer); +} + +void restart_syscall(PAL_CONTEXT* context, uint64_t syscall_nr) { + context->rax = syscall_nr; + context->rip = (uint64_t)&syscalldb; +} + +void restore_sigreturn_context(PAL_CONTEXT* context, __sigset_t* new_mask) { + struct sigframe* sigframe = (struct sigframe*)context->rsp; + *new_mask = sigframe->uc.uc_sigmask; + + struct shim_xstate* syscall_fpregs_buf = (struct shim_xstate*)context->fpregs; + assert(syscall_fpregs_buf); + + ucontext_to_pal_context(context, &sigframe->uc); + + shim_xstate_copy(syscall_fpregs_buf, (struct shim_xstate*)sigframe->uc.uc_mcontext.fpstate); + context->fpregs = (PAL_XREGS_STATE*)syscall_fpregs_buf; + context->is_fpregs_used = 1; +} + +bool maybe_emulate_syscall(PAL_CONTEXT* context) { + uint8_t* rip = (uint8_t*)context->rip; + if (rip[0] == 0x0f && rip[1] == 0x05) { + /* This is syscall instruction, let's emulate it. */ + context->rcx = (uint64_t)rip + 2; + context->rip = (uint64_t)&syscalldb; + return true; + } + return false; } diff --git a/LibOS/shim/src/shim_init.c b/LibOS/shim/src/shim_init.c index efbf29cc..6d29547d 100644 --- a/LibOS/shim/src/shim_init.c +++ b/LibOS/shim/src/shim_init.c @@ -43,7 +43,8 @@ toml_table_t* g_manifest_root = NULL; const unsigned int glibc_version = GLIBC_VERSION; -static void handle_failure(PAL_NUM arg, PAL_CONTEXT* context) { +static void handle_failure(bool is_in_pal, PAL_NUM arg, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); __UNUSED(context); if ((arg <= PAL_ERROR_NATIVE_COUNT) || (arg >= PAL_ERROR_CRYPTO_START && arg <= PAL_ERROR_CRYPTO_END)) @@ -374,9 +375,6 @@ noreturn void* shim_init(int argc, void* args) { /* create the initial TCB, shim can not be run without a tcb */ shim_tcb_init(); - update_tls_base(0); - __disable_preempt(shim_get_tcb()); // Temporarily disable preemption for delaying any signal - // that arrives during initialization struct debug_buf debug_buf; (void)debug_setbuf(shim_get_tcb(), &debug_buf); @@ -420,7 +418,7 @@ noreturn void* shim_init(int argc, void* args) { PAL_NUM ret = DkStreamRead(PAL_CB(parent_process), 0, sizeof(hdr), &hdr, NULL, 0); if (ret == PAL_STREAM_ERROR || ret != sizeof(hdr)) - shim_do_exit(-PAL_ERRNO()); + DkProcessExit(PAL_ERRNO()); assert(hdr.size); RUN_INIT(receive_checkpoint_and_restore, &hdr); @@ -439,8 +437,8 @@ noreturn void* shim_init(int argc, void* args) { RUN_INIT(init_stack, argv, envp, &new_argp, &new_auxv); RUN_INIT(init_loader); + RUN_INIT(init_signal_handling); RUN_INIT(init_ipc_helper); - RUN_INIT(init_signal); if (PAL_CB(parent_process)) { /* Notify the parent process */ @@ -448,19 +446,21 @@ noreturn void* shim_init(int argc, void* args) { PAL_NUM ret = DkStreamWrite(PAL_CB(parent_process), 0, sizeof(child_vmid), &child_vmid, NULL); if (ret == PAL_STREAM_ERROR || ret != sizeof(child_vmid)) - shim_do_exit(-PAL_ERRNO()); + DkProcessExit(PAL_ERRNO()); } debug("Shim process initialized\n"); shim_tcb_t* cur_tcb = shim_get_tcb(); - if (cur_tcb->context.regs && shim_context_get_sp(&cur_tcb->context)) { + if (cur_tcb->context.regs) { vdso_map_migrate(); restore_child_context_after_clone(&cur_tcb->context); /* UNREACHABLE */ } + set_default_tls(); + lock(&g_process.fs_lock); struct shim_handle* exec = g_process.exec; get_handle(exec); @@ -470,7 +470,7 @@ noreturn void* shim_init(int argc, void* args) { /* Passing ownership of `exec` to `execute_elf_object`. */ execute_elf_object(exec, new_argp, new_auxv); } - shim_do_exit(0); + process_exit(0, 0); } static int get_256b_random_hex_string(char* buf, size_t size) { diff --git a/LibOS/shim/src/shim_malloc.c b/LibOS/shim/src/shim_malloc.c index ab9b9e6d..014eebcd 100644 --- a/LibOS/shim/src/shim_malloc.c +++ b/LibOS/shim/src/shim_malloc.c @@ -48,10 +48,7 @@ void* __system_malloc(size_t size) { ret_addr = DkVirtualMemoryAlloc(addr, alloc_size, 0, PAL_PROT_WRITE | PAL_PROT_READ); if (!ret_addr) { - /* If the allocation is interrupted by signal, try to handle the - * signal and then retry the allocation. */ if (PAL_NATIVE_ERRNO() == PAL_ERROR_INTERRUPTED) { - handle_signals(); continue; } diff --git a/LibOS/shim/src/shim_syscalls.c b/LibOS/shim/src/shim_syscalls.c index ae3662ef..2005495a 100644 --- a/LibOS/shim/src/shim_syscalls.c +++ b/LibOS/shim/src/shim_syscalls.c @@ -1,982 +1,63 @@ /* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* Copyright (C) 2014 Stony Brook University - * Copyright (C) 2020 Intel Corporation - * Michał Kowalczyk +/* Copyright (C) 2020 Intel Corporation + * Borys Popławski */ -/* - * This file contains macros to redirect all system calls to the system call table in library OS. - */ - -#if defined(__i386__) || defined(__x86_64__) -#include -#endif -#include -#include - -#include "pal.h" -#include "pal_error.h" +#include "shim_defs.h" #include "shim_internal.h" #include "shim_table.h" #include "shim_tcb.h" -#include "shim_thread.h" #include "shim_types.h" -#include "shim_utils.h" -/* Please place system calls implementations in sys/ directory and name them as the most important - * system call */ - -/* read: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(read, 3, shim_do_read, long, int, fd, void*, buf, size_t, count) - -/* write: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(write, 3, shim_do_write, long, int, fd, const void*, buf, size_t, count) - -/* open: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(open, 3, shim_do_open, long, const char*, file, int, flags, mode_t, mode) - -/* close: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(close, 1, shim_do_close, long, int, fd) - -/* stat: sys/shim_stat.c */ -DEFINE_SHIM_SYSCALL(stat, 2, shim_do_stat, long, const char*, file, struct stat*, statbuf) - -/* fstat: sys/shim_stat.c */ -DEFINE_SHIM_SYSCALL(fstat, 2, shim_do_fstat, long, int, fd, struct stat*, statbuf) - -/* lstat: sys/shim_lstat.c */ -/* for now we don't support symbolic links, so lstat will work exactly the same as stat. */ -DEFINE_SHIM_SYSCALL(lstat, 2, shim_do_lstat, long, const char*, file, struct stat*, statbuf) - -/* poll: sys/shim_poll.c */ -DEFINE_SHIM_SYSCALL(poll, 3, shim_do_poll, long, struct pollfd*, fds, nfds_t, nfds, int, timeout) - -/* lseek: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(lseek, 3, shim_do_lseek, long, int, fd, off_t, offset, int, origin) - -/* mmap: sys/shim_mmap.c */ -DEFINE_SHIM_SYSCALL(mmap, 6, shim_do_mmap, void*, void*, addr, size_t, length, int, prot, int, - flags, int, fd, off_t, offset) - -/* mprotect: sys/shim_mmap.c */ -DEFINE_SHIM_SYSCALL(mprotect, 3, shim_do_mprotect, long, void*, addr, size_t, len, int, prot) - -/* munmap: sys/shim_mmap.c */ -DEFINE_SHIM_SYSCALL(munmap, 2, shim_do_munmap, long, void*, addr, size_t, len) - -DEFINE_SHIM_SYSCALL(brk, 1, shim_do_brk, void*, void*, brk) - -/* rt_sigaction: sys/shim_sigaction.c */ -DEFINE_SHIM_SYSCALL(rt_sigaction, 4, shim_do_sigaction, long, int, signum, - const struct __kernel_sigaction*, act, struct __kernel_sigaction*, oldact, - size_t, sigsetsize) - -/* rt_sigprocmask: sys/shim_sigaction.c */ -DEFINE_SHIM_SYSCALL(rt_sigprocmask, 3, shim_do_sigprocmask, long, int, how, const __sigset_t*, set, - __sigset_t*, oldset) - -/* rt_sigreturn: sys/shim_sigaction.c */ -DEFINE_SHIM_SYSCALL(rt_sigreturn, 1, shim_do_sigreturn, long, int, __unused) - -/* ioctl: sys/shim_ioctl.c */ -DEFINE_SHIM_SYSCALL(ioctl, 3, shim_do_ioctl, long, unsigned int, fd, unsigned int, cmd, unsigned - long, arg) - -/* pread64: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(pread64, 4, shim_do_pread64, long, int, fd, char*, buf, size_t, count, loff_t, - pos) - -/* pwrite64: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(pwrite64, 4, shim_do_pwrite64, long, int, fd, char*, buf, size_t, count, loff_t, - pos) - -/* readv: sys/shim_wrappers.c */ -DEFINE_SHIM_SYSCALL(readv, 3, shim_do_readv, long, int, fd, const struct iovec*, vec, int, vlen) - -/* writev: sys/shim_wrappers.c */ -DEFINE_SHIM_SYSCALL(writev, 3, shim_do_writev, long, int, fd, const struct iovec*, vec, int, vlen) - -/* access: sys/shim_access.c */ -DEFINE_SHIM_SYSCALL(access, 2, shim_do_access, long, const char*, file, mode_t, mode) - -/* pipe: sys/shim_pipe.c */ -DEFINE_SHIM_SYSCALL(pipe, 1, shim_do_pipe, long, int*, fildes) - -/* select: sys/shim_poll.c*/ -DEFINE_SHIM_SYSCALL(select, 5, shim_do_select, long, int, nfds, fd_set*, readfds, fd_set*, writefds, - fd_set*, errorfds, struct __kernel_timeval*, timeout) - -/* sched_yield: sys/shim_sched.c */ -DEFINE_SHIM_SYSCALL(sched_yield, 0, shim_do_sched_yield, long) - -SHIM_SYSCALL_RETURN_ENOSYS(mremap, 5, void*, void*, addr, size_t, old_len, size_t, new_len, int, - flags, void*, new_addr) - -SHIM_SYSCALL_RETURN_ENOSYS(msync, 3, long, void*, start, size_t, len, int, flags) - -/* mincore: sys/shim_mmap.c */ -DEFINE_SHIM_SYSCALL(mincore, 3, shim_do_mincore, long, void*, start, size_t, len, unsigned char*, - vec) - -/* sys/shim_mmap.c */ -DEFINE_SHIM_SYSCALL(madvise, 3, shim_do_madvise, long, unsigned long, start, size_t, len_in, - int, behavior) - -SHIM_SYSCALL_RETURN_ENOSYS(shmget, 3, long, key_t, key, size_t, size, int, shmflg) - -SHIM_SYSCALL_RETURN_ENOSYS(shmat, 3, void*, int, shmid, const void*, shmaddr, int, shmflg) - -SHIM_SYSCALL_RETURN_ENOSYS(shmctl, 3, long, int, shmid, int, cmd, struct shmid_ds*, buf) - -/* dup: sys/shim_dup.c */ -DEFINE_SHIM_SYSCALL(dup, 1, shim_do_dup, long, unsigned int, fd) - -/* dup2: sys/shim_dup.c */ -DEFINE_SHIM_SYSCALL(dup2, 2, shim_do_dup2, long, unsigned int, oldfd, unsigned int, newfd) - -/* pause: sys/shim_sleep.c */ -DEFINE_SHIM_SYSCALL(pause, 0, shim_do_pause, long) - -/* nanosleep: sys/shim_sleep.c */ -DEFINE_SHIM_SYSCALL(nanosleep, 2, shim_do_nanosleep, long, const struct __kernel_timespec*, rqtp, - struct __kernel_timespec*, rmtp) - -/* getitimer: sys/shim_alarm.c */ -DEFINE_SHIM_SYSCALL(getitimer, 2, shim_do_getitimer, long, int, which, struct __kernel_itimerval*, - value) - -/* alarm: sys/shim_alarm.c */ -DEFINE_SHIM_SYSCALL(alarm, 1, shim_do_alarm, long, unsigned int, seconds) - -/* setitimer: sys/shim_alarm.c */ -DEFINE_SHIM_SYSCALL(setitimer, 3, shim_do_setitimer, long, int, which, struct __kernel_itimerval*, - value, struct __kernel_itimerval*, ovalue) - -/* getpid: sys/shim_getpid.c */ -DEFINE_SHIM_SYSCALL(getpid, 0, shim_do_getpid, long) - -/* sendfile: sys/shim_fs.c */ -DEFINE_SHIM_SYSCALL(sendfile, 4, shim_do_sendfile, long, int, out_fd, int, in_fd, off_t*, offset, - size_t, count) - -/* socket: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(socket, 3, shim_do_socket, long, int, family, int, type, int, protocol) - -/* connect: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(connect, 3, shim_do_connect, long, int, sockfd, struct sockaddr*, addr, int, - addrlen) - -/* accept: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(accept, 3, shim_do_accept, long, int, fd, struct sockaddr*, addr, int*, addrlen) - -/* sendto: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(sendto, 6, shim_do_sendto, long, int, fd, const void*, buf, size_t, len, int, - flags, const struct sockaddr*, dest_addr, int, addrlen) - -/* recvfrom: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(recvfrom, 6, shim_do_recvfrom, long, int, fd, void*, buf, size_t, len, int, - flags, struct sockaddr*, addr, int*, addrlen) - -/* bind: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(bind, 3, shim_do_bind, long, int, sockfd, struct sockaddr*, addr, int, addrlen) - -/* listen: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(listen, 2, shim_do_listen, long, int, sockfd, int, backlog) - -/* sendmsg: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(sendmsg, 3, shim_do_sendmsg, long, int, fd, struct msghdr*, msg, int, flags) - -/* recvmsg: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(recvmsg, 3, shim_do_recvmsg, long, int, fd, struct msghdr*, msg, int, flags) - -/* shutdown: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(shutdown, 2, shim_do_shutdown, long, int, sockfd, int, how) - -/* getsockname: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(getsockname, 3, shim_do_getsockname, long, int, sockfd, struct sockaddr*, addr, - int*, addrlen) - -/* getpeername: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(getpeername, 3, shim_do_getpeername, long, int, sockfd, struct sockaddr*, addr, - int*, addrlen) - -/* socketpair: sys/shim_pipe.c */ -DEFINE_SHIM_SYSCALL(socketpair, 4, shim_do_socketpair, long, int, domain, int, type, int, protocol, - int*, sv) - -/* setsockopt: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(setsockopt, 5, shim_do_setsockopt, long, int, fd, int, level, int, optname, - char*, optval, int, optlen) - -/* getsockopt: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(getsockopt, 5, shim_do_getsockopt, long, int, fd, int, level, int, optname, - char*, optval, int*, optlen) - -/* clone: sys/shim_clone.c */ -DEFINE_SHIM_SYSCALL(clone, 5, shim_do_clone, long, unsigned long, flags, unsigned long, - user_stack_addr, int*, parent_tidptr, int*, child_tidptr, unsigned long, tls) - -/* fork: sys/shim_fork.c */ -DEFINE_SHIM_SYSCALL(fork, 0, shim_do_fork, long) - -/* vfork: sys/shim_vfork.c */ -DEFINE_SHIM_SYSCALL(vfork, 0, shim_do_vfork, long) - -/* execve: sys/shim_exec.c */ -DEFINE_SHIM_SYSCALL(execve, 3, shim_do_execve, long, const char*, file, const char**, argv, - const char**, envp) - -/* exit: sys/shim_exit.c */ -DEFINE_SHIM_SYSCALL(exit, 1, shim_do_exit, long, int, error_code) - -/* waitid: sys/shim_wait.c */ -DEFINE_SHIM_SYSCALL(waitid, 5, shim_do_waitid, long, int, which, pid_t, id, siginfo_t*, infop, - int, options, struct __kernel_rusage*, ru) - -/* wait4: sys/shim_wait.c */ -DEFINE_SHIM_SYSCALL(wait4, 4, shim_do_wait4, long, pid_t, pid, int*, stat_addr, int, options, - struct __kernel_rusage*, ru) - -/* kill: sys/shim_sigaction.c */ -DEFINE_SHIM_SYSCALL(kill, 2, shim_do_kill, long, pid_t, pid, int, sig) - -/* uname: sys/shim_uname.c */ -DEFINE_SHIM_SYSCALL(uname, 1, shim_do_uname, long, struct new_utsname*, buf) - -/* semget: sys/shim_semget.c */ -DEFINE_SHIM_SYSCALL(semget, 3, shim_do_semget, long, key_t, key, int, nsems, int, semflg) - -/* semop: sys/shim_semget.c */ -DEFINE_SHIM_SYSCALL(semop, 3, shim_do_semop, long, int, semid, struct sembuf*, sops, unsigned int, - nsops) - -/* semctl: sys/shim_semctl.c */ -DEFINE_SHIM_SYSCALL(semctl, 4, shim_do_semctl, long, int, semid, int, semnum, int, cmd, - unsigned long, arg) - -SHIM_SYSCALL_RETURN_ENOSYS(shmdt, 1, long, const void*, shmaddr) - -/* msgget: sys/shim_msgget.c */ -DEFINE_SHIM_SYSCALL(msgget, 2, shim_do_msgget, long, key_t, key, int, msgflg) - -/* msgsnd: sys/shim_msgget.c */ -DEFINE_SHIM_SYSCALL(msgsnd, 4, shim_do_msgsnd, long, int, msqid, const void*, msgp, size_t, msgsz, - int, msgflg) - -/* msgrcv: sys/shim_msgget.c */ -DEFINE_SHIM_SYSCALL(msgrcv, 5, shim_do_msgrcv, long, int, msqid, void*, msgp, size_t, msgsz, long, - msgtyp, int, msgflg) - -/* msgctl: sys/shim_msgget.c */ -DEFINE_SHIM_SYSCALL(msgctl, 3, shim_do_msgctl, long, int, msqid, int, cmd, struct msqid_ds*, buf) - -/* fcntl: sys/shim_fcntl.c */ -DEFINE_SHIM_SYSCALL(fcntl, 3, shim_do_fcntl, long, int, fd, int, cmd, unsigned long, arg) - -SHIM_SYSCALL_RETURN_ENOSYS(flock, 2, long, int, fd, int, cmd) - -/* fsync: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(fsync, 1, shim_do_fsync, long, int, fd) - -/* fdatasync: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(fdatasync, 1, shim_do_fdatasync, long, int, fd) - -/* truncate: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(truncate, 2, shim_do_truncate, long, const char*, path, loff_t, length) - -/* ftruncate: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(ftruncate, 2, shim_do_ftruncate, long, int, fd, loff_t, length) - -/* getdents: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(getdents, 3, shim_do_getdents, long, int, fd, struct linux_dirent*, buf, - size_t, count) - -/* getcwd: sys/shim_getcwd.c */ -DEFINE_SHIM_SYSCALL(getcwd, 2, shim_do_getcwd, long, char*, buf, size_t, size) - -/* chdir: sys/shim_getcwd.c */ -DEFINE_SHIM_SYSCALL(chdir, 1, shim_do_chdir, long, const char*, filename) - -/* fchdir: sys/shim_getcwd.c */ -DEFINE_SHIM_SYSCALL(fchdir, 1, shim_do_fchdir, long, int, fd) - -/* rename: sys/shim_fs.c */ -DEFINE_SHIM_SYSCALL(rename, 2, shim_do_rename, long, const char*, oldname, const char*, newname) - -/* mkdir: sys/shim_fs.c */ -DEFINE_SHIM_SYSCALL(mkdir, 2, shim_do_mkdir, long, const char*, pathname, int, mode) - -/* rmdir: sys/shim_fs.c */ -DEFINE_SHIM_SYSCALL(rmdir, 1, shim_do_rmdir, long, const char*, pathname) - -DEFINE_SHIM_SYSCALL(creat, 2, shim_do_creat, long, const char*, path, mode_t, mode) - -SHIM_SYSCALL_RETURN_ENOSYS(link, 2, long, const char*, oldname, const char*, newname) - -/* unlink: sys/shim_fs.c */ -DEFINE_SHIM_SYSCALL(unlink, 1, shim_do_unlink, long, const char*, file) - -SHIM_SYSCALL_RETURN_ENOSYS(symlink, 2, long, const char*, old, const char*, new) - -/* readlink: sys/shim_stat.c */ -DEFINE_SHIM_SYSCALL(readlink, 3, shim_do_readlink, long, const char*, path, char*, buf, int, - bufsize) - -DEFINE_SHIM_SYSCALL(chmod, 2, shim_do_chmod, long, const char*, filename, mode_t, mode) - -DEFINE_SHIM_SYSCALL(fchmod, 2, shim_do_fchmod, long, int, fd, mode_t, mode) - -DEFINE_SHIM_SYSCALL(chown, 3, shim_do_chown, long, const char*, filename, uid_t, user, gid_t, group) - -DEFINE_SHIM_SYSCALL(fchown, 3, shim_do_fchown, long, int, fd, uid_t, user, gid_t, group) - -SHIM_SYSCALL_RETURN_ENOSYS(lchown, 3, long, const char*, filename, uid_t, user, gid_t, group) - -DEFINE_SHIM_SYSCALL(umask, 1, shim_do_umask, long, mode_t, mask) - -DEFINE_SHIM_SYSCALL(gettimeofday, 2, shim_do_gettimeofday, long, struct __kernel_timeval*, tv, - struct __kernel_timezone*, tz) - -/* getrlimit: sys/shim_getrlimit.c */ -DEFINE_SHIM_SYSCALL(getrlimit, 2, shim_do_getrlimit, long, int, resource, struct __kernel_rlimit*, - rlim) - -long shim_do_getrusage(int who, struct __kernel_rusage* ru) { - __UNUSED(who); - memset(ru, 0, sizeof(struct __kernel_rusage)); - return -ENOSYS; -} - -DEFINE_SHIM_SYSCALL(getrusage, 2, shim_do_getrusage, long, int, who, struct __kernel_rusage*, ru) - -SHIM_SYSCALL_RETURN_ENOSYS(sysinfo, 1, long, struct sysinfo*, info) - -SHIM_SYSCALL_RETURN_ENOSYS(times, 1, long, struct tms*, tbuf) - -SHIM_SYSCALL_RETURN_ENOSYS(ptrace, 4, long, long, request, pid_t, pid, void*, addr, void*, data) - -/* getuid: sys/shim_getuid.c */ -DEFINE_SHIM_SYSCALL(getuid, 0, shim_do_getuid, long) - -SHIM_SYSCALL_RETURN_ENOSYS(syslog, 3, long, int, type, char*, buf, int, len) - -/* getgid: sys/shim_getuid.c */ -DEFINE_SHIM_SYSCALL(getgid, 0, shim_do_getgid, long) - -/* setuid: sys/shim_getuid.c */ -DEFINE_SHIM_SYSCALL(setuid, 1, shim_do_setuid, long, uid_t, uid) - -/* setgid: sys/shim_getuid.c */ -DEFINE_SHIM_SYSCALL(setgid, 1, shim_do_setgid, long, gid_t, gid) - -/* setgroups: sys/shim_getuid.c */ -DEFINE_SHIM_SYSCALL(setgroups, 2, shim_do_setgroups, long, int, gidsetsize, gid_t*, grouplist) - -/* getgroups: sys/shim_getuid.c */ -DEFINE_SHIM_SYSCALL(getgroups, 2, shim_do_getgroups, long, int, gidsetsize, gid_t*, grouplist) - -/* geteuid: sys/shim_getuid.c */ -DEFINE_SHIM_SYSCALL(geteuid, 0, shim_do_geteuid, long) - -/* getegid: sys/shim_getuid.c */ -DEFINE_SHIM_SYSCALL(getegid, 0, shim_do_getegid, long) - -/* getpgid: sys/shim_getpid.c */ -DEFINE_SHIM_SYSCALL(setpgid, 2, shim_do_setpgid, long, pid_t, pid, pid_t, pgid) - -/* getppid: sys/shim_getpid.c */ -DEFINE_SHIM_SYSCALL(getppid, 0, shim_do_getppid, long) - -/* getpgrp: sys/shim_getpid.c */ -DEFINE_SHIM_SYSCALL(getpgrp, 0, shim_do_getpgrp, long) - -/* setsid: sys/shim_getpid.c */ -DEFINE_SHIM_SYSCALL(setsid, 0, shim_do_setsid, long) - -SHIM_SYSCALL_RETURN_ENOSYS(setreuid, 2, long, uid_t, ruid, uid_t, euid) - -SHIM_SYSCALL_RETURN_ENOSYS(setregid, 2, long, gid_t, rgid, gid_t, egid) - -SHIM_SYSCALL_RETURN_ENOSYS(setresuid, 3, long, uid_t, ruid, uid_t, euid, uid_t, suid) - -SHIM_SYSCALL_RETURN_ENOSYS(getresuid, 3, long, uid_t*, ruid, uid_t*, euid, uid_t*, suid) - -SHIM_SYSCALL_RETURN_ENOSYS(setresgid, 3, long, gid_t, rgid, gid_t, egid, gid_t, sgid) - -SHIM_SYSCALL_RETURN_ENOSYS(getresgid, 3, long, gid_t*, rgid, gid_t*, egid, gid_t*, sgid) - -DEFINE_SHIM_SYSCALL(getpgid, 1, shim_do_getpgid, long, pid_t, pid) - -SHIM_SYSCALL_RETURN_ENOSYS(setfsuid, 1, long, uid_t, uid) - -SHIM_SYSCALL_RETURN_ENOSYS(setfsgid, 1, long, gid_t, gid) - -DEFINE_SHIM_SYSCALL(getsid, 1, shim_do_getsid, long, pid_t, pid) - -SHIM_SYSCALL_RETURN_ENOSYS(capget, 2, long, cap_user_header_t, header, cap_user_data_t, dataptr) - -SHIM_SYSCALL_RETURN_ENOSYS(capset, 2, long, cap_user_header_t, header, const cap_user_data_t, data) - -DEFINE_SHIM_SYSCALL(rt_sigpending, 2, shim_do_sigpending, long, __sigset_t*, set, size_t, - sigsetsize) - -SHIM_SYSCALL_RETURN_ENOSYS(rt_sigtimedwait, 4, long, const __sigset_t*, uthese, siginfo_t*, uinfo, - const struct timespec*, uts, size_t, sigsetsize) - -SHIM_SYSCALL_RETURN_ENOSYS(rt_sigqueueinfo, 3, long, int, pid, int, sig, siginfo_t*, uinfo) - -DEFINE_SHIM_SYSCALL(rt_sigsuspend, 1, shim_do_sigsuspend, long, const __sigset_t*, mask) - -DEFINE_SHIM_SYSCALL(sigaltstack, 2, shim_do_sigaltstack, long, const stack_t*, ss, stack_t*, oss) - -SHIM_SYSCALL_RETURN_ENOSYS(utime, 2, long, char*, filename, struct utimbuf*, times) - -DEFINE_SHIM_SYSCALL(mknod, 3, shim_do_mknod, long, const char*, filename, int, mode, unsigned, dev) - -SHIM_SYSCALL_RETURN_ENOSYS(uselib, 1, long, const char*, library) - -SHIM_SYSCALL_RETURN_ENOSYS(personality, 1, long, unsigned int, personality) - -SHIM_SYSCALL_RETURN_ENOSYS(ustat, 2, long, unsigned, dev, struct __kernel_ustat*, ubuf) - -SHIM_SYSCALL_RETURN_ENOSYS(statfs, 2, long, const char*, path, struct statfs*, buf) - -SHIM_SYSCALL_RETURN_ENOSYS(fstatfs, 2, long, int, fd, struct statfs*, buf) - -SHIM_SYSCALL_RETURN_ENOSYS(sysfs, 3, long, int, option, unsigned long, arg1, unsigned long, arg2) - -DEFINE_SHIM_SYSCALL(setpriority, 3, shim_do_setpriority, long, int, which, int, who, int, niceval) - -DEFINE_SHIM_SYSCALL(getpriority, 2, shim_do_getpriority, long, int, which, int, who) - -DEFINE_SHIM_SYSCALL(sched_setparam, 2, shim_do_sched_setparam, long, pid_t, pid, - struct __kernel_sched_param*, param) - -DEFINE_SHIM_SYSCALL(sched_getparam, 2, shim_do_sched_getparam, long, pid_t, pid, - struct __kernel_sched_param*, param) - -DEFINE_SHIM_SYSCALL(sched_setscheduler, 3, shim_do_sched_setscheduler, long, pid_t, pid, int, - policy, struct __kernel_sched_param*, param) - -DEFINE_SHIM_SYSCALL(sched_getscheduler, 1, shim_do_sched_getscheduler, long, pid_t, pid) - -DEFINE_SHIM_SYSCALL(sched_get_priority_max, 1, shim_do_sched_get_priority_max, long, int, policy) - -DEFINE_SHIM_SYSCALL(sched_get_priority_min, 1, shim_do_sched_get_priority_min, long, int, policy) - -DEFINE_SHIM_SYSCALL(sched_rr_get_interval, 2, shim_do_sched_rr_get_interval, long, pid_t, pid, - struct timespec*, interval) - -SHIM_SYSCALL_RETURN_ENOSYS(mlock, 2, long, void*, start, size_t, len) - -SHIM_SYSCALL_RETURN_ENOSYS(munlock, 2, long, void*, start, size_t, len) - -SHIM_SYSCALL_RETURN_ENOSYS(mlockall, 1, long, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(munlockall, 0, long) - -SHIM_SYSCALL_RETURN_ENOSYS(vhangup, 0, long) - -SHIM_SYSCALL_RETURN_ENOSYS(modify_ldt, 3, long, int, func, void*, ptr, unsigned long, bytecount) - -SHIM_SYSCALL_RETURN_ENOSYS(pivot_root, 2, long, const char*, new_root, const char*, put_old) - -SHIM_SYSCALL_RETURN_ENOSYS(_sysctl, 1, long, struct __kernel_sysctl_args*, args) - -SHIM_SYSCALL_RETURN_ENOSYS(prctl, 5, long, int, option, unsigned long, arg2, unsigned long, arg3, - unsigned long, arg4, unsigned long, arg5) - -#if defined(__i386__) || defined(__x86_64__) -DEFINE_SHIM_SYSCALL(arch_prctl, 2, shim_do_arch_prctl, long, int, code, void*, addr) - -long shim_do_arch_prctl(int code, void* addr) { - if (code != ARCH_SET_FS && code != ARCH_GET_FS) { - debug("Not supported flag (0x%x) passed to arch_prctl\n", code); - return -ENOSYS; +typedef arch_syscall_arg_t (*six_args_syscall_t)(arch_syscall_arg_t, arch_syscall_arg_t, + arch_syscall_arg_t, arch_syscall_arg_t, + arch_syscall_arg_t, arch_syscall_arg_t); + +/* + * `context` is expected to be placed at the bottom of Graphene-internal stack. + * If you change this function please also look at `shim_do_rt_sigsuspend`! + */ +noreturn void shim_emulate_syscall(PAL_CONTEXT* context) { + SHIM_TCB_SET(context.regs, context); + + unsigned long sysnr = pal_context_get_syscall(context); + arch_syscall_arg_t ret = 0; + if (sysnr >= LIBOS_SYSCALL_BOUND || !shim_table[sysnr]) { + log_warning("Unsupported system call %lu\n", sysnr); + ret = -ENOSYS; + goto out; } - switch (code) { - case ARCH_SET_FS: - if (!addr) - return -EINVAL; + SHIM_TCB_SET(context.syscall_nr, sysnr); - update_tls_base((unsigned long)addr); - debug("set fs_base to 0x%lx\n", (unsigned long)addr); - return 0; + six_args_syscall_t syscall_func = (six_args_syscall_t)shim_table[sysnr]; - case ARCH_GET_FS: - return DkSegmentRegisterGet(PAL_SEGMENT_FS, addr) ? 0 : -PAL_ERRNO(); + debug_print_syscall_before(sysnr, ALL_SYSCALL_ARGS(context)); + + ret = syscall_func(ALL_SYSCALL_ARGS(context)); + + debug_print_syscall_after(sysnr, ret, ALL_SYSCALL_ARGS(context)); + +out: + pal_context_set_retval(context, ret); + + /* Some syscalls e.g. `sigreturn` could have changed context and in reality we might be not + * returning from a syscall. */ + if (!handle_signal(context, /*old_mask_ptr=*/NULL) && SHIM_TCB_GET(context.syscall_nr) >= 0) { + switch (ret) { + case -ERESTARTNOHAND: + case -ERESTARTSYS: + case -ERESTARTNOINTR: + restart_syscall(context, sysnr); + break; + default: + break; + } } - return -ENOSYS; + SHIM_TCB_SET(context.syscall_nr, -1); + + SHIM_TCB_SET(context.regs, NULL); + + return_from_syscall(context); } -#endif - -SHIM_SYSCALL_RETURN_ENOSYS(adjtimex, 1, long, struct ____kernel_timex*, txc_p) - -/* setrlimit: sys/shim_getrlimit.c */ -DEFINE_SHIM_SYSCALL(setrlimit, 2, shim_do_setrlimit, long, int, resource, struct __kernel_rlimit*, - rlim) - -/* chroot: sys/shim_isolate.c */ -DEFINE_SHIM_SYSCALL(chroot, 1, shim_do_chroot, long, const char*, filename) - -SHIM_SYSCALL_RETURN_ENOSYS(sync, 0, long) - -SHIM_SYSCALL_RETURN_ENOSYS(acct, 1, long, const char*, name) - -SHIM_SYSCALL_RETURN_ENOSYS(settimeofday, 2, long, struct timeval*, tv, struct __kernel_timezone*, - tz) - -SHIM_SYSCALL_RETURN_ENOSYS(mount, 5, long, char*, dev_name, char*, dir_name, char*, type, - unsigned long, flags, void*, data) - -SHIM_SYSCALL_RETURN_ENOSYS(umount2, 2, long, const char*, target, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(swapon, 2, long, const char*, specialfile, int, swap_flags) - -SHIM_SYSCALL_RETURN_ENOSYS(swapoff, 1, long, const char*, specialfile) - -SHIM_SYSCALL_RETURN_ENOSYS(reboot, 4, long, int, magic1, int, magic2, int, cmd, void*, arg) - -DEFINE_SHIM_SYSCALL(sethostname, 2, shim_do_sethostname, long, char*, name, int, len) - -DEFINE_SHIM_SYSCALL(setdomainname, 2, shim_do_setdomainname, long, char*, name, int, len) - -#if defined(__i386__) || defined(__x86_64__) -SHIM_SYSCALL_RETURN_ENOSYS(iopl, 1, long, int, level) - -SHIM_SYSCALL_RETURN_ENOSYS(ioperm, 3, long, unsigned long, from, unsigned long, num, int, on) -#endif - -SHIM_SYSCALL_RETURN_ENOSYS(create_module, 2, long, const char*, name, size_t, size) - -SHIM_SYSCALL_RETURN_ENOSYS(init_module, 3, long, void*, umod, unsigned long, len, const char*, - uargs) - -SHIM_SYSCALL_RETURN_ENOSYS(delete_module, 2, long, const char*, name_user, unsigned int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(query_module, 5, long, const char*, name, int, which, void*, buf, size_t, - bufsize, size_t*, retsize) - -SHIM_SYSCALL_RETURN_ENOSYS(quotactl, 4, long, int, cmd, const char*, special, qid_t, id, void*, - addr) - -/* gettid: sys/shim_getpid.c */ -DEFINE_SHIM_SYSCALL(gettid, 0, shim_do_gettid, long) - -SHIM_SYSCALL_RETURN_ENOSYS(readahead, 3, long, int, fd, loff_t, offset, size_t, count) - -SHIM_SYSCALL_RETURN_ENOSYS(setxattr, 5, long, const char*, path, const char*, name, const void*, - value, size_t, size, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(lsetxattr, 5, long, const char*, path, const char*, name, const void*, - value, size_t, size, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(fsetxattr, 5, long, int, fd, const char*, name, const void*, value, - size_t, size, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(getxattr, 4, long, const char*, path, const char*, name, void*, value, - size_t, size) - -SHIM_SYSCALL_RETURN_ENOSYS(lgetxattr, 4, long, const char*, path, const char*, name, void*, value, - size_t, size) - -SHIM_SYSCALL_RETURN_ENOSYS(fgetxattr, 4, long, int, fd, const char*, name, void*, value, size_t, - size) - -SHIM_SYSCALL_RETURN_ENOSYS(listxattr, 3, long, const char*, path, char*, list, size_t, size) - -SHIM_SYSCALL_RETURN_ENOSYS(llistxattr, 3, long, const char*, path, char*, list, size_t, size) - -SHIM_SYSCALL_RETURN_ENOSYS(flistxattr, 3, long, int, fd, char*, list, size_t, size) - -SHIM_SYSCALL_RETURN_ENOSYS(removexattr, 2, long, const char*, path, const char*, name) - -SHIM_SYSCALL_RETURN_ENOSYS(lremovexattr, 2, long, const char*, path, const char*, name) - -SHIM_SYSCALL_RETURN_ENOSYS(fremovexattr, 2, long, int, fd, const char*, name) - -DEFINE_SHIM_SYSCALL(tkill, 2, shim_do_tkill, long, pid_t, pid, int, sig) - -DEFINE_SHIM_SYSCALL(time, 1, shim_do_time, long, time_t*, tloc) - -/* futex: sys/shim_futex.c */ -DEFINE_SHIM_SYSCALL(futex, 6, shim_do_futex, long, int*, uaddr, int, op, int, val, void*, utime, - int*, uaddr2, int, val3) - -DEFINE_SHIM_SYSCALL(sched_setaffinity, 3, shim_do_sched_setaffinity, long, pid_t, pid, - unsigned int, len, unsigned long*, user_mask_ptr) - -DEFINE_SHIM_SYSCALL(sched_getaffinity, 3, shim_do_sched_getaffinity, long, pid_t, pid, - unsigned int, len, unsigned long*, user_mask_ptr) - -#if defined(__i386__) || defined(__x86_64__) -SHIM_SYSCALL_RETURN_ENOSYS(set_thread_area, 1, long, struct user_desc*, u_info) -#endif - -/* no glibc wrapper */ - -SHIM_SYSCALL_RETURN_ENOSYS(io_setup, 2, long, unsigned, nr_reqs, aio_context_t*, ctx) - -SHIM_SYSCALL_RETURN_ENOSYS(io_destroy, 1, long, aio_context_t, ctx) - -SHIM_SYSCALL_RETURN_ENOSYS(io_getevents, 5, long, aio_context_t, ctx_id, long, min_nr, long, nr, - struct io_event*, events, struct timespec*, timeout) - -SHIM_SYSCALL_RETURN_ENOSYS(io_submit, 3, long, aio_context_t, ctx_id, long, nr, struct iocb**, - iocbpp) - -SHIM_SYSCALL_RETURN_ENOSYS(io_cancel, 3, long, aio_context_t, ctx_id, struct iocb*, iocb, - struct io_event*, result) - -#if defined(__i386__) || defined(__x86_64__) -SHIM_SYSCALL_RETURN_ENOSYS(get_thread_area, 1, long, struct user_desc*, u_info) -#endif - -SHIM_SYSCALL_RETURN_ENOSYS(lookup_dcookie, 3, long, unsigned long, cookie64, char*, buf, size_t, - len) - -DEFINE_SHIM_SYSCALL(epoll_create, 1, shim_do_epoll_create, long, int, size) - -SHIM_SYSCALL_RETURN_ENOSYS(remap_file_pages, 5, long, void*, start, size_t, size, int, prot, - ssize_t, pgoff, int, flags) - -/* getdents64: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(getdents64, 3, shim_do_getdents64, long, int, fd, struct linux_dirent64*, buf, - size_t, count) - -/* set_tid_address: sys/shim_getpid.c */ -DEFINE_SHIM_SYSCALL(set_tid_address, 1, shim_do_set_tid_address, long, int*, tidptr) - -SHIM_SYSCALL_RETURN_ENOSYS(restart_syscall, 0, long) - -/* semtimedop: sys/shim_semget.c */ -DEFINE_SHIM_SYSCALL(semtimedop, 4, shim_do_semtimedop, long, int, semid, struct sembuf*, sops, - unsigned int, nsops, const struct timespec*, timeout) - -SHIM_SYSCALL_RETURN_ENOSYS(fadvise64, 4, long, int, fd, loff_t, offset, size_t, len, int, advice) - -SHIM_SYSCALL_RETURN_ENOSYS(timer_create, 3, long, clockid_t, which_clock, struct sigevent*, - timer_event_spec, timer_t*, created_timer_id) - -SHIM_SYSCALL_RETURN_ENOSYS(timer_settime, 4, long, timer_t, timer_id, int, flags, - const struct __kernel_itimerspec*, new_setting, - struct __kernel_itimerspec*, old_setting) - -SHIM_SYSCALL_RETURN_ENOSYS(timer_gettime, 2, long, timer_t, timer_id, struct __kernel_itimerspec*, - setting) - -SHIM_SYSCALL_RETURN_ENOSYS(timer_getoverrun, 1, long, timer_t, timer_id) - -SHIM_SYSCALL_RETURN_ENOSYS(timer_delete, 1, long, timer_t, timer_id) - -SHIM_SYSCALL_RETURN_ENOSYS(clock_settime, 2, long, clockid_t, which_clock, const struct timespec*, - tp) - -/* clock_gettime: sys/shim_time.c */ -DEFINE_SHIM_SYSCALL(clock_gettime, 2, shim_do_clock_gettime, long, clockid_t, which_clock, - struct timespec*, tp) - -DEFINE_SHIM_SYSCALL(clock_getres, 2, shim_do_clock_getres, long, clockid_t, which_clock, - struct timespec*, tp) - -/* clock_nanosleep: sys/shim_sleep.c */ -DEFINE_SHIM_SYSCALL(clock_nanosleep, 4, shim_do_clock_nanosleep, long, clockid_t, which_clock, int, - flags, const struct __kernel_timespec*, rqtp, struct __kernel_timespec*, rmtp) - -/* exit_group: sys/shim_exit.c */ -DEFINE_SHIM_SYSCALL(exit_group, 1, shim_do_exit_group, long, int, error_code) - -DEFINE_SHIM_SYSCALL(epoll_wait, 4, shim_do_epoll_wait, long, int, epfd, - struct __kernel_epoll_event*, events, int, maxevents, int, timeout_ms) - -DEFINE_SHIM_SYSCALL(epoll_ctl, 4, shim_do_epoll_ctl, long, int, epfd, int, op, int, fd, - struct __kernel_epoll_event*, event) - -DEFINE_SHIM_SYSCALL(tgkill, 3, shim_do_tgkill, long, pid_t, tgid, pid_t, pid, int, sig) - -SHIM_SYSCALL_RETURN_ENOSYS(utimes, 2, long, char*, filename, struct timeval*, utimes) - -DEFINE_SHIM_SYSCALL(mbind, 6, shim_do_mbind, long, void*, start, unsigned long, len, int, mode, - unsigned long*, nmask, unsigned long, maxnode, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(set_mempolicy, 3, long, int, mode, unsigned long*, nmask, unsigned long, - maxnode) - -SHIM_SYSCALL_RETURN_ENOSYS(get_mempolicy, 5, long, int*, policy, unsigned long*, nmask, - unsigned long, maxnode, unsigned long, addr, unsigned long, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(mq_open, 4, long, const char*, name, int, oflag, mode_t, mode, - struct __kernel_mq_attr*, attr) - -SHIM_SYSCALL_RETURN_ENOSYS(mq_unlink, 1, long, const char*, name) - -SHIM_SYSCALL_RETURN_ENOSYS(mq_timedsend, 5, long, __kernel_mqd_t, mqdes, const char*, msg_ptr, - size_t, msg_len, unsigned int, msg_prio, const struct timespec*, - abs_timeout) - -SHIM_SYSCALL_RETURN_ENOSYS(mq_timedreceive, 5, long, __kernel_mqd_t, mqdes, char*, msg_ptr, size_t, - msg_len, unsigned int*, msg_prio, const struct timespec*, abs_timeout) - -SHIM_SYSCALL_RETURN_ENOSYS(mq_notify, 2, long, __kernel_mqd_t, mqdes, const struct sigevent*, - notification) - -SHIM_SYSCALL_RETURN_ENOSYS(mq_getsetattr, 3, long, __kernel_mqd_t, mqdes, - const struct __kernel_mq_attr*, mqstat, struct __kernel_mq_attr*, - omqstat) - -SHIM_SYSCALL_RETURN_ENOSYS(ioprio_set, 3, long, int, which, int, who, int, ioprio) - -SHIM_SYSCALL_RETURN_ENOSYS(ioprio_get, 2, long, int, which, int, who) - -SHIM_SYSCALL_RETURN_ENOSYS(inotify_init, 0, long) - -SHIM_SYSCALL_RETURN_ENOSYS(inotify_add_watch, 3, long, int, fd, const char*, path, unsigned int, - mask) - -SHIM_SYSCALL_RETURN_ENOSYS(inotify_rm_watch, 2, long, int, fd, unsigned int, wd) - -SHIM_SYSCALL_RETURN_ENOSYS(migrate_pages, 4, long, pid_t, pid, unsigned long, maxnode, - const unsigned long*, from, const unsigned long*, to) - -/* openat: sys/shim_open.c */ -DEFINE_SHIM_SYSCALL(openat, 4, shim_do_openat, long, int, dfd, const char*, filename, int, flags, - int, mode) - -/* mkdirat: sys/shim_fs.c */ -DEFINE_SHIM_SYSCALL(mkdirat, 3, shim_do_mkdirat, long, int, dfd, const char*, pathname, int, mode) - -DEFINE_SHIM_SYSCALL(mknodat, 4, shim_do_mknodat, long, int, dfd, const char*, filename, int, mode, - unsigned, dev) - -DEFINE_SHIM_SYSCALL(fchownat, 5, shim_do_fchownat, long, int, dfd, const char*, filename, uid_t, - user, gid_t, group, int, flag) - -SHIM_SYSCALL_RETURN_ENOSYS(futimesat, 3, long, int, dfd, const char*, filename, struct timeval*, - utimes) - -/* fstatat: sys/shim_stat.c */ -DEFINE_SHIM_SYSCALL(newfstatat, 4, shim_do_newfstatat, long, int, dfd, const char*, filename, - struct stat*, statbuf, int, flag) - -/* unlinkat: sys/shim_fs.c */ -DEFINE_SHIM_SYSCALL(unlinkat, 3, shim_do_unlinkat, long, int, dfd, const char*, pathname, int, flag) - -/* renameat: sys/shim_fs.c */ -DEFINE_SHIM_SYSCALL(renameat, 4, shim_do_renameat, long, int, olddfd, const char*, oldname, int, - newdfd, const char*, newname) - -SHIM_SYSCALL_RETURN_ENOSYS(linkat, 5, long, int, olddfd, const char*, oldname, int, newdfd, - const char*, newname, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(symlinkat, 3, long, const char*, oldname, int, newdfd, const char*, - newname) - -DEFINE_SHIM_SYSCALL(readlinkat, 4, shim_do_readlinkat, long, int, dfd, const char*, path, char*, - buf, int, bufsiz) - -/* fchmodat: sys/shim_fs.c */ -DEFINE_SHIM_SYSCALL(fchmodat, 3, shim_do_fchmodat, long, int, dfd, const char*, filename, mode_t, - mode) - -/* faccessat: sys/shim_access.c */ -DEFINE_SHIM_SYSCALL(faccessat, 3, shim_do_faccessat, long, int, dfd, const char*, filename, int, - mode) - -/* pselect6: sys/shim_poll.c */ -DEFINE_SHIM_SYSCALL(pselect6, 6, shim_do_pselect6, long, int, nfds, fd_set*, readfds, fd_set*, - writefds, fd_set*, errorfds, const struct __kernel_timespec*, tsp, - const __sigset_t*, sigmask) - -/* ppoll: sys/shim_poll.c */ -DEFINE_SHIM_SYSCALL(ppoll, 5, shim_do_ppoll, long, struct pollfd*, fds, int, nfds, struct timespec*, - tsp, const __sigset_t*, sigmask, size_t, sigsetsize) - -SHIM_SYSCALL_RETURN_ENOSYS(unshare, 1, long, int, unshare_flags) - -/* set_robust_list: sys/shim_futex.c */ -DEFINE_SHIM_SYSCALL(set_robust_list, 2, shim_do_set_robust_list, long, struct robust_list_head*, - head, size_t, len) - -/* get_roubust_list: sys/shim_futex.c */ -DEFINE_SHIM_SYSCALL(get_robust_list, 3, shim_do_get_robust_list, long, pid_t, pid, - struct robust_list_head**, head, size_t*, len) - -SHIM_SYSCALL_RETURN_ENOSYS(splice, 6, long, int, fd_in, loff_t*, off_in, int, fd_out, loff_t*, - off_out, size_t, len, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(tee, 4, long, int, fdin, int, fdout, size_t, len, unsigned int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(sync_file_range, 4, long, int, fd, loff_t, offset, loff_t, nbytes, int, - flags) - -SHIM_SYSCALL_RETURN_ENOSYS(vmsplice, 4, long, int, fd, const struct iovec*, iov, unsigned long, - nr_segs, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(move_pages, 6, long, pid_t, pid, unsigned long, nr_pages, void**, pages, - const int*, nodes, int*, status, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(utimensat, 4, long, int, dfd, const char*, filename, struct timespec*, - utimes, int, flags) - -DEFINE_SHIM_SYSCALL(epoll_pwait, 6, shim_do_epoll_pwait, long, int, epfd, - struct __kernel_epoll_event*, events, int, maxevents, int, timeout_ms, - const __sigset_t*, sigmask, size_t, sigsetsize) - -SHIM_SYSCALL_RETURN_ENOSYS(signalfd, 3, long, int, ufd, __sigset_t*, user_mask, size_t, sizemask) - -SHIM_SYSCALL_RETURN_ENOSYS(timerfd_create, 2, long, int, clockid, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(fallocate, 4, long, int, fd, int, mode, loff_t, offset, loff_t, len) - -SHIM_SYSCALL_RETURN_ENOSYS(timerfd_settime, 4, long, int, ufd, int, flags, - const struct __kernel_itimerspec*, utmr, struct __kernel_itimerspec*, - otmr) - -SHIM_SYSCALL_RETURN_ENOSYS(timerfd_gettime, 2, long, int, ufd, struct __kernel_itimerspec*, otmr) - -/* accept4: sys/shim_socket.c */ -DEFINE_SHIM_SYSCALL(accept4, 4, shim_do_accept4, long, int, sockfd, struct sockaddr*, addr, int*, - addrlen, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(signalfd4, 4, long, int, ufd, __sigset_t*, user_mask, size_t, sizemask, - int, flags) - -DEFINE_SHIM_SYSCALL(eventfd, 1, shim_do_eventfd, long, unsigned int, count) - -DEFINE_SHIM_SYSCALL(eventfd2, 2, shim_do_eventfd2, long, unsigned int, count, int, flags) - -/* epoll_create1: sys/shim_epoll.c */ -DEFINE_SHIM_SYSCALL(epoll_create1, 1, shim_do_epoll_create1, long, int, flags) - -/* dup3: sys/shim_dup.c */ -DEFINE_SHIM_SYSCALL(dup3, 3, shim_do_dup3, long, unsigned int, oldfd, unsigned int, newfd, int, - flags) - -/* pipe2: sys/shim_pipe.c */ -DEFINE_SHIM_SYSCALL(pipe2, 2, shim_do_pipe2, long, int*, fildes, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(inotify_init1, 1, long, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(preadv, 5, long, unsigned long, fd, const struct iovec*, vec, - unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h) - -SHIM_SYSCALL_RETURN_ENOSYS(pwritev, 5, long, unsigned long, fd, const struct iovec*, vec, - unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h) - -SHIM_SYSCALL_RETURN_ENOSYS(rt_tgsigqueueinfo, 4, long, pid_t, tgid, pid_t, pid, int, sig, - siginfo_t*, uinfo) - -SHIM_SYSCALL_RETURN_ENOSYS(perf_event_open, 5, long, struct perf_event_attr*, attr_uptr, pid_t, pid, - int, cpu, int, group_fd, int, flags) - -DEFINE_SHIM_SYSCALL(recvmmsg, 5, shim_do_recvmmsg, long, int, fd, struct mmsghdr*, msg, - unsigned int, vlen, int, flags, struct __kernel_timespec*, timeout) - -SHIM_SYSCALL_RETURN_ENOSYS(fanotify_init, 2, long, int, flags, int, event_f_flags) - -SHIM_SYSCALL_RETURN_ENOSYS(fanotify_mark, 5, long, int, fanotify_fd, int, flags, unsigned long, - mask, int, fd, const char*, pathname) - -DEFINE_SHIM_SYSCALL(prlimit64, 4, shim_do_prlimit64, long, pid_t, pid, int, resource, - const struct __kernel_rlimit64*, new_rlim, struct __kernel_rlimit64*, old_rlim) - -SHIM_SYSCALL_RETURN_ENOSYS(name_to_handle_at, 5, long, int, dfd, const char*, name, - struct linux_file_handle*, handle, int*, mnt_id, int, flag) - -SHIM_SYSCALL_RETURN_ENOSYS(open_by_handle_at, 3, long, int, mountdirfd, struct linux_file_handle*, - handle, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(clock_adjtime, 2, long, clockid_t, which_clock, struct timex*, tx) - -SHIM_SYSCALL_RETURN_ENOSYS(syncfs, 1, long, int, fd) - -DEFINE_SHIM_SYSCALL(sendmmsg, 4, shim_do_sendmmsg, long, int, fd, struct mmsghdr*, msg, - unsigned int, vlen, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(setns, 2, long, int, fd, int, nstype) - -DEFINE_SHIM_SYSCALL(getcpu, 3, shim_do_getcpu, long, unsigned*, cpu, unsigned*, node, - struct getcpu_cache*, cache) - -SHIM_SYSCALL_RETURN_ENOSYS(process_vm_readv, 6, long, pid_t, pid, const struct iovec*, lvec, - unsigned long, liovcnt, const struct iovec*, rvec, unsigned long, - riovcnt, unsigned long, flags); - -SHIM_SYSCALL_RETURN_ENOSYS(process_vm_writev, 6, long, pid_t, pid, const struct iovec*, lvec, - unsigned long, liovcnt, const struct iovec*, rvec, - unsigned long, riovcnt, unsigned long, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(kcmp, 5, long, pid_t, pid1, pid_t, pid2, int, type, unsigned long, idx1, - unsigned long, idx2) - -SHIM_SYSCALL_RETURN_ENOSYS(finit_module, 3, long, int, fd, const char*, uargs, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(sched_setattr, 3, long, pid_t, pid, struct sched_attr*, uattr, - unsigned int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(sched_getattr, 4, long, pid_t, pid, struct sched_attr*, uattr, - unsigned int, usize, unsigned int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(renameat2, 5, long, int, olddfd, const char*, oldname, int, newdfd, - const char*, newname, unsigned int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(seccomp, 3, long, unsigned int, op, unsigned int, flags, void*, uargs) - -DEFINE_SHIM_SYSCALL(getrandom, 3, shim_do_getrandom, long, char*, buf, size_t, count, unsigned int, - flags) - -SHIM_SYSCALL_RETURN_ENOSYS(memfd_create, 2, long, const char*, uname, unsigned int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(kexec_file_load, 5, long, int, kernel_fd, int, initrd_fd, unsigned long, - cmdline_len, const char*, cmdline_ptr, unsigned long, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(bpf, 3, long, int, cmd, union bpf_attr*, uattr, unsigned int, size) - -SHIM_SYSCALL_RETURN_ENOSYS(execveat, 5, long, int, fd, const char*, filename, const char* const*, - argv, const char* const*, envp, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(userfaultfd, 1, long, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(membarrier, 2, long, int, cmd, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(mlock2, 3, long, unsigned long, start, size_t, len, int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(copy_file_range, 6, long, int, fd_in, loff_t*, off_in, int, fd_out, - loff_t*, off_out, size_t, len, unsigned int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(preadv2, 6, long, unsigned long, fd, const struct iovec*, vec, - unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h, rwf_t, - flags) - -SHIM_SYSCALL_RETURN_ENOSYS(pwritev2, 6, long, unsigned long, fd, const struct iovec*, vec, - unsigned long, vlen, unsigned long, pos_l, unsigned long, pos_h, rwf_t, - flags) - -SHIM_SYSCALL_RETURN_ENOSYS(pkey_mprotect, 4, long, unsigned long, start, size_t, len, unsigned long, - prot, int, pkey) - -SHIM_SYSCALL_RETURN_ENOSYS(pkey_alloc, 2, long, unsigned long, flags, unsigned long, init_val) - -SHIM_SYSCALL_RETURN_ENOSYS(pkey_free, 1, long, int, pkey) - -SHIM_SYSCALL_RETURN_ENOSYS(statx, 5, long, int, dfd, const char*, filename, unsigned, flags, - unsigned int, mask, struct statx*, buffer) - -SHIM_SYSCALL_RETURN_ENOSYS(io_pgetevents, 6, long, aio_context_t, ctx_id, long, min_nr, long, nr, - struct io_event*, events, struct __kernel_timespec*, timeout, - const struct __aio_sigset*, usig) - -SHIM_SYSCALL_RETURN_ENOSYS(rseq, 4, long, struct rseq*, rseq, u32, rseq_len, int, flags, u32, sig) - -SHIM_SYSCALL_RETURN_ENOSYS(pidfd_send_signal, 4, long, int, pidfd, int, sig, siginfo_t*, info, - unsigned int, flags) - -SHIM_SYSCALL_RETURN_ENOSYS(io_uring_setup, 2, long, u32, entries, struct io_uring_params*, params) - -SHIM_SYSCALL_RETURN_ENOSYS(io_uring_enter, 6, long, unsigned int, fd, u32, to_submit, u32, - min_complete, u32, flags, const sigset_t*, sig, size_t, sigsz) - -SHIM_SYSCALL_RETURN_ENOSYS(io_uring_register, 4, long, unsigned int, fd, unsigned int, opcode, - void*, arg, unsigned int, nr_args) diff --git a/LibOS/shim/src/shim_table-x86_64.c b/LibOS/shim/src/shim_table-x86_64.c index 0503e675..14322260 100644 --- a/LibOS/shim/src/shim_table-x86_64.c +++ b/LibOS/shim/src/shim_table-x86_64.c @@ -2,357 +2,356 @@ /* Copyright (C) 2014 Stony Brook University * Copyright (C) 2020 Intel Corporation * Michał Kowalczyk + * Borys Popławski */ /* * This file contains the system call table. */ +#include + #include "shim_internal.h" #include "shim_table.h" -void debug_unsupp(int num) { - debug("Unsupported system call %d\n", num); -} - shim_fp shim_table[LIBOS_SYSCALL_BOUND] = { - (shim_fp)__shim_read, - (shim_fp)__shim_write, - (shim_fp)__shim_open, - (shim_fp)__shim_close, - (shim_fp)__shim_stat, - (shim_fp)__shim_fstat, - (shim_fp)__shim_lstat, - (shim_fp)__shim_poll, - (shim_fp)__shim_lseek, - (shim_fp)__shim_mmap, - (shim_fp)__shim_mprotect, - (shim_fp)__shim_munmap, - (shim_fp)__shim_brk, - (shim_fp)__shim_rt_sigaction, - (shim_fp)__shim_rt_sigprocmask, - (shim_fp)__shim_rt_sigreturn, - (shim_fp)__shim_ioctl, - (shim_fp)__shim_pread64, - (shim_fp)__shim_pwrite64, - (shim_fp)__shim_readv, - (shim_fp)__shim_writev, - (shim_fp)__shim_access, - (shim_fp)__shim_pipe, - (shim_fp)__shim_select, - (shim_fp)__shim_sched_yield, - (shim_fp)__shim_mremap, - (shim_fp)__shim_msync, - (shim_fp)__shim_mincore, - (shim_fp)__shim_madvise, - (shim_fp)__shim_shmget, - (shim_fp)__shim_shmat, - (shim_fp)__shim_shmctl, - (shim_fp)__shim_dup, - (shim_fp)__shim_dup2, - (shim_fp)__shim_pause, - (shim_fp)__shim_nanosleep, - (shim_fp)__shim_getitimer, - (shim_fp)__shim_alarm, - (shim_fp)__shim_setitimer, - (shim_fp)__shim_getpid, - (shim_fp)__shim_sendfile, - (shim_fp)__shim_socket, - (shim_fp)__shim_connect, - (shim_fp)__shim_accept, - (shim_fp)__shim_sendto, - (shim_fp)__shim_recvfrom, - (shim_fp)__shim_sendmsg, - (shim_fp)__shim_recvmsg, - (shim_fp)__shim_shutdown, - (shim_fp)__shim_bind, - (shim_fp)__shim_listen, - (shim_fp)__shim_getsockname, - (shim_fp)__shim_getpeername, - (shim_fp)__shim_socketpair, - (shim_fp)__shim_setsockopt, - (shim_fp)__shim_getsockopt, - (shim_fp)__shim_clone, - (shim_fp)__shim_fork, - (shim_fp)__shim_vfork, - (shim_fp)__shim_execve, - (shim_fp)__shim_exit, - (shim_fp)__shim_wait4, - (shim_fp)__shim_kill, - (shim_fp)__shim_uname, - (shim_fp)__shim_semget, - (shim_fp)__shim_semop, - (shim_fp)__shim_semctl, - (shim_fp)__shim_shmdt, - (shim_fp)__shim_msgget, - (shim_fp)__shim_msgsnd, - (shim_fp)__shim_msgrcv, - (shim_fp)__shim_msgctl, - (shim_fp)__shim_fcntl, - (shim_fp)__shim_flock, - (shim_fp)__shim_fsync, - (shim_fp)__shim_fdatasync, - (shim_fp)__shim_truncate, - (shim_fp)__shim_ftruncate, - (shim_fp)__shim_getdents, - (shim_fp)__shim_getcwd, - (shim_fp)__shim_chdir, - (shim_fp)__shim_fchdir, - (shim_fp)__shim_rename, - (shim_fp)__shim_mkdir, - (shim_fp)__shim_rmdir, - (shim_fp)__shim_creat, - (shim_fp)__shim_link, - (shim_fp)__shim_unlink, - (shim_fp)__shim_symlink, - (shim_fp)__shim_readlink, - (shim_fp)__shim_chmod, - (shim_fp)__shim_fchmod, - (shim_fp)__shim_chown, - (shim_fp)__shim_fchown, - (shim_fp)__shim_lchown, - (shim_fp)__shim_umask, - (shim_fp)__shim_gettimeofday, - (shim_fp)__shim_getrlimit, - (shim_fp)__shim_getrusage, - (shim_fp)__shim_sysinfo, - (shim_fp)__shim_times, - (shim_fp)__shim_ptrace, - (shim_fp)__shim_getuid, - (shim_fp)__shim_syslog, - (shim_fp)__shim_getgid, - (shim_fp)__shim_setuid, - (shim_fp)__shim_setgid, - (shim_fp)__shim_geteuid, - (shim_fp)__shim_getegid, - (shim_fp)__shim_setpgid, - (shim_fp)__shim_getppid, - (shim_fp)__shim_getpgrp, - (shim_fp)__shim_setsid, - (shim_fp)__shim_setreuid, - (shim_fp)__shim_setregid, - (shim_fp)__shim_getgroups, - (shim_fp)__shim_setgroups, - (shim_fp)__shim_setresuid, - (shim_fp)__shim_getresuid, - (shim_fp)__shim_setresgid, - (shim_fp)__shim_getresgid, - (shim_fp)__shim_getpgid, - (shim_fp)__shim_setfsuid, - (shim_fp)__shim_setfsgid, - (shim_fp)__shim_getsid, - (shim_fp)__shim_capget, - (shim_fp)__shim_capset, - (shim_fp)__shim_rt_sigpending, - (shim_fp)__shim_rt_sigtimedwait, - (shim_fp)__shim_rt_sigqueueinfo, - (shim_fp)__shim_rt_sigsuspend, - (shim_fp)__shim_sigaltstack, - (shim_fp)__shim_utime, - (shim_fp)__shim_mknod, - (shim_fp)__shim_uselib, - (shim_fp)__shim_personality, - (shim_fp)__shim_ustat, - (shim_fp)__shim_statfs, - (shim_fp)__shim_fstatfs, - (shim_fp)__shim_sysfs, - (shim_fp)__shim_getpriority, - (shim_fp)__shim_setpriority, - (shim_fp)__shim_sched_setparam, - (shim_fp)__shim_sched_getparam, - (shim_fp)__shim_sched_setscheduler, - (shim_fp)__shim_sched_getscheduler, - (shim_fp)__shim_sched_get_priority_max, - (shim_fp)__shim_sched_get_priority_min, - (shim_fp)__shim_sched_rr_get_interval, - (shim_fp)__shim_mlock, - (shim_fp)__shim_munlock, - (shim_fp)__shim_mlockall, - (shim_fp)__shim_munlockall, - (shim_fp)__shim_vhangup, - (shim_fp)__shim_modify_ldt, - (shim_fp)__shim_pivot_root, - (shim_fp)__shim__sysctl, - (shim_fp)__shim_prctl, - (shim_fp)__shim_arch_prctl, - (shim_fp)__shim_adjtimex, - (shim_fp)__shim_setrlimit, - (shim_fp)__shim_chroot, - (shim_fp)__shim_sync, - (shim_fp)__shim_acct, - (shim_fp)__shim_settimeofday, - (shim_fp)__shim_mount, - (shim_fp)__shim_umount2, - (shim_fp)__shim_swapon, - (shim_fp)__shim_swapoff, - (shim_fp)__shim_reboot, - (shim_fp)__shim_sethostname, - (shim_fp)__shim_setdomainname, - (shim_fp)__shim_iopl, - (shim_fp)__shim_ioperm, - (shim_fp)__shim_create_module, - (shim_fp)__shim_init_module, - (shim_fp)__shim_delete_module, - (shim_fp)0, // shim_get_kernel_syms, - (shim_fp)__shim_query_module, - (shim_fp)__shim_quotactl, - (shim_fp)0, // shim_nfsservctl, - (shim_fp)0, // shim_getpmsg, - (shim_fp)0, // shim_putpmsg, - (shim_fp)0, // shim_afs_syscall, - (shim_fp)0, // shim_tuxcall, - (shim_fp)0, // shim_security, - (shim_fp)__shim_gettid, - (shim_fp)__shim_readahead, - (shim_fp)__shim_setxattr, - (shim_fp)__shim_lsetxattr, - (shim_fp)__shim_fsetxattr, - (shim_fp)__shim_getxattr, - (shim_fp)__shim_lgetxattr, - (shim_fp)__shim_fgetxattr, - (shim_fp)__shim_listxattr, - (shim_fp)__shim_llistxattr, - (shim_fp)__shim_flistxattr, - (shim_fp)__shim_removexattr, - (shim_fp)__shim_lremovexattr, - (shim_fp)__shim_fremovexattr, - (shim_fp)__shim_tkill, - (shim_fp)__shim_time, - (shim_fp)__shim_futex, - (shim_fp)__shim_sched_setaffinity, - (shim_fp)__shim_sched_getaffinity, - (shim_fp)__shim_set_thread_area, - (shim_fp)__shim_io_setup, - (shim_fp)__shim_io_destroy, - (shim_fp)__shim_io_getevents, - (shim_fp)__shim_io_submit, - (shim_fp)__shim_io_cancel, - (shim_fp)__shim_get_thread_area, - (shim_fp)__shim_lookup_dcookie, - (shim_fp)__shim_epoll_create, - (shim_fp)0, // shim_epoll_ctl_old, - (shim_fp)0, // shim_epoll_wait_old, - (shim_fp)__shim_remap_file_pages, - (shim_fp)__shim_getdents64, - (shim_fp)__shim_set_tid_address, - (shim_fp)__shim_restart_syscall, - (shim_fp)__shim_semtimedop, - (shim_fp)__shim_fadvise64, - (shim_fp)__shim_timer_create, - (shim_fp)__shim_timer_settime, - (shim_fp)__shim_timer_gettime, - (shim_fp)__shim_timer_getoverrun, - (shim_fp)__shim_timer_delete, - (shim_fp)__shim_clock_settime, - (shim_fp)__shim_clock_gettime, - (shim_fp)__shim_clock_getres, - (shim_fp)__shim_clock_nanosleep, - (shim_fp)__shim_exit_group, - (shim_fp)__shim_epoll_wait, - (shim_fp)__shim_epoll_ctl, - (shim_fp)__shim_tgkill, - (shim_fp)__shim_utimes, - (shim_fp)0, // shim_vserver, - (shim_fp)__shim_mbind, - (shim_fp)__shim_set_mempolicy, - (shim_fp)__shim_get_mempolicy, - (shim_fp)__shim_mq_open, - (shim_fp)__shim_mq_unlink, - (shim_fp)__shim_mq_timedsend, - (shim_fp)__shim_mq_timedreceive, - (shim_fp)__shim_mq_notify, - (shim_fp)__shim_mq_getsetattr, - (shim_fp)0, // shim_kexec_load, - (shim_fp)__shim_waitid, - (shim_fp)0, // shim_add_key, - (shim_fp)0, // shim_request_key, - (shim_fp)0, // shim_keyctl, - (shim_fp)__shim_ioprio_set, - (shim_fp)__shim_ioprio_get, - (shim_fp)__shim_inotify_init, - (shim_fp)__shim_inotify_add_watch, - (shim_fp)__shim_inotify_rm_watch, - (shim_fp)__shim_migrate_pages, - (shim_fp)__shim_openat, - (shim_fp)__shim_mkdirat, - (shim_fp)__shim_mknodat, - (shim_fp)__shim_fchownat, - (shim_fp)__shim_futimesat, - (shim_fp)__shim_newfstatat, - (shim_fp)__shim_unlinkat, - (shim_fp)__shim_renameat, - (shim_fp)__shim_linkat, - (shim_fp)__shim_symlinkat, - (shim_fp)__shim_readlinkat, - (shim_fp)__shim_fchmodat, - (shim_fp)__shim_faccessat, - (shim_fp)__shim_pselect6, - (shim_fp)__shim_ppoll, - (shim_fp)__shim_unshare, - (shim_fp)__shim_set_robust_list, - (shim_fp)__shim_get_robust_list, - (shim_fp)__shim_splice, - (shim_fp)__shim_tee, - (shim_fp)__shim_sync_file_range, - (shim_fp)__shim_vmsplice, - (shim_fp)__shim_move_pages, - (shim_fp)__shim_utimensat, - (shim_fp)__shim_epoll_pwait, - (shim_fp)__shim_signalfd, - (shim_fp)__shim_timerfd_create, - (shim_fp)__shim_eventfd, - (shim_fp)__shim_fallocate, - (shim_fp)__shim_timerfd_settime, - (shim_fp)__shim_timerfd_gettime, - (shim_fp)__shim_accept4, - (shim_fp)__shim_signalfd4, - (shim_fp)__shim_eventfd2, - (shim_fp)__shim_epoll_create1, - (shim_fp)__shim_dup3, - (shim_fp)__shim_pipe2, - (shim_fp)__shim_inotify_init1, - (shim_fp)__shim_preadv, - (shim_fp)__shim_pwritev, - (shim_fp)__shim_rt_tgsigqueueinfo, - (shim_fp)__shim_perf_event_open, - (shim_fp)__shim_recvmmsg, - (shim_fp)__shim_fanotify_init, - (shim_fp)__shim_fanotify_mark, - (shim_fp)__shim_prlimit64, - (shim_fp)__shim_name_to_handle_at, - (shim_fp)__shim_open_by_handle_at, - (shim_fp)__shim_clock_adjtime, - (shim_fp)__shim_syncfs, - (shim_fp)__shim_sendmmsg, - (shim_fp)__shim_setns, - (shim_fp)__shim_getcpu, - (shim_fp)__shim_process_vm_readv, - (shim_fp)__shim_process_vm_writev, - (shim_fp)__shim_kcmp, - (shim_fp)__shim_finit_module, - (shim_fp)__shim_sched_setattr, - (shim_fp)__shim_sched_getattr, - (shim_fp)__shim_renameat2, - (shim_fp)__shim_seccomp, - (shim_fp)__shim_getrandom, - (shim_fp)__shim_memfd_create, - (shim_fp)__shim_kexec_file_load, - (shim_fp)__shim_bpf, - (shim_fp)__shim_execveat, - (shim_fp)__shim_userfaultfd, - (shim_fp)__shim_membarrier, - (shim_fp)__shim_mlock2, - (shim_fp)__shim_copy_file_range, - (shim_fp)__shim_preadv2, - (shim_fp)__shim_pwritev2, - (shim_fp)__shim_pkey_mprotect, - (shim_fp)__shim_pkey_alloc, - (shim_fp)__shim_pkey_free, - (shim_fp)__shim_statx, - (shim_fp)__shim_io_pgetevents, - (shim_fp)__shim_rseq, - (shim_fp)__shim_pidfd_send_signal, - (shim_fp)__shim_io_uring_setup, - (shim_fp)__shim_io_uring_enter, - (shim_fp)__shim_io_uring_register, + [__NR_read] = (shim_fp)shim_do_read, + [__NR_write] = (shim_fp)shim_do_write, + [__NR_open] = (shim_fp)shim_do_open, + [__NR_close] = (shim_fp)shim_do_close, + [__NR_stat] = (shim_fp)shim_do_stat, + [__NR_fstat] = (shim_fp)shim_do_fstat, + [__NR_lstat] = (shim_fp)shim_do_lstat, + [__NR_poll] = (shim_fp)shim_do_poll, + [__NR_lseek] = (shim_fp)shim_do_lseek, + [__NR_mmap] = (shim_fp)shim_do_mmap, + [__NR_mprotect] = (shim_fp)shim_do_mprotect, + [__NR_munmap] = (shim_fp)shim_do_munmap, + [__NR_brk] = (shim_fp)shim_do_brk, + [__NR_rt_sigaction] = (shim_fp)shim_do_rt_sigaction, + [__NR_rt_sigprocmask] = (shim_fp)shim_do_rt_sigprocmask, + [__NR_rt_sigreturn] = (shim_fp)shim_do_rt_sigreturn, + [__NR_ioctl] = (shim_fp)shim_do_ioctl, + [__NR_pread64] = (shim_fp)shim_do_pread64, + [__NR_pwrite64] = (shim_fp)shim_do_pwrite64, + [__NR_readv] = (shim_fp)shim_do_readv, + [__NR_writev] = (shim_fp)shim_do_writev, + [__NR_access] = (shim_fp)shim_do_access, + [__NR_pipe] = (shim_fp)shim_do_pipe, + [__NR_select] = (shim_fp)shim_do_select, + [__NR_sched_yield] = (shim_fp)shim_do_sched_yield, + [__NR_mremap] = (shim_fp)0, // shim_do_mremap + [__NR_msync] = (shim_fp)0, // shim_do_msync, + [__NR_mincore] = (shim_fp)shim_do_mincore, + [__NR_madvise] = (shim_fp)shim_do_madvise, + [__NR_shmget] = (shim_fp)0, // shim_do_shmget + [__NR_shmat] = (shim_fp)0, // shim_do_shmat + [__NR_shmctl] = (shim_fp)0, // shim_do_shmctl + [__NR_dup] = (shim_fp)shim_do_dup, + [__NR_dup2] = (shim_fp)shim_do_dup2, + [__NR_pause] = (shim_fp)shim_do_pause, + [__NR_nanosleep] = (shim_fp)shim_do_nanosleep, + [__NR_getitimer] = (shim_fp)shim_do_getitimer, + [__NR_alarm] = (shim_fp)shim_do_alarm, + [__NR_setitimer] = (shim_fp)shim_do_setitimer, + [__NR_getpid] = (shim_fp)shim_do_getpid, + [__NR_sendfile] = (shim_fp)shim_do_sendfile, + [__NR_socket] = (shim_fp)shim_do_socket, + [__NR_connect] = (shim_fp)shim_do_connect, + [__NR_accept] = (shim_fp)shim_do_accept, + [__NR_sendto] = (shim_fp)shim_do_sendto, + [__NR_recvfrom] = (shim_fp)shim_do_recvfrom, + [__NR_sendmsg] = (shim_fp)shim_do_sendmsg, + [__NR_recvmsg] = (shim_fp)shim_do_recvmsg, + [__NR_shutdown] = (shim_fp)shim_do_shutdown, + [__NR_bind] = (shim_fp)shim_do_bind, + [__NR_listen] = (shim_fp)shim_do_listen, + [__NR_getsockname] = (shim_fp)shim_do_getsockname, + [__NR_getpeername] = (shim_fp)shim_do_getpeername, + [__NR_socketpair] = (shim_fp)shim_do_socketpair, + [__NR_setsockopt] = (shim_fp)shim_do_setsockopt, + [__NR_getsockopt] = (shim_fp)shim_do_getsockopt, + [__NR_clone] = (shim_fp)shim_do_clone, + [__NR_fork] = (shim_fp)shim_do_fork, + [__NR_vfork] = (shim_fp)shim_do_vfork, + [__NR_execve] = (shim_fp)shim_do_execve, + [__NR_exit] = (shim_fp)shim_do_exit, + [__NR_wait4] = (shim_fp)shim_do_wait4, + [__NR_kill] = (shim_fp)shim_do_kill, + [__NR_uname] = (shim_fp)shim_do_uname, + [__NR_semget] = (shim_fp)shim_do_semget, + [__NR_semop] = (shim_fp)shim_do_semop, + [__NR_semctl] = (shim_fp)shim_do_semctl, + [__NR_shmdt] = (shim_fp)0, // shim_do_shmdt + [__NR_msgget] = (shim_fp)shim_do_msgget, + [__NR_msgsnd] = (shim_fp)shim_do_msgsnd, + [__NR_msgrcv] = (shim_fp)shim_do_msgrcv, + [__NR_msgctl] = (shim_fp)shim_do_msgctl, + [__NR_fcntl] = (shim_fp)shim_do_fcntl, + [__NR_flock] = (shim_fp)0, // shim_do_flock + [__NR_fsync] = (shim_fp)shim_do_fsync, + [__NR_fdatasync] = (shim_fp)shim_do_fdatasync, + [__NR_truncate] = (shim_fp)shim_do_truncate, + [__NR_ftruncate] = (shim_fp)shim_do_ftruncate, + [__NR_getdents] = (shim_fp)shim_do_getdents, + [__NR_getcwd] = (shim_fp)shim_do_getcwd, + [__NR_chdir] = (shim_fp)shim_do_chdir, + [__NR_fchdir] = (shim_fp)shim_do_fchdir, + [__NR_rename] = (shim_fp)shim_do_rename, + [__NR_mkdir] = (shim_fp)shim_do_mkdir, + [__NR_rmdir] = (shim_fp)shim_do_rmdir, + [__NR_creat] = (shim_fp)shim_do_creat, + [__NR_link] = (shim_fp)0, // shim_do_link + [__NR_unlink] = (shim_fp)shim_do_unlink, + [__NR_symlink] = (shim_fp)0, // shim_do_symlink + [__NR_readlink] = (shim_fp)shim_do_readlink, + [__NR_chmod] = (shim_fp)shim_do_chmod, + [__NR_fchmod] = (shim_fp)shim_do_fchmod, + [__NR_chown] = (shim_fp)shim_do_chown, + [__NR_fchown] = (shim_fp)shim_do_fchown, + [__NR_lchown] = (shim_fp)0, // shim_do_lchown + [__NR_umask] = (shim_fp)shim_do_umask, + [__NR_gettimeofday] = (shim_fp)shim_do_gettimeofday, + [__NR_getrlimit] = (shim_fp)shim_do_getrlimit, + [__NR_getrusage] = (shim_fp)0, // shim_do_getrusage + [__NR_sysinfo] = (shim_fp)0, // shim_do_sysinfo + [__NR_times] = (shim_fp)0, // shim_do_times + [__NR_ptrace] = (shim_fp)0, // shim_do_ptrace + [__NR_getuid] = (shim_fp)shim_do_getuid, + [__NR_syslog] = (shim_fp)0, // shim_do_syslog + [__NR_getgid] = (shim_fp)shim_do_getgid, + [__NR_setuid] = (shim_fp)shim_do_setuid, + [__NR_setgid] = (shim_fp)shim_do_setgid, + [__NR_geteuid] = (shim_fp)shim_do_geteuid, + [__NR_getegid] = (shim_fp)shim_do_getegid, + [__NR_setpgid] = (shim_fp)shim_do_setpgid, + [__NR_getppid] = (shim_fp)shim_do_getppid, + [__NR_getpgrp] = (shim_fp)shim_do_getpgrp, + [__NR_setsid] = (shim_fp)shim_do_setsid, + [__NR_setreuid] = (shim_fp)0, // shim_do_setreuid + [__NR_setregid] = (shim_fp)0, // shim_do_setregid + [__NR_getgroups] = (shim_fp)shim_do_getgroups, + [__NR_setgroups] = (shim_fp)shim_do_setgroups, + [__NR_setresuid] = (shim_fp)0, // shim_do_setresuid + [__NR_getresuid] = (shim_fp)0, // shim_do_getresuid + [__NR_setresgid] = (shim_fp)0, // shim_do_setresgid + [__NR_getresgid] = (shim_fp)0, // shim_do_getresgid + [__NR_getpgid] = (shim_fp)shim_do_getpgid, + [__NR_setfsuid] = (shim_fp)0, // shim_do_setfsuid + [__NR_setfsgid] = (shim_fp)0, // shim_do_setfsgid + [__NR_getsid] = (shim_fp)shim_do_getsid, + [__NR_capget] = (shim_fp)0, // shim_do_capget + [__NR_capset] = (shim_fp)0, // shim_do_capset + [__NR_rt_sigpending] = (shim_fp)shim_do_rt_sigpending, + [__NR_rt_sigtimedwait] = (shim_fp)0, // shim_do_rt_sigtimedwait + [__NR_rt_sigqueueinfo] = (shim_fp)0, // shim_do_rt_sigqueueinfo + [__NR_rt_sigsuspend] = (shim_fp)shim_do_rt_sigsuspend, + [__NR_sigaltstack] = (shim_fp)shim_do_sigaltstack, + [__NR_utime] = (shim_fp)0, // shim_do_utime + [__NR_mknod] = (shim_fp)shim_do_mknod, + [__NR_uselib] = (shim_fp)0, // shim_do_uselib + [__NR_personality] = (shim_fp)0, // shim_do_personality + [__NR_ustat] = (shim_fp)0, // shim_do_ustat + [__NR_statfs] = (shim_fp)shim_do_statfs, + [__NR_fstatfs] = (shim_fp)shim_do_fstatfs, + [__NR_sysfs] = (shim_fp)0, // shim_do_sysfs + [__NR_getpriority] = (shim_fp)shim_do_getpriority, + [__NR_setpriority] = (shim_fp)shim_do_setpriority, + [__NR_sched_setparam] = (shim_fp)shim_do_sched_setparam, + [__NR_sched_getparam] = (shim_fp)shim_do_sched_getparam, + [__NR_sched_setscheduler] = (shim_fp)shim_do_sched_setscheduler, + [__NR_sched_getscheduler] = (shim_fp)shim_do_sched_getscheduler, + [__NR_sched_get_priority_max] = (shim_fp)shim_do_sched_get_priority_max, + [__NR_sched_get_priority_min] = (shim_fp)shim_do_sched_get_priority_min, + [__NR_sched_rr_get_interval] = (shim_fp)shim_do_sched_rr_get_interval, + [__NR_mlock] = (shim_fp)0, // shim_do_mlock + [__NR_munlock] = (shim_fp)0, // shim_do_munlock + [__NR_mlockall] = (shim_fp)0, // shim_do_mlockall + [__NR_munlockall] = (shim_fp)0, // shim_do_munlockall + [__NR_vhangup] = (shim_fp)0, // shim_do_vhangup + [__NR_modify_ldt] = (shim_fp)0, // shim_do_modify_ldt + [__NR_pivot_root] = (shim_fp)0, // shim_do_pivot_root + [__NR__sysctl] = (shim_fp)0, // shim_do__sysctl + [__NR_prctl] = (shim_fp)0, // shim_do_prctl + [__NR_arch_prctl] = (shim_fp)shim_do_arch_prctl, + [__NR_adjtimex] = (shim_fp)0, // shim_do_adjtimex + [__NR_setrlimit] = (shim_fp)shim_do_setrlimit, + [__NR_chroot] = (shim_fp)shim_do_chroot, + [__NR_sync] = (shim_fp)0, // shim_do_sync + [__NR_acct] = (shim_fp)0, // shim_do_acct + [__NR_settimeofday] = (shim_fp)0, // shim_do_settimeofday + [__NR_mount] = (shim_fp)0, // shim_do_mount + [__NR_umount2] = (shim_fp)0, // shim_do_umount2 + [__NR_swapon] = (shim_fp)0, // shim_do_swapon + [__NR_swapoff] = (shim_fp)0, // shim_do_swapoff + [__NR_reboot] = (shim_fp)0, // shim_do_reboot + [__NR_sethostname] = (shim_fp)shim_do_sethostname, + [__NR_setdomainname] = (shim_fp)shim_do_setdomainname, + [__NR_iopl] = (shim_fp)0, // shim_do_iopl + [__NR_ioperm] = (shim_fp)0, // shim_do_ioperm + [__NR_create_module] = (shim_fp)0, // shim_do_create_module + [__NR_init_module] = (shim_fp)0, // shim_do_init_module + [__NR_delete_module] = (shim_fp)0, // shim_do_delete_module + [__NR_get_kernel_syms] = (shim_fp)0, // shim_do_get_kernel_syms, + [__NR_query_module] = (shim_fp)0, // shim_do_query_module + [__NR_quotactl] = (shim_fp)0, // shim_do_quotactl + [__NR_nfsservctl] = (shim_fp)0, // shim_do_nfsservctl, + [__NR_getpmsg] = (shim_fp)0, // shim_do_getpmsg, + [__NR_putpmsg] = (shim_fp)0, // shim_do_putpmsg, + [__NR_afs_syscall] = (shim_fp)0, // shim_do_afs_syscall, + [__NR_tuxcall] = (shim_fp)0, // shim_do_tuxcall, + [__NR_security] = (shim_fp)0, // shim_do_security, + [__NR_gettid] = (shim_fp)shim_do_gettid, + [__NR_readahead] = (shim_fp)0, // shim_do_readahead + [__NR_setxattr] = (shim_fp)0, // shim_do_setxattr + [__NR_lsetxattr] = (shim_fp)0, // shim_do_lsetxattr + [__NR_fsetxattr] = (shim_fp)0, // shim_do_fsetxattr + [__NR_getxattr] = (shim_fp)0, // shim_do_getxattr + [__NR_lgetxattr] = (shim_fp)0, // shim_do_lgetxattr + [__NR_fgetxattr] = (shim_fp)0, // shim_do_fgetxattr + [__NR_listxattr] = (shim_fp)0, // shim_do_listxattr + [__NR_llistxattr] = (shim_fp)0, // shim_do_llistxattr + [__NR_flistxattr] = (shim_fp)0, // shim_do_flistxattr + [__NR_removexattr] = (shim_fp)0, // shim_do_removexattr + [__NR_lremovexattr] = (shim_fp)0, // shim_do_lremovexattr + [__NR_fremovexattr] = (shim_fp)0, // shim_do_fremovexattr + [__NR_tkill] = (shim_fp)shim_do_tkill, + [__NR_time] = (shim_fp)shim_do_time, + [__NR_futex] = (shim_fp)shim_do_futex, + [__NR_sched_setaffinity] = (shim_fp)shim_do_sched_setaffinity, + [__NR_sched_getaffinity] = (shim_fp)shim_do_sched_getaffinity, + [__NR_set_thread_area] = (shim_fp)0, // shim_do_set_thread_area + [__NR_io_setup] = (shim_fp)0, // shim_do_io_setup + [__NR_io_destroy] = (shim_fp)0, // shim_do_io_destroy + [__NR_io_getevents] = (shim_fp)0, // shim_do_io_getevents + [__NR_io_submit] = (shim_fp)0, // shim_do_io_submit + [__NR_io_cancel] = (shim_fp)0, // shim_do_io_cancel + [__NR_get_thread_area] = (shim_fp)0, // shim_do_get_thread_area + [__NR_lookup_dcookie] = (shim_fp)0, // shim_do_lookup_dcookie + [__NR_epoll_create] = (shim_fp)shim_do_epoll_create, + [__NR_epoll_ctl_old] = (shim_fp)0, // shim_do_epoll_ctl_old, + [__NR_epoll_wait_old] = (shim_fp)0, // shim_do_epoll_wait_old, + [__NR_remap_file_pages] = (shim_fp)0, // shim_do_remap_file_pages + [__NR_getdents64] = (shim_fp)shim_do_getdents64, + [__NR_set_tid_address] = (shim_fp)shim_do_set_tid_address, + [__NR_restart_syscall] = (shim_fp)0, // shim_do_restart_syscall + [__NR_semtimedop] = (shim_fp)shim_do_semtimedop, + [__NR_fadvise64] = (shim_fp)0, // shim_do_fadvise64 + [__NR_timer_create] = (shim_fp)0, // shim_do_timer_create + [__NR_timer_settime] = (shim_fp)0, // shim_do_timer_settime + [__NR_timer_gettime] = (shim_fp)0, // shim_do_timer_gettime + [__NR_timer_getoverrun] = (shim_fp)0, // shim_do_timer_getoverrun + [__NR_timer_delete] = (shim_fp)0, // shim_do_timer_delete + [__NR_clock_settime] = (shim_fp)0, // shim_do_clock_settime + [__NR_clock_gettime] = (shim_fp)shim_do_clock_gettime, + [__NR_clock_getres] = (shim_fp)shim_do_clock_getres, + [__NR_clock_nanosleep] = (shim_fp)shim_do_clock_nanosleep, + [__NR_exit_group] = (shim_fp)shim_do_exit_group, + [__NR_epoll_wait] = (shim_fp)shim_do_epoll_wait, + [__NR_epoll_ctl] = (shim_fp)shim_do_epoll_ctl, + [__NR_tgkill] = (shim_fp)shim_do_tgkill, + [__NR_utimes] = (shim_fp)0, // shim_do_utimes + [__NR_vserver] = (shim_fp)0, // shim_do_vserver, + [__NR_mbind] = (shim_fp)shim_do_mbind, + [__NR_set_mempolicy] = (shim_fp)0, // shim_do_set_mempolicy + [__NR_get_mempolicy] = (shim_fp)0, // shim_do_get_mempolicy + [__NR_mq_open] = (shim_fp)0, // shim_do_mq_open + [__NR_mq_unlink] = (shim_fp)0, // shim_do_mq_unlink + [__NR_mq_timedsend] = (shim_fp)0, // shim_do_mq_timedsend + [__NR_mq_timedreceive] = (shim_fp)0, // shim_do_mq_timedreceive + [__NR_mq_notify] = (shim_fp)0, // shim_do_mq_notify + [__NR_mq_getsetattr] = (shim_fp)0, // shim_do_mq_getsetattr + [__NR_kexec_load] = (shim_fp)0, // shim_do_kexec_load, + [__NR_waitid] = (shim_fp)shim_do_waitid, + [__NR_add_key] = (shim_fp)0, // shim_do_add_key, + [__NR_request_key] = (shim_fp)0, // shim_do_request_key, + [__NR_keyctl] = (shim_fp)0, // shim_do_keyctl, + [__NR_ioprio_set] = (shim_fp)0, // shim_do_ioprio_set + [__NR_ioprio_get] = (shim_fp)0, // shim_do_ioprio_get + [__NR_inotify_init] = (shim_fp)0, // shim_do_inotify_init + [__NR_inotify_add_watch] = (shim_fp)0, // shim_do_inotify_add_watch + [__NR_inotify_rm_watch] = (shim_fp)0, // shim_do_inotify_rm_watch + [__NR_migrate_pages] = (shim_fp)0, // shim_do_migrate_pages + [__NR_openat] = (shim_fp)shim_do_openat, + [__NR_mkdirat] = (shim_fp)shim_do_mkdirat, + [__NR_mknodat] = (shim_fp)shim_do_mknodat, + [__NR_fchownat] = (shim_fp)shim_do_fchownat, + [__NR_futimesat] = (shim_fp)0, // shim_do_futimesat + [__NR_newfstatat] = (shim_fp)shim_do_newfstatat, + [__NR_unlinkat] = (shim_fp)shim_do_unlinkat, + [__NR_renameat] = (shim_fp)shim_do_renameat, + [__NR_linkat] = (shim_fp)0, // shim_do_linkat + [__NR_symlinkat] = (shim_fp)0, // shim_do_symlinkat + [__NR_readlinkat] = (shim_fp)shim_do_readlinkat, + [__NR_fchmodat] = (shim_fp)shim_do_fchmodat, + [__NR_faccessat] = (shim_fp)shim_do_faccessat, + [__NR_pselect6] = (shim_fp)shim_do_pselect6, + [__NR_ppoll] = (shim_fp)shim_do_ppoll, + [__NR_unshare] = (shim_fp)0, // shim_do_unshare + [__NR_set_robust_list] = (shim_fp)shim_do_set_robust_list, + [__NR_get_robust_list] = (shim_fp)shim_do_get_robust_list, + [__NR_splice] = (shim_fp)0, // shim_do_splice + [__NR_tee] = (shim_fp)0, // shim_do_tee + [__NR_sync_file_range] = (shim_fp)0, // shim_do_sync_file_range + [__NR_vmsplice] = (shim_fp)0, // shim_do_vmsplice + [__NR_move_pages] = (shim_fp)0, // shim_do_move_pages + [__NR_utimensat] = (shim_fp)0, // shim_do_utimensat + [__NR_epoll_pwait] = (shim_fp)shim_do_epoll_pwait, + [__NR_signalfd] = (shim_fp)0, // shim_do_signalfd + [__NR_timerfd_create] = (shim_fp)0, // shim_do_timerfd_create + [__NR_eventfd] = (shim_fp)shim_do_eventfd, + [__NR_fallocate] = (shim_fp)0, // shim_do_fallocate + [__NR_timerfd_settime] = (shim_fp)0, // shim_do_timerfd_settime + [__NR_timerfd_gettime] = (shim_fp)0, // shim_do_timerfd_gettime + [__NR_accept4] = (shim_fp)shim_do_accept4, + [__NR_signalfd4] = (shim_fp)0, // shim_do_signalfd4 + [__NR_eventfd2] = (shim_fp)shim_do_eventfd2, + [__NR_epoll_create1] = (shim_fp)shim_do_epoll_create1, + [__NR_dup3] = (shim_fp)shim_do_dup3, + [__NR_pipe2] = (shim_fp)shim_do_pipe2, + [__NR_inotify_init1] = (shim_fp)0, // shim_do_inotify_init1 + [__NR_preadv] = (shim_fp)0, // shim_do_preadv + [__NR_pwritev] = (shim_fp)0, // shim_do_pwritev + [__NR_rt_tgsigqueueinfo] = (shim_fp)0, // shim_do_rt_tgsigqueueinfo + [__NR_perf_event_open] = (shim_fp)0, // shim_do_perf_event_open + [__NR_recvmmsg] = (shim_fp)shim_do_recvmmsg, + [__NR_fanotify_init] = (shim_fp)0, // shim_do_fanotify_init + [__NR_fanotify_mark] = (shim_fp)0, // shim_do_fanotify_mark + [__NR_prlimit64] = (shim_fp)shim_do_prlimit64, + [__NR_name_to_handle_at] = (shim_fp)0, // shim_do_name_to_handle_at + [__NR_open_by_handle_at] = (shim_fp)0, // shim_do_open_by_handle_at + [__NR_clock_adjtime] = (shim_fp)0, // shim_do_clock_adjtime + [__NR_syncfs] = (shim_fp)0, // shim_do_syncfs + [__NR_sendmmsg] = (shim_fp)shim_do_sendmmsg, + [__NR_setns] = (shim_fp)0, // shim_do_setns + [__NR_getcpu] = (shim_fp)shim_do_getcpu, + [__NR_process_vm_readv] = (shim_fp)0, // shim_do_process_vm_readv + [__NR_process_vm_writev] = (shim_fp)0, // shim_do_process_vm_writev + [__NR_kcmp] = (shim_fp)0, // shim_do_kcmp + [__NR_finit_module] = (shim_fp)0, // shim_do_finit_module + [__NR_sched_setattr] = (shim_fp)0, // shim_do_sched_setattr + [__NR_sched_getattr] = (shim_fp)0, // shim_do_sched_getattr + [__NR_renameat2] = (shim_fp)0, // shim_do_renameat2 + [__NR_seccomp] = (shim_fp)0, // shim_do_seccomp + [__NR_getrandom] = (shim_fp)shim_do_getrandom, + [__NR_memfd_create] = (shim_fp)0, // shim_do_memfd_create + [__NR_kexec_file_load] = (shim_fp)0, // shim_do_kexec_file_load + [__NR_bpf] = (shim_fp)0, // shim_do_bpf + [__NR_execveat] = (shim_fp)0, // shim_do_execveat + [__NR_userfaultfd] = (shim_fp)0, // shim_do_userfaultfd + [__NR_membarrier] = (shim_fp)0, // shim_do_membarrier + [__NR_mlock2] = (shim_fp)0, // shim_do_mlock2 + [__NR_copy_file_range] = (shim_fp)0, // shim_do_copy_file_range + [__NR_preadv2] = (shim_fp)0, // shim_do_preadv2 + [__NR_pwritev2] = (shim_fp)0, // shim_do_pwritev2 + [__NR_pkey_mprotect] = (shim_fp)0, // shim_do_pkey_mprotect + [__NR_pkey_alloc] = (shim_fp)0, // shim_do_pkey_alloc + [__NR_pkey_free] = (shim_fp)0, // shim_do_pkey_free + [__NR_statx] = (shim_fp)0, // shim_do_statx + [__NR_io_pgetevents] = (shim_fp)0, // shim_do_io_pgetevents + [__NR_rseq] = (shim_fp)0, // shim_do_rseq + [__NR_pidfd_send_signal] = (shim_fp)0, // shim_do_pidfd_send_signal + [__NR_io_uring_setup] = (shim_fp)0, // shim_do_io_uring_setup + [__NR_io_uring_enter] = (shim_fp)0, // shim_do_io_uring_enter + [__NR_io_uring_register] = (shim_fp)0, // shim_do_io_uring_register }; diff --git a/LibOS/shim/src/sys/shim_clone.c b/LibOS/shim/src/sys/shim_clone.c index ccf2dc49..f4148c8d 100644 --- a/LibOS/shim/src/sys/shim_clone.c +++ b/LibOS/shim/src/sys/shim_clone.c @@ -28,9 +28,8 @@ struct shim_clone_args { PAL_HANDLE initialize_event; struct shim_thread* thread; void* stack; - unsigned long tls_base; - struct shim_regs regs; - struct shim_ext_context ext_ctx; + unsigned long tls; + PAL_CONTEXT* regs; }; /* @@ -60,7 +59,6 @@ static int clone_implementation_wrapper(struct shim_clone_args* arg) { shim_tcb_init(); set_cur_thread(my_thread); - update_tls_base(arg->tls_base); /* only now we can call LibOS/PAL functions because they require a set-up TCB; * do not move the below functions before shim_tcb_init/set_cur_thread()! */ @@ -68,14 +66,10 @@ static int clone_implementation_wrapper(struct shim_clone_args* arg) { DkObjectClose(arg->create_event); shim_tcb_t* tcb = my_thread->shim_tcb; - __disable_preempt(tcb); // Temporarily disable preemption, because the preemption - // will be re-enabled when the thread starts. struct debug_buf debug_buf; (void)debug_setbuf(tcb, &debug_buf); - debug("set tls_base to 0x%lx\n", tcb->context.tls_base); - if (my_thread->set_child_tid) { *(my_thread->set_child_tid) = my_thread->tid; my_thread->set_child_tid = NULL; @@ -95,22 +89,16 @@ static int clone_implementation_wrapper(struct shim_clone_args* arg) { add_thread(my_thread); - /* New thread inherits FP (fpcw) and SSE/AVX/... (mxcsr) control words of parent thread. */ - tcb->context.ext_ctx = arg->ext_ctx; - /* Copy regs before we let the parent release them. */ - struct shim_regs regs = arg->regs; + PAL_CONTEXT regs; + pal_context_copy(®s, arg->regs); + pal_context_set_sp(®s, (unsigned long)stack); + tcb->context.regs = ®s; + tcb->context.tls = arg->tls; /* Inform the parent thread that we finished initialization. */ DkEventSet(arg->initialize_event); - debug("child swapping stack to %p return 0x%lx: %d\n", stack, shim_regs_get_ip(®s), - my_thread->tid); - - tcb->context.regs = ®s; - fixup_child_context(tcb->context.regs); - shim_context_set_sp(&tcb->context, (unsigned long)stack); - put_thread(my_thread); restore_child_context_after_clone(&tcb->context); @@ -141,7 +129,7 @@ static int migrate_fork(struct shim_cp_store* store, struct shim_process* proces return START_MIGRATE(store, fork, process_description, thread_description, process_ipc_info); } -static long do_clone_new_vm(unsigned long flags, struct shim_thread* thread, unsigned long tls_base, +static long do_clone_new_vm(unsigned long flags, struct shim_thread* thread, unsigned long tls, unsigned long user_stack_addr, int* set_parent_tid) { assert(!(flags & CLONE_VM)); @@ -156,19 +144,10 @@ static long do_clone_new_vm(unsigned long flags, struct shim_thread* thread, uns * since we might need to modify some registers. */ shim_tcb_t shim_tcb = { 0 }; __shim_tcb_init(&shim_tcb); - /* Preemption is disabled and we are copying our own tcb, which should be ok to do, - * even without any locks. Note this is a shallow copy, so `shim_tcb.context.regs` will be - * shared with the parent. */ + /* We are copying our own tcb, which should be ok to do, even without any locks. Note this is + * a shallow copy, so `shim_tcb.context.regs` will be shared with the parent. */ shim_tcb.context.regs = self->shim_tcb->context.regs; - - /* new process inherits FP (fpcw) and SSE/AVX/... (mxcsr) control words */ - shim_tcb.context.ext_ctx = self->shim_tcb->context.ext_ctx; - - if (flags & CLONE_SETTLS) { - shim_tcb.context.tls_base = tls_base; - } else { - shim_tcb.context.tls_base = self->shim_tcb->context.tls_base; - } + shim_tcb.context.tls = tls; thread->shim_tcb = &shim_tcb; @@ -181,8 +160,8 @@ static long do_clone_new_vm(unsigned long flags, struct shim_thread* thread, uns } thread->stack_top = (char*)vma_info.addr + vma_info.length; thread->stack_red = thread->stack = vma_info.addr; - parent_stack = shim_context_get_sp(&self->shim_tcb->context); - shim_context_set_sp(&thread->shim_tcb->context, user_stack_addr); + parent_stack = pal_context_get_sp(self->shim_tcb->context.regs); + pal_context_set_sp(thread->shim_tcb->context.regs, user_stack_addr); if (vma_info.file) { put_handle(vma_info.file); @@ -221,7 +200,7 @@ static long do_clone_new_vm(unsigned long flags, struct shim_thread* thread, uns &process_description, thread); if (parent_stack) { - shim_context_set_sp(&self->shim_tcb->context, parent_stack); + pal_context_set_sp(self->shim_tcb->context.regs, parent_stack); } thread->shim_tcb = NULL; @@ -324,8 +303,6 @@ long shim_do_clone(unsigned long flags, unsigned long user_stack_addr, int* pare set_parent_tid = parent_tidptr; } - disable_preempt(NULL); - long ret = 0; struct shim_thread* thread = get_new_thread(); @@ -345,9 +322,8 @@ long shim_do_clone(unsigned long flags, unsigned long user_stack_addr, int* pare if (flags & CLONE_CHILD_CLEARTID) thread->clear_child_tid = child_tidptr; - unsigned long tls_base = 0; - if (flags & CLONE_SETTLS) { - tls_base = tls_to_tls_base(tls); + if (!(flags & CLONE_SETTLS)) { + tls = get_tls(); } if (!(flags & CLONE_VM)) { @@ -355,7 +331,7 @@ long shim_do_clone(unsigned long flags, unsigned long user_stack_addr, int* pare * another process. */ assert(!(flags & CLONE_THREAD)); - ret = do_clone_new_vm(flags, thread, tls_base, user_stack_addr, set_parent_tid); + ret = do_clone_new_vm(flags, thread, tls, user_stack_addr, set_parent_tid); /* We should not have saved any references to this thread anywhere and `put_thread` below * should free it. */ @@ -366,12 +342,16 @@ long shim_do_clone(unsigned long flags, unsigned long user_stack_addr, int* pare thread->tid = 0; put_thread(thread); - enable_preempt(NULL); return ret; } assert(flags & CLONE_THREAD); + ret = alloc_thread_libos_stack(thread); + if (ret < 0) { + goto failed; + } + /* Threads do not generate signals on death, ignore it. */ flags &= ~CSIGNAL; @@ -411,11 +391,10 @@ long shim_do_clone(unsigned long flags, unsigned long user_stack_addr, int* pare /* Increasing refcount due to copy below. Passing ownership of the new copy * of this pointer to the new thread (receiver of new_args). */ get_thread(thread); - new_args.thread = thread; - new_args.stack = (void*)(user_stack_addr ?: shim_context_get_sp(&self->shim_tcb->context)); - new_args.tls_base = tls_base; - new_args.regs = *self->shim_tcb->context.regs; - new_args.ext_ctx = self->shim_tcb->context.ext_ctx; + new_args.thread = thread; + new_args.stack = (void*)(user_stack_addr ?: pal_context_get_sp(self->shim_tcb->context.regs)); + new_args.tls = tls; + new_args.regs = self->shim_tcb->context.regs; // Invoke DkThreadCreate to spawn off a child process using the actual // "clone" system call. DkThreadCreate allocates a stack for the child @@ -442,7 +421,6 @@ long shim_do_clone(unsigned long flags, unsigned long user_stack_addr, int* pare IDTYPE tid = thread->tid; put_thread(thread); - enable_preempt(NULL); return tid; clone_thread_failed: @@ -453,6 +431,5 @@ clone_thread_failed: failed: if (thread) put_thread(thread); - enable_preempt(NULL); return ret; } diff --git a/LibOS/shim/src/sys/shim_exec.c b/LibOS/shim/src/sys/shim_exec.c index f09c968d..897e4d51 100644 --- a/LibOS/shim/src/sys/shim_exec.c +++ b/LibOS/shim/src/sys/shim_exec.c @@ -43,14 +43,11 @@ struct execve_rtld_arg { noreturn static void __shim_do_execve_rtld(struct execve_rtld_arg* __arg) { struct execve_rtld_arg arg = *__arg; - struct shim_thread* cur_thread = get_cur_thread(); int ret = 0; - unsigned long tls_base = 0; - update_tls_base(tls_base); - debug("set tls_base to 0x%lx\n", tls_base); + set_default_tls(); - thread_sigaction_reset_on_execve(cur_thread); + thread_sigaction_reset_on_execve(); remove_loaded_libraries(); clean_link_map_list(); @@ -64,6 +61,7 @@ noreturn static void __shim_do_execve_rtld(struct execve_rtld_arg* __arg) { goto error; } + struct shim_thread* cur_thread = get_cur_thread(); for (struct shim_vma_info* vma = vmas; vma < vmas + count; vma++) { /* Don't free the current stack */ if (vma->addr == cur_thread->stack || vma->addr == cur_thread->stack_red) @@ -317,11 +315,6 @@ reopen: * instance and call execve again. */ __atomic_store_n(&first, 0, __ATOMIC_RELAXED); - /* Disable preemption during `execve`. It will be enabled back in `execute_elf_object` if we - * stay in the same process. Otherwise it is never enabled, since this process dies both on - * errors and success. */ - disable_preempt(NULL); - /* Passing ownership of `exec`. */ ret = shim_do_execve_rtld(exec, argv, envp); assert(ret < 0); diff --git a/LibOS/shim/src/sys/shim_exit.c b/LibOS/shim/src/sys/shim_exit.c index a2d59ded..4bd36a10 100644 --- a/LibOS/shim/src/sys/shim_exit.c +++ b/LibOS/shim/src/sys/shim_exit.c @@ -48,9 +48,6 @@ static noreturn void libos_clean_and_exit(int exit_code) { } noreturn void thread_exit(int error_code, int term_signal) { - /* Disable preemption as soon we won't be able to process signals. */ - disable_preempt(NULL); - /* Remove current thread from the threads list. */ if (!check_last_thread(/*mark_self_dead=*/true)) { struct shim_thread* cur_thread = get_cur_thread(); @@ -98,16 +95,9 @@ static int mark_thread_to_die(struct shim_thread* thread, void* arg) { return 0; } - bool need_wakeup = false; + bool need_wakeup = !__atomic_exchange_n(&thread->time_to_die, true, __ATOMIC_ACQ_REL); - lock(&thread->lock); - if (!thread->time_to_die) { - need_wakeup = true; - } - thread->time_to_die = true; - unlock(&thread->lock); - - /* Now let's kick `thread`, so that it notices (in `__handle_signals`) the flag `time_to_die` + /* Now let's kick `thread`, so that it notices (in `handle_signal`) the flag `time_to_die` * set above (but only if we really set that flag). */ if (need_wakeup) { thread_wakeup(thread); @@ -155,7 +145,7 @@ noreturn void process_exit(int error_code, int term_signal) { thread_exit(error_code, term_signal); } -noreturn long shim_do_exit_group(int error_code) { +long shim_do_exit_group(int error_code) { assert(!is_internal(get_cur_thread())); error_code &= 0xFF; @@ -165,7 +155,7 @@ noreturn long shim_do_exit_group(int error_code) { process_exit(error_code, 0); } -noreturn long shim_do_exit(int error_code) { +long shim_do_exit(int error_code) { assert(!is_internal(get_cur_thread())); error_code &= 0xFF; diff --git a/LibOS/shim/src/sys/shim_futex.c b/LibOS/shim/src/sys/shim_futex.c index 0a6adaa6..765b68d8 100644 --- a/LibOS/shim/src/sys/shim_futex.c +++ b/LibOS/shim/src/sys/shim_futex.c @@ -26,6 +26,7 @@ #include "list.h" #include "pal.h" #include "shim_internal.h" +#include "shim_signal.h" #include "shim_table.h" #include "shim_thread.h" #include "shim_types.h" @@ -101,10 +102,10 @@ static void lock_two_futexes(struct shim_futex* futex1, struct shim_futex* futex if (!futex1 && !futex2) { return; } else if (futex1 && !futex2) { - spinlock_lock_signal_off(&futex1->lock); + spinlock_lock(&futex1->lock); return; } else if (!futex1 && futex2) { - spinlock_lock_signal_off(&futex2->lock); + spinlock_lock(&futex2->lock); return; } /* Both are not NULL. */ @@ -113,13 +114,13 @@ static void lock_two_futexes(struct shim_futex* futex1, struct shim_futex* futex * If both futexes are equal, just take one lock. */ int cmp = cmp_futexes(futex1, futex2); if (cmp < 0) { - spinlock_lock_signal_off(&futex1->lock); - spinlock_lock_signal_off(&futex2->lock); + spinlock_lock(&futex1->lock); + spinlock_lock(&futex2->lock); } else if (cmp == 0) { - spinlock_lock_signal_off(&futex1->lock); + spinlock_lock(&futex1->lock); } else { - spinlock_lock_signal_off(&futex2->lock); - spinlock_lock_signal_off(&futex1->lock); + spinlock_lock(&futex2->lock); + spinlock_lock(&futex1->lock); } } @@ -127,10 +128,10 @@ static void unlock_two_futexes(struct shim_futex* futex1, struct shim_futex* fut if (!futex1 && !futex2) { return; } else if (futex1 && !futex2) { - spinlock_unlock_signal_on(&futex1->lock); + spinlock_unlock(&futex1->lock); return; } else if (!futex1 && futex2) { - spinlock_unlock_signal_on(&futex2->lock); + spinlock_unlock(&futex2->lock); return; } /* Both are not NULL. */ @@ -138,10 +139,10 @@ static void unlock_two_futexes(struct shim_futex* futex1, struct shim_futex* fut /* For unlocking order does not matter. */ int cmp = cmp_futexes(futex1, futex2); if (cmp) { - spinlock_unlock_signal_on(&futex1->lock); - spinlock_unlock_signal_on(&futex2->lock); + spinlock_unlock(&futex1->lock); + spinlock_unlock(&futex2->lock); } else { - spinlock_unlock_signal_on(&futex1->lock); + spinlock_unlock(&futex1->lock); } } @@ -189,18 +190,18 @@ static void _maybe_dequeue_futex(struct shim_futex* futex) { * it acquires these locks itself. */ static void maybe_dequeue_futex(struct shim_futex* futex) { - spinlock_lock_signal_off(&g_futex_tree_lock); - spinlock_lock_signal_off(&futex->lock); + spinlock_lock(&g_futex_tree_lock); + spinlock_lock(&futex->lock); _maybe_dequeue_futex(futex); - spinlock_unlock_signal_on(&futex->lock); - spinlock_unlock_signal_on(&g_futex_tree_lock); + spinlock_unlock(&futex->lock); + spinlock_unlock(&g_futex_tree_lock); } /* * Same as `maybe_dequeue_futex`, but works for two futexes, any of which might be NULL. */ static void maybe_dequeue_two_futexes(struct shim_futex* futex1, struct shim_futex* futex2) { - spinlock_lock_signal_off(&g_futex_tree_lock); + spinlock_lock(&g_futex_tree_lock); lock_two_futexes(futex1, futex2); if (futex1) { _maybe_dequeue_futex(futex1); @@ -209,7 +210,7 @@ static void maybe_dequeue_two_futexes(struct shim_futex* futex1, struct shim_fut _maybe_dequeue_futex(futex2); } unlock_two_futexes(futex1, futex2); - spinlock_unlock_signal_on(&g_futex_tree_lock); + spinlock_unlock(&g_futex_tree_lock); } /* @@ -318,15 +319,15 @@ static int futex_wait(uint32_t* uaddr, uint32_t val, uint64_t timeout, uint32_t struct shim_thread* thread = NULL; struct shim_futex* tmp = NULL; - spinlock_lock_signal_off(&g_futex_tree_lock); + spinlock_lock(&g_futex_tree_lock); futex = find_futex(uaddr); if (!futex) { - spinlock_unlock_signal_on(&g_futex_tree_lock); + spinlock_unlock(&g_futex_tree_lock); tmp = create_new_futex(uaddr); if (!tmp) { return -ENOMEM; } - spinlock_lock_signal_off(&g_futex_tree_lock); + spinlock_lock(&g_futex_tree_lock); futex = find_futex(uaddr); if (!futex) { enqueue_futex(tmp); @@ -334,8 +335,8 @@ static int futex_wait(uint32_t* uaddr, uint32_t val, uint64_t timeout, uint32_t tmp = NULL; } } - spinlock_lock_signal_off(&futex->lock); - spinlock_unlock_signal_on(&g_futex_tree_lock); + spinlock_lock(&futex->lock); + spinlock_unlock(&g_futex_tree_lock); if (__atomic_load_n(uaddr, __ATOMIC_RELAXED) != val) { ret = -EAGAIN; @@ -345,31 +346,38 @@ static int futex_wait(uint32_t* uaddr, uint32_t val, uint64_t timeout, uint32_t struct futex_waiter waiter = {0}; add_futex_waiter(&waiter, futex, bitset); - spinlock_unlock_signal_on(&futex->lock); + spinlock_unlock(&futex->lock); /* Give up this futex reference - we have no idea what futex we will be on once we wake up * (due to possible requeues). */ put_futex(futex); futex = NULL; - ret = thread_sleep(timeout); + ret = thread_sleep(timeout, /*ignore_pending_signals=*/false); /* On timeout thread_sleep returns -EAGAIN. */ if (ret == -EAGAIN) { ret = -ETIMEDOUT; } - spinlock_lock_signal_off(&g_futex_tree_lock); + spinlock_lock(&g_futex_tree_lock); /* We might have been requeued. Grab the (possibly new) futex reference. */ futex = waiter.futex; assert(futex); get_futex(futex); - spinlock_lock_signal_off(&futex->lock); - spinlock_unlock_signal_on(&g_futex_tree_lock); + spinlock_lock(&futex->lock); + spinlock_unlock(&g_futex_tree_lock); if (!LIST_EMPTY(&waiter, list)) { - /* If we woke up due to time out, we were not removed from the waiters list (opposite - * of when another thread calls FUTEX_WAKE, which would remove us from the list). */ + /* If we woke up due to time out or a signal, we were not removed from the waiters list + * (opposite of when another thread calls FUTEX_WAKE, which would remove us from the list). + */ thread = remove_futex_waiter(&waiter, futex); + + if (ret == 0 || ret == -EINTR) { + ret = -ERESTARTSYS; + } + } else if (ret == -EINTR) { + ret = 0; } /* At this point we are done using the `waiter` struct and need to give up the futex reference @@ -382,7 +390,7 @@ out_with_futex_lock:; // C is awesome! * we check if we actually need to do it now (locks acquisition and dequeuing). */ bool needs_dequeue = check_dequeue_futex(futex); - spinlock_unlock_signal_on(&futex->lock); + spinlock_unlock(&futex->lock); if (needs_dequeue) { maybe_dequeue_futex(futex); @@ -445,20 +453,20 @@ static int futex_wake(uint32_t* uaddr, int to_wake, uint32_t bitset) { return -EINVAL; } - spinlock_lock_signal_off(&g_futex_tree_lock); + spinlock_lock(&g_futex_tree_lock); futex = find_futex(uaddr); if (!futex) { - spinlock_unlock_signal_on(&g_futex_tree_lock); + spinlock_unlock(&g_futex_tree_lock); return 0; } - spinlock_lock_signal_off(&futex->lock); - spinlock_unlock_signal_on(&g_futex_tree_lock); + spinlock_lock(&futex->lock); + spinlock_unlock(&g_futex_tree_lock); woken = move_to_wake_queue(futex, bitset, to_wake, &queue); bool needs_dequeue = check_dequeue_futex(futex); - spinlock_unlock_signal_on(&futex->lock); + spinlock_unlock(&futex->lock); if (needs_dequeue) { maybe_dequeue_futex(futex); @@ -490,12 +498,12 @@ static int futex_wake_op(uint32_t* uaddr1, uint32_t* uaddr2, int to_wake1, int t bool needs_dequeue1 = false; bool needs_dequeue2 = false; - spinlock_lock_signal_off(&g_futex_tree_lock); + spinlock_lock(&g_futex_tree_lock); futex1 = find_futex(uaddr1); futex2 = find_futex(uaddr2); lock_two_futexes(futex1, futex2); - spinlock_unlock_signal_on(&g_futex_tree_lock); + spinlock_unlock(&g_futex_tree_lock); unsigned int op = (val3 >> 28) & 0x7; // highest bit is for FUTEX_OP_OPARG_SHIFT unsigned int cmp = (val3 >> 24) & 0xf; @@ -611,17 +619,17 @@ static int futex_requeue(uint32_t* uaddr1, uint32_t* uaddr2, int to_wake, int to return -EINVAL; } - spinlock_lock_signal_off(&g_futex_tree_lock); + spinlock_lock(&g_futex_tree_lock); futex2 = find_futex(uaddr2); if (!futex2) { - spinlock_unlock_signal_on(&g_futex_tree_lock); + spinlock_unlock(&g_futex_tree_lock); tmp = create_new_futex(uaddr2); if (!tmp) { return -ENOMEM; } needs_dequeue2 = true; - spinlock_lock_signal_off(&g_futex_tree_lock); + spinlock_lock(&g_futex_tree_lock); futex2 = find_futex(uaddr2); if (!futex2) { enqueue_futex(tmp); @@ -632,7 +640,7 @@ static int futex_requeue(uint32_t* uaddr1, uint32_t* uaddr2, int to_wake, int to futex1 = find_futex(uaddr1); lock_two_futexes(futex1, futex2); - spinlock_unlock_signal_on(&g_futex_tree_lock); + spinlock_unlock(&g_futex_tree_lock); if (val != NULL) { if (__atomic_load_n(uaddr1, __ATOMIC_RELAXED) != *val) { diff --git a/LibOS/shim/src/sys/shim_sigaction.c b/LibOS/shim/src/sys/shim_sigaction.c index 8ac73350..1a01b42f 100644 --- a/LibOS/shim/src/sys/shim_sigaction.c +++ b/LibOS/shim/src/sys/shim_sigaction.c @@ -9,6 +9,7 @@ * and "tgkill". */ +#include #include #include // FIXME(mkow): Without this we get: // asm/signal.h:126:2: error: unknown type name ‘size_t’ @@ -26,19 +27,26 @@ #include "shim_thread.h" #include "shim_utils.h" -long shim_do_sigaction(int signum, const struct __kernel_sigaction* act, - struct __kernel_sigaction* oldact, size_t sigsetsize) { +long shim_do_rt_sigaction(int signum, const struct __kernel_sigaction* act, + struct __kernel_sigaction* oldact, size_t sigsetsize) { /* SIGKILL and SIGSTOP cannot be caught or ignored */ if (signum == SIGKILL || signum == SIGSTOP || signum <= 0 || signum > NUM_SIGS || sigsetsize != sizeof(__sigset_t)) return -EINVAL; - if (act && test_user_memory((void*)act, sizeof(*act), false)) + if (act && test_user_memory((void*)act, sizeof(*act), /*write=*/false)) return -EFAULT; - if (oldact && test_user_memory(oldact, sizeof(*oldact), false)) + if (oldact && test_user_memory(oldact, sizeof(*oldact), /*write=*/true)) return -EFAULT; + if (act && !(act->sa_flags & SA_RESTORER)) { + /* XXX: This might not be true for all architectures (but is for x86_64)... + * Check `shim_signal.c` if you update this! */ + debug("SA_RESTORER flag is required!\n"); + return -EINVAL; + } + struct shim_thread* cur = get_cur_thread(); lock(&cur->signal_dispositions->lock); @@ -57,13 +65,25 @@ long shim_do_sigaction(int signum, const struct __kernel_sigaction* act, return 0; } -long shim_do_sigreturn(int __unused) { - __UNUSED(__unused); - /* do nothing */ - return 0; +long shim_do_rt_sigreturn(void) { + PAL_CONTEXT* context = SHIM_TCB_GET(context.regs); + + __sigset_t new_mask; + restore_sigreturn_context(context, &new_mask); + clear_illegal_signals(&new_mask); + + struct shim_thread* current = get_cur_thread(); + lock(¤t->lock); + set_sig_mask(current, &new_mask); + unlock(¤t->lock); + + /* We restored user context, it's not a syscall. */ + SHIM_TCB_SET(context.syscall_nr, -1); + + return pal_context_get_retval(context); } -long shim_do_sigprocmask(int how, const __sigset_t* set, __sigset_t* oldset) { +long shim_do_rt_sigprocmask(int how, const __sigset_t* set, __sigset_t* oldset) { __sigset_t old; if (how != SIG_BLOCK && how != SIG_UNBLOCK && how != SIG_SETMASK) @@ -113,25 +133,33 @@ out: } long shim_do_sigaltstack(const stack_t* ss, stack_t* oss) { + if (ss && test_user_memory((void*)ss, sizeof(*ss), /*write=*/false)) { + return -EFAULT; + } + if (oss && test_user_memory(oss, sizeof(*oss), /*write=*/true)) { + return -EFAULT; + } + if (ss && (ss->ss_flags & ~SS_DISABLE)) return -EINVAL; struct shim_thread* cur = get_cur_thread(); - lock(&cur->lock); stack_t* cur_ss = &cur->signal_altstack; - if (oss) + if (oss) { *oss = *cur_ss; + if (cur_ss->ss_size == 0) { + oss->ss_flags |= SS_DISABLE; + } + } - void* sp = (void*)shim_context_get_sp(&(shim_get_tcb()->context)); - /* check if thread is currently executing on an active altstack */ - if (!(cur_ss->ss_flags & SS_DISABLE) && sp && cur_ss->ss_sp <= sp && - sp < cur_ss->ss_sp + cur_ss->ss_size) { + if (!(cur_ss->ss_flags & SS_DISABLE) + && is_on_altstack(pal_context_get_sp(shim_get_tcb()->context.regs), cur_ss)) { + /* We are currently using the alternative stack. */ if (oss) oss->ss_flags |= SS_ONSTACK; if (ss) { - unlock(&cur->lock); return -EPERM; } } @@ -142,7 +170,6 @@ long shim_do_sigaltstack(const stack_t* ss, stack_t* oss) { cur_ss->ss_flags = SS_DISABLE; } else { if (ss->ss_size < MINSIGSTKSZ) { - unlock(&cur->lock); return -ENOMEM; } @@ -150,77 +177,92 @@ long shim_do_sigaltstack(const stack_t* ss, stack_t* oss) { } } - unlock(&cur->lock); return 0; } -long shim_do_sigsuspend(const __sigset_t* mask_ptr) { +long shim_do_rt_sigsuspend(const __sigset_t* mask_ptr, size_t setsize) { + if (setsize != sizeof(sigset_t)) { + return -EINVAL; + } if (!mask_ptr || test_user_memory((void*)mask_ptr, sizeof(*mask_ptr), false)) return -EFAULT; __sigset_t mask = *mask_ptr; clear_illegal_signals(&mask); - struct shim_thread* cur = get_cur_thread(); - - __atomic_store_n(&cur->signal_handled, SIGNAL_NOT_HANDLED, __ATOMIC_RELEASE); - - lock(&cur->lock); + struct shim_thread* current = get_cur_thread(); __sigset_t old; - get_sig_mask(cur, &old); + lock(¤t->lock); + get_sig_mask(current, &old); + set_sig_mask(current, &mask); + unlock(¤t->lock); - set_sig_mask(cur, &mask); - unlock(&cur->lock); - - /* We might have unblocked some pending signals. */ - handle_signals(); - - thread_setwait(NULL, NULL); - - /* `SA_RESTART` does not affect `sigsuspend`. */ - while (__atomic_load_n(&cur->signal_handled, __ATOMIC_ACQUIRE) == SIGNAL_NOT_HANDLED) { - thread_sleep(NO_TIMEOUT); - handle_signals(); + DkEventClear(current->scheduler_event); + while (!have_pending_signals()) { + int ret = thread_sleep(NO_TIMEOUT, /*ignore_pending_signals=*/false); + if (ret < 0 && ret != -EINTR && ret != -EAGAIN) { + return ret; + } } - lock(&cur->lock); - set_sig_mask(cur, &old); - unlock(&cur->lock); - return -EINTR; + /* XXX: This basicaly doubles the work of `shim_emulate_syscall`. The alternative would be to + * add handling of saved signal mask (probably inside `current`) to `shim_emulate_syscall`, but + * as it is specific to sigsuspend I'm leaving this here for now. */ + int ret = -EINTR; + PAL_CONTEXT* context = SHIM_TCB_GET(context.regs); + pal_context_set_retval(context, ret); + + debug_print_syscall_after(__NR_rt_sigsuspend, ret, ALL_SYSCALL_ARGS(context)); + + if (!handle_signal(context, &old)) { + restart_syscall(context, __NR_rt_sigsuspend); + } + + SHIM_TCB_SET(context.syscall_nr, -1); + SHIM_TCB_SET(context.regs, NULL); + return_from_syscall(context); } -long shim_do_sigpending(__sigset_t* set, size_t sigsetsize) { +long shim_do_rt_sigpending(__sigset_t* set, size_t sigsetsize) { if (sigsetsize != sizeof(*set)) return -EINVAL; - if (!set || test_user_memory(set, sigsetsize, false)) + if (!set || test_user_memory(set, sigsetsize, /*write=*/true)) return -EFAULT; - struct shim_thread* cur = get_cur_thread(); + get_all_pending_signals(set); - get_pending_signals(cur, set); + struct shim_thread* current = get_cur_thread(); + /* We are interested only in blocked signals... */ + lock(¤t->lock); + __sigandset(set, set, ¤t->signal_mask); + unlock(¤t->lock); + + /* ...and not ignored. */ + lock(¤t->signal_dispositions->lock); + for (int sig = 1; sig <= NUM_SIGS; sig++) { + if (current->signal_dispositions->actions[sig - 1].k_sa_handler == SIG_IGN) { + __sigdelset(set, sig); + } + } + unlock(¤t->signal_dispositions->lock); return 0; } -struct signal_thread_arg { - int sig; - bool current_should_handle; -}; - -static int _wakeup_one_thread(struct shim_thread* thread, void* _arg) { - struct signal_thread_arg* arg = (struct signal_thread_arg*)_arg; +static int _wakeup_one_thread(struct shim_thread* thread, void* arg) { + int sig = (int)(long)arg; int ret = 0; + if (thread == get_cur_thread()) { + return ret; + } + lock(&thread->lock); - if (!__sigismember(&thread->signal_mask, arg->sig)) { - if (thread == get_cur_thread()) { - arg->current_should_handle = true; - } else { - thread_wakeup(thread); - DkThreadResume(thread->pal_handle); - } + if (!__sigismember(&thread->signal_mask, sig)) { + thread_wakeup(thread); + DkThreadResume(thread->pal_handle); ret = 1; } @@ -238,23 +280,25 @@ int kill_current_proc(siginfo_t* info) { return ret; } - struct signal_thread_arg arg = { - .sig = info->si_signo, - .current_should_handle = false, - }; + int sig = info->si_signo; + struct shim_thread* current = get_cur_thread(); + if (!is_internal(current)) { + /* Can we handle this signal? */ + lock(¤t->lock); + if (!__sigismember(¤t->signal_mask, sig)) { + /* Yes we can. */ + unlock(¤t->lock); + return 0; + } + unlock(¤t->lock); + } - ret = walk_thread_list(_wakeup_one_thread, &arg, /*one_shot=*/true); + ret = walk_thread_list(_wakeup_one_thread, (void*)(long)sig, /*one_shot=*/true); /* Ignore `-ESRCH` as this just means that currently no thread is able to handle the signal. */ if (ret < 0 && ret != -ESRCH) { return ret; } - if (arg.current_should_handle) { - assert(ret == 0); - /* We've delivered the signal to the current thread, now need to handle it. */ - handle_signals(); - } - return 0; } @@ -346,14 +390,12 @@ int do_kill_thread(IDTYPE sender, IDTYPE tgid, IDTYPE tid, int sig, bool use_ipc .si_pid = sender, .si_code = SI_TKILL, }; - if (thread == get_cur_thread()) { - deliver_signal(&info, NULL); - } else { - int ret = append_signal(thread, &info); - if (ret < 0) { - put_thread(thread); - return ret; - } + int ret = append_signal(thread, &info); + if (ret < 0) { + put_thread(thread); + return ret; + } + if (thread != get_cur_thread()) { thread_wakeup(thread); DkThreadResume(thread->pal_handle); } diff --git a/LibOS/shim/src/sys/shim_wait.c b/LibOS/shim/src/sys/shim_wait.c index 1975c162..83a92174 100644 --- a/LibOS/shim/src/sys/shim_wait.c +++ b/LibOS/shim/src/sys/shim_wait.c @@ -89,7 +89,7 @@ static void remove_qnode_from_wait_queue(struct shim_thread_queue* qnode) { break; } /* We cannot handle any errors here. */ - (void)thread_sleep(NO_TIMEOUT); + (void)thread_sleep(NO_TIMEOUT, /*ignore_pending_signals=*/true); } } @@ -118,9 +118,9 @@ static long do_waitid(int which, pid_t id, siginfo_t* infop, int options) { long ret = 0; - lock(&g_process.children_lock); + do { + lock(&g_process.children_lock); - while (1) { struct shim_child_process* child; /* First search already exited children. */ LISTP_FOR_EACH_ENTRY(child, &g_process.zombies, list) { @@ -176,37 +176,27 @@ static long do_waitid(int which, pid_t id, siginfo_t* infop, int options) { g_process.wait_queue = &qnode; __atomic_store_n(&qnode.in_use, true, __ATOMIC_RELEASE); - __atomic_store_n(&self->signal_handled, SIGNAL_NOT_HANDLED, __ATOMIC_RELEASE); - unlock(&g_process.children_lock); - while (1) { - if (__atomic_load_n(&self->signal_handled, __ATOMIC_ACQUIRE) == SIGNAL_HANDLED) { - remove_qnode_from_wait_queue(&qnode); - return -EINTR; - } - - DkEventClear(self->scheduler_event); - /* Check `mark_child_exited` for explanation why we might need this compiler barrier. */ - COMPILER_BARRIER(); - /* Check that we are still supposed to sleep. */ - if (!__atomic_load_n(&qnode.in_use, __ATOMIC_ACQUIRE)) { - break; - } - ret = thread_sleep(NO_TIMEOUT); - if (ret < 0 && ret != -EINTR && ret != -EAGAIN && ret != -EWOULDBLOCK) { - debug("thread_sleep failed in waitid\n"); - - remove_qnode_from_wait_queue(&qnode); - /* `ret` is already set. */ - return ret; - } - - handle_signals(); + DkEventClear(self->scheduler_event); + /* Check `mark_child_exited` for explanation why we might need this compiler barrier. */ + COMPILER_BARRIER(); + /* Check that we are still supposed to sleep. */ + if (!__atomic_load_n(&qnode.in_use, __ATOMIC_ACQUIRE)) { + break; + } + ret = thread_sleep(NO_TIMEOUT, /*ignore_pending_signals=*/false); + if (ret < 0 && ret != -EINTR && ret != -EAGAIN) { + debug("thread_sleep failed in waitid\n"); + remove_qnode_from_wait_queue(&qnode); + /* `ret` is already set. */ + goto out; } - lock(&g_process.children_lock); - } + ret = -ERESTARTSYS; + + remove_qnode_from_wait_queue(&qnode); + } while (!have_pending_signals()); out: unlock(&g_process.children_lock); diff --git a/LibOS/shim/src/syscallas-x86_64.S b/LibOS/shim/src/syscallas-x86_64.S index fc98acec..6ad26240 100644 --- a/LibOS/shim/src/syscallas-x86_64.S +++ b/LibOS/shim/src/syscallas-x86_64.S @@ -1,14 +1,15 @@ /* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* Copyright (C) 2014 Stony Brook University */ +/* Copyright (C) 2020 Intel Corporation + * Borys Popławski + */ /* - * This file contains the entry point of system call table in library OS (the function syscalldb() - * and its wrapper syscall_wrapper() for cases of redirection of raw SYSCALL instructions). + * This file contains the entry point of system call table in library OS (the function syscalldb()). * * The below entry point implementation first saves the CPU context of the current application - * thread on the thread's stack, then calls the corresponding LibOS syscall-emulation function, and - * then restores the context and passes control back to the application. The context consists of - * GPRs, FP control word (fpcw) and the SSE/AVX/... control word (mxcsr). + * thread on the thread's LibOS stack, then calls the LibOS syscall-emulation function, which, upon + * returning, calls context restoring function, which passes control back to the application. + * The context consists of GPRs, FP control word (fpcw) and the SSE/AVX/... control word (mxcsr). * * Note that LibOS may clobber all FP/SSE/AVX/... (extended) state except the control words. We rely * on the fact that applications do *not* assume that this extended state is preserved across system @@ -19,154 +20,255 @@ */ #include "asm-offsets.h" -#include "shim_defs.h" - .global syscalldb - .type syscalldb, @function - .extern shim_table, debug_unsupp - .global syscall_wrapper - .type syscall_wrapper, @function - .global syscall_wrapper_after_syscalldb - .type syscall_wrapper_after_syscalldb, @function +.extern shim_emulate_syscall +.extern shim_xstate_size +.extern shim_xstate_restore +.global syscalldb +.type syscalldb, @function syscalldb: - .cfi_startproc + # On entry to this function rcx contains the return value (next instruction after syscall), + # all other registers can have arbitrary values. + # We have to be very careful with executed instructions not to change any flags until they + # are saved! + .cfi_startproc + .cfi_def_cfa %rsp, 0 + .cfi_register %rip, %rcx - # Create shim_regs struct on the stack. - pushfq + # We can clobber r11 as it will be set to rflags later on. + mov %rsp, %r11 + .cfi_undefined %r11 + .cfi_register %rsp, %r11 + .cfi_def_cfa_register %r11 + mov %gs:(SHIM_TCB_OFF + SHIM_TCB_LIBOS_STACK_OFF), %rsp - # Under GDB, single-stepping sets Trap Flag (TP) of EFLAGS, - # thus TP=1 is stored on pushfq above. Upon consequent popfq, - # TP is 1, resulting in spurious trap. Reset TP here. - andq $~0x100, (%rsp) + # Create PAL_CONTEXT struct on the stack. - cld - pushq %rbp - pushq %rbx - pushq %rdi - pushq %rsi - pushq %rdx - pushq %rcx - pushq %r8 - pushq %r9 - pushq %r10 - pushq %r11 - pushq %r12 - pushq %r13 - pushq %r14 - pushq %r15 - leaq SHIM_REGS_SIZE - SHIM_REGS_R15(%rsp), %rbx - pushq %rbx - pushq %rax - # shim_regs struct ends here. + # reserve space for mxcsr + fpcw + is_fpregs_used + pushq $0 - movq %rsp, %rbp - .cfi_def_cfa_offset SHIM_REGS_SIZE - .cfi_offset %rbp, -3 * 8 # saved_rbp is at CFA-24 (saved_rflags + saved_rbp) - .cfi_def_cfa_register %rbp # %rbp + # fpregs, but for now we use this to store rax - to get a scratch register + push %rax - cmp $LIBOS_SYSCALL_BOUND, %rax - jae isundef + # err + trapno + oldmask + cr2 are cleared for a syscall frame + mov $0, %eax + push %rax + push %rax + push %rax + push %rax - movq shim_table@GOTPCREL(%rip), %rbx - movq (%rbx,%rax,8), %rbx - cmp $0, %rbx - je isundef + # csgsfsss - default value, as we do not support changing it + mov $(0x2b << 48 | 0x33), %rax + push %rax - # set pointer to shim_regs and save FP Control Word & MXCSR into current thread's TCB - movq %rbp, %gs:(SHIM_TCB_OFFSET + TCB_REGS) - fnstcw %gs:(SHIM_TCB_OFFSET + TCB_FPCW) - stmxcsr %gs:(SHIM_TCB_OFFSET + TCB_MXCSR) + # after this we can use instructions changing flags + pushfq + # Debuggers use Trap Flag (TF) of EFLAGS to do single-stepping - otherwise it is unused by normal + # applications. If the previous instruction was single-stepped, it stored TF, so reset it here. + andq $~0x100, (%rsp) - /* Translating x86_64 kernel calling convention to user-space - * calling convention */ - movq %r10, %rcx - andq $~0xF, %rsp # Required by System V AMD64 ABI. - call *%rbx + # Set default rflags value (just IF set). + pushq $0x202 + popfq - # invalidate pointer to shim_regs and restore FP Control Word & MXCSR from TCB - movq $0, %gs:(SHIM_TCB_OFFSET + TCB_REGS) - fldcw %gs:(SHIM_TCB_OFFSET + TCB_FPCW) - ldmxcsr %gs:(SHIM_TCB_OFFSET + TCB_MXCSR) + push %rcx # rip + push %r11 # rsp + .cfi_def_cfa %rsp, 0x50 + .cfi_rel_offset %rsp, 0 + .cfi_rel_offset %rip, 8 -ret: - movq %rbp, %rsp - addq $2 * 8, %rsp # skip orig_rax and rsp - popq %r15 - popq %r14 - popq %r13 - popq %r12 - popq %r11 - popq %r10 - popq %r9 - popq %r8 - popq %rcx - popq %rdx - popq %rsi - popq %rdi - popq %rbx - popq %rbp - .cfi_def_cfa %rsp, 2 * 8 # +8 for ret_addr, +8 for saved_rflags - popfq - .cfi_def_cfa_offset 8 # +8 for ret_addr - retq + # Set r11 to rflags + mov 0x10(%rsp), %r11 + + push %rcx + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rcx, 0 + + # rax was saved in fpregs, save it in proper place now, fpregs will be populated later + pushq 0x48(%rsp) + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rax, 0 + + push %rdx + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdx, 0 + push %rbx + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rbx, 0 + push %rbp + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rbp, 0 + push %rsi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rsi, 0 + push %rdi + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %rdi, 0 + push %r15 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r15, 0 + push %r14 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r14, 0 + push %r13 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r13, 0 + push %r12 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r12, 0 + push %r11 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r11, 0 + push %r10 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r10, 0 + push %r9 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r9, 0 + push %r8 + .cfi_adjust_cfa_offset 8 + .cfi_rel_offset %r8, 0 + # PAL_CONTEXT struct ends here. + + mov %rsp, %r15 + .cfi_def_cfa_register %r15 + + and $~0xF, %rsp # Required by System V AMD64 ABI. + + # save FP Control Word & MXCSR into current thread's TCB + stmxcsr PAL_CONTEXT_MXCSR_OFF(%r15) + fnstcw PAL_CONTEXT_FPCW_OFF(%r15) + + # fpregs is not populated, so is_fpregs_used should be 0. + movb $0, PAL_CONTEXT_FPREGS_USED_OFF(%r15) + + call shim_xstate_size + sub %rax, %rsp # allocate space for xstate + and $~(SHIM_XSTATE_ALIGN - 1), %rsp + mov %rsp, PAL_CONTEXT_FPREGS_OFF(%r15) + + and $~0xF, %rsp # Required by System V AMD64 ABI. + xor %ebp, %ebp -isundef: #ifdef DEBUG - mov %rax, %rdi - andq $~0xF, %rsp # Required by System V AMD64 ABI. - call *debug_unsupp@GOTPCREL(%rip) + # Pretend that this function (`syscalldb`) is called from somewhere inside a function called + # `__morestack`. This is the only way to have a backtrace in GDB spanning from LibOS/Pal, + # through `syscalldb` to the user application code/libc, because GDB does not handle switching + # stacks in the middle of backtrace, unless the function doing it is called `__morestack`. + # Thanks GDB! + # Technical details: we load an address somewhere inside `__morestack` (this cannot be the first + # instruction in there) into r14 (a callee-saved register) and mark it as holding old rip. This + # way GDB thinks `syscalldb` was called by `__morestack`. Inside `__morestack` we mark all + # registers as having the same value as in the previous frame (basically a no-op frame). Now GDB + # sees a backtrace: `user_function` -> `__morestack` -> `syscalldb`, with `__morestack` having + # the same stack value as `user_function` and `syscalldb` having the new stack value. This makes + # GDB happy and it prints correct backtrace across all these functions, which is what we are + # after with all this madness. + lea Lmorestack_for_gdb_bt(%rip), %r14 + .cfi_register %rip, %r14 #endif - movq $-38, %rax # ENOSYS - jmp ret - .cfi_endproc - .size syscalldb, .-syscalldb + mov %r15, %rdi + call shim_emulate_syscall # this does not return - /* - * syscall_wrapper: emulate syscall instruction - * prohibited in e.g. Linux-SGX PAL which raises a SIGILL exception - * See illegal_upcall() @ shim_signal.c and - * fixup_child_context() @ shim_clone.c - * - * input: - * %rcx: Instruction address to continue app execution after trapped - * syscall instruction - * %r11: rflags on entering syscall - * - * Omit CFI information for %rflags when compiling under clang, see bug: - * https://bugs.llvm.org/show_bug.cgi?id=33633 - */ -syscall_wrapper: - .cfi_startproc - .cfi_def_cfa %rsp, 0 - # %rcx is used as input for returning %rip - .cfi_register %rip, %rcx - # %r11 is used as input to keep %rflags -#ifndef __clang__ - .cfi_register %rflags, %r11 -#endif - subq $RED_ZONE_SIZE, %rsp - .cfi_adjust_cfa_offset RED_ZONE_SIZE - callq *syscalldb@GOTPCREL(%rip) -syscall_wrapper_after_syscalldb: - addq $RED_ZONE_SIZE, %rsp - .cfi_adjust_cfa_offset -RED_ZONE_SIZE - # restore %rflags for syscall abi compatibility. - # This must be done after "addq $RED_ZONE_SIZE, %rsp" above - # which destroys %rflags - xchg %r11, (%rsp) -#ifndef __clang__ - .cfi_offset %rflags, 0 -#endif - popfq - .cfi_adjust_cfa_offset -8 -#ifndef __clang__ - .cfi_same_value %rflags -#endif - pushq %r11 - .cfi_adjust_cfa_offset 8 - jmp *%rcx + # Just to make return address point inside this function. + ud2 - .cfi_endproc - .size syscall_wrapper, .-syscall_wrapper + .cfi_endproc +.size syscalldb, .-syscalldb + +#ifdef DEBUG +.global __morestack +.type __morestack, @function +__morestack: + .cfi_startproc + .cfi_register %rip, %rcx + .cfi_same_value %r8 + .cfi_same_value %r9 + .cfi_same_value %r10 + .cfi_same_value %r11 + .cfi_same_value %r12 + .cfi_same_value %r13 + .cfi_same_value %r14 + .cfi_same_value %r15 + .cfi_same_value %rdi + .cfi_same_value %rsi + .cfi_same_value %rbp + .cfi_same_value %rbx + .cfi_same_value %rdx + .cfi_same_value %rax + .cfi_same_value %rcx + .cfi_same_value %rsp + + nop +Lmorestack_for_gdb_bt: + nop + + .cfi_endproc +.size __morestack, .-__morestack +#endif + +.global return_from_syscall +.type return_from_syscall, @function +return_from_syscall: + # expects one argument (in `rdi`) - pointer to PAL_CONTEXT + .cfi_startproc + + mov %rdi, %rbx + + movb PAL_CONTEXT_FPREGS_USED_OFF(%rbx), %al + test %al, %al + jne .Lrestore_xstate + + # restore FP Control Word & MXCSR from TCB + fldcw PAL_CONTEXT_FPCW_OFF(%rbx) + ldmxcsr PAL_CONTEXT_MXCSR_OFF(%rbx) + +.Lrestore_context: + # After this line cfi will be broken, but we don't care much since this does not call anything + # and just restores the user context, so it will not be visible in any backtrace. + # Note that fixing it is not trivial - we would need the trick with `__morestack`, but we do not + # have neither a stack, nor a scratch register. + mov %rbx, %rsp + + pop %r8 + pop %r9 + pop %r10 + pop %r11 + pop %r12 + pop %r13 + pop %r14 + pop %r15 + pop %rdi + pop %rsi + pop %rbp + pop %rbx + pop %rdx + + # exchange rcx with rip + mov 0x8(%rsp), %rcx + mov 0x18(%rsp), %rax + mov %rcx, 0x18(%rsp) + mov %rax, 0x8(%rsp) + # exchange rsp with flags + mov 0x10(%rsp), %rcx + mov 0x20(%rsp), %rax + mov %rcx, 0x20(%rsp) + mov %rax, 0x10(%rsp) + + pop %rax + pop %rcx # rip + popfq + mov %rcx, %gs:(SHIM_TCB_OFF + SHIM_TCB_SCRATCH_PC_OFF) + pop %rcx + pop %rsp + jmp *%gs:(SHIM_TCB_OFF + SHIM_TCB_SCRATCH_PC_OFF) + +.Lrestore_xstate: + mov PAL_CONTEXT_FPREGS_OFF(%rbx), %rdi + call shim_xstate_restore + jmp .Lrestore_context + + .cfi_endproc +.size return_from_syscall, .-return_from_syscall diff --git a/LibOS/shim/src/vdso/arch/x86_64/vdso_syscall.h b/LibOS/shim/src/vdso/arch/x86_64/vdso_syscall.h new file mode 100644 index 00000000..b623a0f1 --- /dev/null +++ b/LibOS/shim/src/vdso/arch/x86_64/vdso_syscall.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* Copyright (C) 2020 Intel Corporation + * Borys Popławski + */ +#ifndef VDSO_SYSCALL_H_ +#define VDSO_SYSCALL_H_ + +static inline long vdso_arch_syscall(long (*syscalldb)(void), long nr, long arg1, long arg2) { + long ret; + __asm__ volatile( + "lea .Lret%=(%%rip), %%rcx\n" + "jmp *%[syscalldb]\n" + ".Lret%=:\n" + : "=a" (ret) + : "0" (nr), "D"(arg1), "S"(arg2), [syscalldb] "rm" (syscalldb) + : "memory", "rcx", "r11" + ); + return ret; +} + +#endif // VDSO_SYSCALL_H_ diff --git a/LibOS/shim/src/vdso/vdso-x86_64.lds b/LibOS/shim/src/vdso/vdso-x86_64.lds index c96f33db..79c0f161 100644 --- a/LibOS/shim/src/vdso/vdso-x86_64.lds +++ b/LibOS/shim/src/vdso/vdso-x86_64.lds @@ -60,10 +60,7 @@ VERSION { time; __vdso_time; - __vdso_shim_clock_gettime; - __vdso_shim_gettimeofday; - __vdso_shim_getcpu; - __vdso_shim_time; + __vdso_syscalldb; local: *; }; } diff --git a/LibOS/shim/src/vdso/vdso.c b/LibOS/shim/src/vdso/vdso.c index a45765ba..ebb152a4 100644 --- a/LibOS/shim/src/vdso/vdso.c +++ b/LibOS/shim/src/vdso/vdso.c @@ -2,53 +2,44 @@ /* Copyright (C) 2018 Intel Corporation * Isaku Yamahata * + * Copyright (C) 2020 Intel Corporation + * Borys Popławski */ +#include + #include "vdso.h" +#include "vdso_syscall.h" /* - * The symbols below need to be exported for libsysdb to inject those values, + * The symbol below needs to be exported for libsysdb to inject those values, * but relocation (.rela.dyn section) isn't wanted in the code generation. */ #define EXPORT_SYMBOL(name) extern __typeof__(name) __vdso_##name __attribute__((alias(#name))) -static int (*shim_clock_gettime)(clockid_t clock, struct timespec* t) = NULL; -static int (*shim_gettimeofday)(struct timeval* tv, struct timezone* tz) = NULL; -static time_t (*shim_time)(time_t* t) = NULL; -static long (*shim_getcpu)(unsigned* cpu, struct getcpu_cache* unused) = NULL; +static long (*syscalldb)(void) = NULL; -EXPORT_SYMBOL(shim_clock_gettime); -EXPORT_SYMBOL(shim_gettimeofday); -EXPORT_SYMBOL(shim_time); -EXPORT_SYMBOL(shim_getcpu); +EXPORT_SYMBOL(syscalldb); #define EXPORT_WEAK_SYMBOL(name) \ __typeof__(__vdso_##name) name __attribute__((weak, alias("__vdso_" #name))) int __vdso_clock_gettime(clockid_t clock, struct timespec* t) { - if (shim_clock_gettime) - return (*shim_clock_gettime)(clock, t); - return -ENOSYS; + return vdso_arch_syscall(syscalldb, __NR_clock_gettime, (long)clock, (long)t); } EXPORT_WEAK_SYMBOL(clock_gettime); int __vdso_gettimeofday(struct timeval* tv, struct timezone* tz) { - if (shim_gettimeofday) - return (*shim_gettimeofday)(tv, tz); - return -ENOSYS; + return vdso_arch_syscall(syscalldb, __NR_gettimeofday, (long)tv, (long)tz); } EXPORT_WEAK_SYMBOL(gettimeofday); time_t __vdso_time(time_t* t) { - if (shim_time) - return (*shim_time)(t); - return -ENOSYS; + return vdso_arch_syscall(syscalldb, __NR_time, (long)t, 0); } EXPORT_WEAK_SYMBOL(time); long __vdso_getcpu(unsigned* cpu, struct getcpu_cache* unused) { - if (shim_getcpu) - return (*shim_getcpu)(cpu, unused); - return -ENOSYS; + return vdso_arch_syscall(syscalldb, __NR_getcpu, (long)cpu, (long)unused); } EXPORT_WEAK_SYMBOL(getcpu); diff --git a/LibOS/shim/test/ltp/ltp.cfg b/LibOS/shim/test/ltp/ltp.cfg index f3c482b1..96306e3d 100644 --- a/LibOS/shim/test/ltp/ltp.cfg +++ b/LibOS/shim/test/ltp/ltp.cfg @@ -2562,11 +2562,6 @@ skip = yes [tee02] skip = yes -# timeouts on test cleanup -[tgkill01] -must-pass = - 1 - # RLIMIT_SIGPENDING not supported [tgkill02] skip = yes diff --git a/LibOS/shim/test/regression/openmp.manifest.template b/LibOS/shim/test/regression/openmp.manifest.template index ebe43cf2..5fb8d49e 100644 --- a/LibOS/shim/test/regression/openmp.manifest.template +++ b/LibOS/shim/test/regression/openmp.manifest.template @@ -4,8 +4,9 @@ loader.argv0_override = "openmp" loader.env.LD_LIBRARY_PATH = "/lib:/usrlib" -# the below manifest option is added only for testing, it has no significance for OpenMP +# two manifest options below are added only for testing, they have no significance for OpenMP libos.check_invalid_pointers = 0 +sys.enable_sigterm_injection = 1 fs.mount.lib.type = "chroot" fs.mount.lib.path = "/lib" diff --git a/Pal/include/arch/x86_64/Linux/ucontext.h b/Pal/include/arch/x86_64/Linux/ucontext.h index 85e35168..f906f9cf 100644 --- a/Pal/include/arch/x86_64/Linux/ucontext.h +++ b/Pal/include/arch/x86_64/Linux/ucontext.h @@ -1,269 +1,106 @@ -/* Copyright (C) 2001, 2002 Free Software Foundation, Inc. - This file is part of the GNU C Library. +/* SPDX-License-Identifier: LGPL-3.0-or-later */ +/* Copyright (C) 2020 Intel Corporation + * Borys Popławski + */ +#ifndef LINUX_X86_64_UCONTEXT_H_ +#define LINUX_X86_64_UCONTEXT_H_ - The GNU C Library is free software; you can redistribute it and/or - modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - The GNU C Library is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with the GNU C Library; if not, write to the Free - Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - 02111-1307 USA. */ - -#ifndef _LINUX_X86_64_UCONTEXT_H -#define _LINUX_X86_64_UCONTEXT_H 1 - -#include #include -/* We need the signal context definitions even if they are not used - included in . */ -#include "sigcontext.h" - #include "api.h" #include "assert.h" #include "pal.h" -#if __WORDSIZE == 64 +/* Structures definition source: + * https://elixir.bootlin.com/linux/v5.10.3/source/include/uapi/asm-generic/ucontext.h */ -/* Type for general register. */ -typedef long int greg_t; +#define UC_FP_XSTATE 1 +#define UC_SIGCONTEXT_SS 2 +#define UC_STRICT_RESTORE_SS 4 -/* Number of general registers. */ -#define NGREG 23 - -/* Container for all general registers. */ -typedef greg_t gregset_t[NGREG]; - -/* Number of each register in the `gregset_t' array. */ -enum { - REG_R8 = 0, -#define REG_R8 REG_R8 - REG_R9, -#define REG_R9 REG_R9 - REG_R10, -#define REG_R10 REG_R10 - REG_R11, -#define REG_R11 REG_R11 - REG_R12, -#define REG_R12 REG_R12 - REG_R13, -#define REG_R13 REG_R13 - REG_R14, -#define REG_R14 REG_R14 - REG_R15, -#define REG_R15 REG_R15 - REG_RDI, -#define REG_RDI REG_RDI - REG_RSI, -#define REG_RSI REG_RSI - REG_RBP, -#define REG_RBP REG_RBP - REG_RBX, -#define REG_RBX REG_RBX - REG_RDX, -#define REG_RDX REG_RDX - REG_RAX, -#define REG_RAX REG_RAX - REG_RCX, -#define REG_RCX REG_RCX - REG_RSP, -#define REG_RSP REG_RSP - REG_RIP, -#define REG_RIP REG_RIP - REG_EFL, -#define REG_EFL REG_EFL - REG_CSGSFS, /* Actually short cs, gs, fs, __pad0. */ -#define REG_CSGSFS REG_CSGSFS - REG_ERR, -#define REG_ERR REG_ERR - REG_TRAPNO, -#define REG_TRAPNO REG_TRAPNO - REG_OLDMASK, -#define REG_OLDMASK REG_OLDMASK - REG_CR2 -#define REG_CR2 REG_CR2 -}; - -struct _libc_fpxreg { - unsigned short int significand[4]; - unsigned short int exponent; - unsigned short int padding[3]; -}; - -struct _libc_xmmreg { - uint32_t element[4]; -}; - -struct _libc_fpstate { +struct _fpstate { /* 64-bit FXSAVE format. */ uint16_t cwd; uint16_t swd; - uint16_t ftw; + uint16_t twd; uint16_t fop; uint64_t rip; uint64_t rdp; uint32_t mxcsr; uint32_t mxcr_mask; - struct _libc_fpxreg _st[8]; - struct _libc_xmmreg _xmm[16]; - uint32_t padding[24]; + uint32_t st_space[32]; + uint32_t xmm_space[64]; + uint32_t _reserved[24]; +} __attribute__((packed, aligned(64))); + +struct sigcontext { + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rbx; + uint64_t rdx; + uint64_t rax; + uint64_t rcx; + uint64_t rsp; + uint64_t rip; + uint64_t eflags; + union { + struct { + uint16_t cs; + uint16_t gs; + uint16_t fs; + uint16_t ss; + }; + uint64_t csgsfsss; + }; + uint64_t err; + uint64_t trapno; + uint64_t oldmask; + uint64_t cr2; + struct _fpstate* fpstate; + uint64_t reserved1[8]; }; -/* Structure to describe FPU registers. */ -typedef struct _libc_fpstate* fpregset_t; - -/* Context to describe whole processor state. */ -typedef struct { - gregset_t gregs; - /* Note that fpregs is a pointer. */ - fpregset_t fpregs; - unsigned long __reserved1[8]; -} mcontext_t; - -/* Userlevel context. */ typedef struct ucontext { - unsigned long int uc_flags; - struct ucontext* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - __sigset_t uc_sigmask; - struct _libc_fpstate __fpregs_mem; + unsigned long uc_flags; + struct ucontext* uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + __sigset_t uc_sigmask; } ucontext_t; /* fpregs is shallow copied by only setting a pointer */ static inline void ucontext_to_pal_context(PAL_CONTEXT* context, ucontext_t* uc) { - static_assert(sizeof(uc->uc_mcontext.gregs) == offsetof(struct PAL_CONTEXT_, fpregs), - "uc's gregs and PAL_CONTEXT sizes are different"); - memcpy(&context->r8, uc->uc_mcontext.gregs, sizeof(uc->uc_mcontext.gregs)); - context->fpregs = (PAL_XREGS_STATE*)uc->uc_mcontext.fpregs; + static_assert(offsetof(PAL_CONTEXT, fpregs) == offsetof(struct sigcontext, fpstate), + "This requires `PAL_CONTEXT` and `sigcontext` to have same layout"); + memcpy(context, &uc->uc_mcontext, offsetof(struct sigcontext, fpstate)); + context->fpregs = (PAL_XREGS_STATE*)uc->uc_mcontext.fpstate; + context->is_fpregs_used = context->fpregs ? 1 : 0; } /* fpregs is shallow copied by only setting a pointer */ static inline void pal_context_to_ucontext(ucontext_t* uc, PAL_CONTEXT* context) { - static_assert(sizeof(uc->uc_mcontext.gregs) == offsetof(struct PAL_CONTEXT_, fpregs), - "uc's gregs and PAL_CONTEXT sizes are different"); - memcpy(uc->uc_mcontext.gregs, &context->r8, sizeof(uc->uc_mcontext.gregs)); - uc->uc_mcontext.fpregs = (struct _libc_fpstate*)context->fpregs; + memcpy(&uc->uc_mcontext, context, offsetof(struct sigcontext, fpstate)); + uc->uc_mcontext.fpstate = context->is_fpregs_used ? (struct _fpstate*)context->fpregs : NULL; } -static inline uint64_t pal_ucontext_get_ip(ucontext_t* uc) { - return uc->uc_mcontext.gregs[REG_RIP]; +static inline uint64_t ucontext_get_ip(ucontext_t* uc) { + return uc->uc_mcontext.rip; } -static inline void pal_ucontext_set_function_parameters(ucontext_t* uc, void* func, - size_t func_args_num, greg_t* func_args) { - const unsigned int param_regs[] = {REG_RDI, REG_RSI, REG_RDX, REG_RCX}; - - assert(func_args_num <= ARRAY_SIZE(param_regs)); - - uc->uc_mcontext.gregs[REG_RIP] = (greg_t)func; - for (size_t i = 0; i < func_args_num; i++) - uc->uc_mcontext.gregs[param_regs[i]] = func_args[i]; +static inline void ucontext_set_function_parameters(ucontext_t* uc, void* func, uint64_t arg1, + uint64_t arg2) { + uc->uc_mcontext.rip = (uint64_t)func; + uc->uc_mcontext.rdi = arg1; + uc->uc_mcontext.rsi = arg2; } -#else /* __WORDSIZE == 32 */ - -/* Type for general register. */ -typedef int greg_t; - -/* Number of general registers. */ -#define NGREG 19 - -/* Container for all general registers. */ -typedef greg_t gregset_t[NGREG]; - -#ifdef __USE_GNU -/* Number of each register is the `gregset_t' array. */ -enum { - REG_GS = 0, -#define REG_GS REG_GS - REG_FS, -#define REG_FS REG_FS - REG_ES, -#define REG_ES REG_ES - REG_DS, -#define REG_DS REG_DS - REG_EDI, -#define REG_EDI REG_EDI - REG_ESI, -#define REG_ESI REG_ESI - REG_EBP, -#define REG_EBP REG_EBP - REG_ESP, -#define REG_ESP REG_ESP - REG_EBX, -#define REG_EBX REG_EBX - REG_EDX, -#define REG_EDX REG_EDX - REG_ECX, -#define REG_ECX REG_ECX - REG_EAX, -#define REG_EAX REG_EAX - REG_TRAPNO, -#define REG_TRAPNO REG_TRAPNO - REG_ERR, -#define REG_ERR REG_ERR - REG_EIP, -#define REG_EIP REG_EIP - REG_CS, -#define REG_CS REG_CS - REG_EFL, -#define REG_EFL REG_EFL - REG_UESP, -#define REG_UESP REG_UESP - REG_SS -#define REG_SS REG_SS -}; -#endif - -/* Definitions taken from the kernel headers. */ -struct _libc_fpreg { - unsigned short int significand[4]; - unsigned short int exponent; -}; - -struct _libc_fpstate { - unsigned long int cw; - unsigned long int sw; - unsigned long int tag; - unsigned long int ipoff; - unsigned long int cssel; - unsigned long int dataoff; - unsigned long int datasel; - struct _libc_fpreg _st[8]; - unsigned long int status; -}; - -/* Structure to describe FPU registers. */ -typedef struct _libc_fpstate* fpregset_t; - -/* Context to describe whole processor state. */ -typedef struct { - gregset_t gregs; - /* Due to Linux's history we have to use a pointer here. The SysV/i386 - ABI requires a struct with the values. */ - fpregset_t fpregs; - unsigned long int oldmask; - unsigned long int cr2; -} mcontext_t; - -/* Userlevel context. */ -typedef struct ucontext { - unsigned long int uc_flags; - struct ucontext* uc_link; - stack_t uc_stack; - mcontext_t uc_mcontext; - __sigset_t uc_sigmask; - struct _libc_fpstate __fpregs_mem; -} ucontext_t; - -#endif /* __WORDSIZE == 32 */ - -#endif /* _LINUX_X86_64_UCONTEXT_H */ +#endif /* LINUX_X86_64_UCONTEXT_H_ */ diff --git a/Pal/include/arch/x86_64/pal-arch.h b/Pal/include/arch/x86_64/pal-arch.h index d3aaa36b..1ffb9fa7 100644 --- a/Pal/include/arch/x86_64/pal-arch.h +++ b/Pal/include/arch/x86_64/pal-arch.h @@ -1,5 +1,8 @@ /* SPDX-License-Identifier: LGPL-3.0-or-later */ -/* Copyright (C) 2014 Stony Brook University */ +/* Copyright (C) 2014 Stony Brook University + * Copyright (C) 2020 Intel Corporation + * Borys Popławski + */ /* * This file contains definition of x86_64-specific aspects of PAL. @@ -16,6 +19,7 @@ #include #include +#include "api.h" #include "cpu.h" #include "pal.h" @@ -186,26 +190,86 @@ typedef struct { PAL_XSTATE_HEADER header; } __attribute__((packed, aligned(PAL_XSTATE_ALIGN))) PAL_XREGS_STATE; -/* Define PAL_CONTEXT_ outside the typedef for Doxygen */ -struct PAL_CONTEXT_ { - PAL_NUM r8, r9, r10, r11, r12, r13, r14, r15; - PAL_NUM rdi, rsi, rbp, rbx, rdx, rax, rcx; - PAL_NUM rsp, rip; - PAL_NUM efl, csgsfs, err, trapno, oldmask, cr2; +/* Define PAL_CONTEXT outside the typedef for Doxygen */ +struct PAL_CONTEXT { + uint64_t r8; + uint64_t r9; + uint64_t r10; + uint64_t r11; + uint64_t r12; + uint64_t r13; + uint64_t r14; + uint64_t r15; + uint64_t rdi; + uint64_t rsi; + uint64_t rbp; + uint64_t rbx; + uint64_t rdx; + uint64_t rax; + uint64_t rcx; + uint64_t rsp; + uint64_t rip; + uint64_t efl; + uint64_t csgsfsss; + uint64_t err; + uint64_t trapno; + uint64_t oldmask; + uint64_t cr2; + PAL_XREGS_STATE* fpregs; + + uint32_t mxcsr; /* MXCSR control/status register (for SSE/AVX/...) */ + uint16_t fpcw; /* FPU Control Word (for x87) */ + uint8_t is_fpregs_used; /* Equal to 0 iff `fpregs` is not populated. */ + uint8_t _pad; }; -typedef struct PAL_CONTEXT_ PAL_CONTEXT; +typedef struct PAL_CONTEXT PAL_CONTEXT; + +typedef int64_t arch_syscall_arg_t; + +#define ALL_SYSCALL_ARGS(context) \ + (context)->rdi, \ + (context)->rsi, \ + (context)->rdx, \ + (context)->r10, \ + (context)->r8, \ + (context)->r9 + static inline void pal_context_set_ip(PAL_CONTEXT* context, PAL_NUM insnptr) { context->rip = insnptr; } - static inline PAL_NUM pal_context_get_ip(PAL_CONTEXT* context) { return context->rip; } -static inline bool pal_context_has_user_pagefault(PAL_CONTEXT* context) { - return !!(context->err & 4); +static inline void pal_context_set_sp(PAL_CONTEXT* context, PAL_NUM sp) { + context->rsp = sp; +} +static inline PAL_NUM pal_context_get_sp(PAL_CONTEXT* context) { + return context->rsp; +} + +static inline void pal_context_set_retval(PAL_CONTEXT* context, uint64_t val) { + context->rax = val; +} +static inline uint64_t pal_context_get_retval(PAL_CONTEXT* context) { + return context->rax; +} + +static inline uint64_t pal_context_get_syscall(PAL_CONTEXT* context) { + return context->rax; +} + +/* Copies `PAL_CONTEXT` without extended FPU/SSE state (but keeping control words). */ +static inline void pal_context_copy(PAL_CONTEXT* dst, PAL_CONTEXT* src) { + *dst = *src; + dst->is_fpregs_used = 0; + dst->fpregs = NULL; + if (src->is_fpregs_used) { + dst->mxcsr = src->fpregs->fpstate.mxcsr; + dst->fpcw = src->fpregs->fpstate.cwd; + } } enum { diff --git a/Pal/include/lib/spinlock.h b/Pal/include/lib/spinlock.h index 52e0e615..434ac65d 100644 --- a/Pal/include/lib/spinlock.h +++ b/Pal/include/lib/spinlock.h @@ -171,18 +171,6 @@ static inline void spinlock_unlock(spinlock_t* lock) { __atomic_store_n(&lock->lock, SPINLOCK_UNLOCKED, __ATOMIC_RELEASE); } -#ifdef IN_SHIM -static inline void spinlock_lock_signal_off(spinlock_t* lock) { - disable_preempt(NULL); - spinlock_lock(lock); -} - -static inline void spinlock_unlock_signal_on(spinlock_t* lock) { - spinlock_unlock(lock); - enable_preempt(NULL); -} -#endif // IN_SHIM - #ifdef DEBUG_SPINLOCKS static inline bool _spinlock_is_locked(spinlock_t* lock) { return __atomic_load_n(&lock->lock, __ATOMIC_SEQ_CST) != SPINLOCK_UNLOCKED; diff --git a/Pal/include/pal/pal.h b/Pal/include/pal/pal.h index b1cd5feb..4f4b1560 100644 --- a/Pal/include/pal/pal.h +++ b/Pal/include/pal/pal.h @@ -553,22 +553,27 @@ enum PAL_EVENT { /*! arithmetic error (div-by-zero, floating point exception, etc.) */ PAL_EVENT_ARITHMETIC_ERROR = 1, /*! segmentation fault, protection fault, bus fault */ - PAL_EVENT_MEMFAULT = 2, + PAL_EVENT_MEMFAULT, /*! illegal instructions */ - PAL_EVENT_ILLEGAL = 3, - /*! terminated by external program */ - PAL_EVENT_QUIT = 4, - /*! suspended by external program */ - PAL_EVENT_SUSPEND = 5, - /*! continued by external program */ - PAL_EVENT_RESUME = 6, + PAL_EVENT_ILLEGAL, + /*! terminated by external program (see "sys.enable_sigterm_injection" manifest option) */ + PAL_EVENT_QUIT, + /*! interrupted (usually internally to handle aync event) */ + PAL_EVENT_INTERRUPTED, /*! failure within PAL calls */ - PAL_EVENT_FAILURE = 7, + PAL_EVENT_FAILURE, - PAL_EVENT_NUM_BOUND = 8, + PAL_EVENT_NUM_BOUND, }; -typedef void (*PAL_EVENT_HANDLER)(PAL_NUM arg, PAL_CONTEXT*); +/*! + * \brief Type of exception handlers (upcalls). + * + * \param is_in_pal `true` if the exception happened inside PAL + * \param addr address of the exception (meaningful only for sync exceptions) + * \param context CPU context at the moment of exception. + */ +typedef void (*PAL_EVENT_HANDLER)(bool is_in_pal, PAL_NUM addr, PAL_CONTEXT* context); /*! * \brief Set the handler for the specific exception event. diff --git a/Pal/regression/Bootstrap.c b/Pal/regression/Bootstrap.c index a21887bc..ee347bce 100644 --- a/Pal/regression/Bootstrap.c +++ b/Pal/regression/Bootstrap.c @@ -1,5 +1,3 @@ -#include - #include "pal.h" #include "pal_debug.h" diff --git a/Pal/regression/Event.c b/Pal/regression/Event.c index 7d95f962..b24d059f 100644 --- a/Pal/regression/Event.c +++ b/Pal/regression/Event.c @@ -1,4 +1,5 @@ #include +#include #include "pal.h" #include "pal_debug.h" @@ -18,7 +19,9 @@ static int thread2_run(void* args) { /* UNREACHABLE */ } -static void pal_failure_handler(PAL_NUM error, PAL_CONTEXT* context) { +static void pal_failure_handler(bool is_in_pal, PAL_NUM error, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); + pal_printf("pal_failure_handler called\n"); if (error == PAL_ERROR_TRYAGAIN) { diff --git a/Pal/regression/Exception.c b/Pal/regression/Exception.c index 00e8a338..25843699 100644 --- a/Pal/regression/Exception.c +++ b/Pal/regression/Exception.c @@ -2,6 +2,7 @@ #include #include +#include #include "pal.h" #include "pal_debug.h" @@ -12,7 +13,9 @@ static void* get_stack(void) { return stack; } -static void handler1(PAL_NUM arg, PAL_CONTEXT* context) { +static void handler1(bool is_in_pal, PAL_NUM arg, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); + pal_printf("Arithmetic Exception Handler 1: 0x%08lx, rip = 0x%08lx\n", arg, context->rip); pal_printf("Stack in handler: %p\n", get_stack()); @@ -21,14 +24,18 @@ static void handler1(PAL_NUM arg, PAL_CONTEXT* context) { context->rip++; } -static void handler2(PAL_NUM arg, PAL_CONTEXT* context) { +static void handler2(bool is_in_pal, PAL_NUM arg, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); + pal_printf("Arithmetic Exception Handler 2: 0x%08lx, rip = 0x%08lx\n", arg, context->rip); while (*(unsigned char*)context->rip != 0x90) context->rip++; } -static void handler3(PAL_NUM arg, PAL_CONTEXT* context) { +static void handler3(bool is_in_pal, PAL_NUM arg, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); + pal_printf("Memory Fault Exception Handler: 0x%08lx, rip = 0x%08lx\n", arg, context->rip); while (*(unsigned char*)context->rip != 0x90) @@ -37,7 +44,9 @@ static void handler3(PAL_NUM arg, PAL_CONTEXT* context) { atomic_bool handler4_called = false; -static void handler4(PAL_NUM arg, PAL_CONTEXT* context) { +static void handler4(bool is_in_pal, PAL_NUM arg, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); + pal_printf("Arithmetic Exception Handler 4: 0x%" PRIx64 ", rip = 0x%" PRIx64 "\n", arg, context->rip); diff --git a/Pal/regression/Exception2.c b/Pal/regression/Exception2.c index 491fe53a..bf0ef7d1 100644 --- a/Pal/regression/Exception2.c +++ b/Pal/regression/Exception2.c @@ -1,10 +1,14 @@ +#include + #include "pal.h" #include "pal_debug.h" int count = 0; int i = 0; -static void handler(PAL_NUM arg, PAL_CONTEXT* context) { +static void handler(bool is_in_pal, PAL_NUM arg, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); + pal_printf("failure in the handler: 0x%08lx\n", arg); count++; diff --git a/Pal/regression/Failure.c b/Pal/regression/Failure.c index d2245049..8fbfefec 100644 --- a/Pal/regression/Failure.c +++ b/Pal/regression/Failure.c @@ -1,10 +1,14 @@ +#include + #include "pal.h" #include "pal_debug.h" #include "pal_error.h" int handled = 0; -static void FailureHandler(PAL_NUM arg, PAL_CONTEXT* context) { +static void FailureHandler(bool is_in_pal, PAL_NUM arg, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); + pal_printf("Failure notified: %s\n", pal_strerror((unsigned long)arg)); handled = 1; diff --git a/Pal/regression/Memory.c b/Pal/regression/Memory.c index eff03dfb..d8de4020 100644 --- a/Pal/regression/Memory.c +++ b/Pal/regression/Memory.c @@ -1,5 +1,7 @@ /* XXX: What on earth is this supposed to be, an attempt to fit most UBs in one file? */ +#include + #include "api.h" #include "pal.h" #include "pal_debug.h" @@ -8,7 +10,9 @@ static volatile int count = 0; -static void handler(PAL_NUM arg, PAL_CONTEXT* context) { +static void handler(bool is_in_pal, PAL_NUM arg, PAL_CONTEXT* context) { + __UNUSED(is_in_pal); + count++; pal_printf("Memory Fault %d\n", count); diff --git a/Pal/src/host/Linux-SGX/db_exception.c b/Pal/src/host/Linux-SGX/db_exception.c index 1b71199b..46c34646 100644 --- a/Pal/src/host/Linux-SGX/db_exception.c +++ b/Pal/src/host/Linux-SGX/db_exception.c @@ -56,7 +56,7 @@ noreturn static void restore_pal_context(sgx_cpu_context_t* uc, PAL_CONTEXT* ctx uc->rflags = ctx->efl; uc->rip = ctx->rip; - restore_sgx_context(uc, ctx->fpregs); + restore_sgx_context(uc, ctx->is_fpregs_used ? ctx->fpregs : NULL); } static void save_pal_context(PAL_CONTEXT* ctx, sgx_cpu_context_t* uc, @@ -87,10 +87,11 @@ static void save_pal_context(PAL_CONTEXT* ctx, sgx_cpu_context_t* uc, .gs = 0, .ss = 0x2b, // __USER_DS(6) | 0(GDT) | 3(RPL) }; - ctx->csgsfs = csgsfs.csgsfs; + ctx->csgsfsss = csgsfs.csgsfs; assert(xregs_state); ctx->fpregs = xregs_state; + ctx->is_fpregs_used = 1; /* Emulate format for fp registers Linux sets up as signal frame. * https://elixir.bootlin.com/linux/v5.4.13/source/arch/x86/kernel/fpu/signal.c#L86 @@ -214,8 +215,7 @@ void _DkExceptionHandler(unsigned int exit_info, sgx_cpu_context_t* uc, if (ADDR_IN_PAL(uc->rip) && /* event isn't asynchronous (i.e., synchronous exception) */ event_num != PAL_EVENT_QUIT && - event_num != PAL_EVENT_SUSPEND && - event_num != PAL_EVENT_RESUME) { + event_num != PAL_EVENT_INTERRUPTED) { printf("*** Unexpected exception occurred inside PAL at RIP = +0x%08lx! ***\n", uc->rip - (uintptr_t)TEXT_START); @@ -246,38 +246,42 @@ void _DkExceptionHandler(unsigned int exit_info, sgx_cpu_context_t* uc, /* TODO: save EXINFO from MISC region and populate below fields */ ctx.err = 0; - ctx.trapno = ei.info.valid ? ei.info.vector : event_num; + ctx.trapno = ei.info.valid ? ei.info.vector : 0; ctx.oldmask = 0; ctx.cr2 = 0; - PAL_NUM arg = 0; + PAL_NUM addr = 0; switch (event_num) { case PAL_EVENT_ILLEGAL: - arg = uc->rip; + addr = uc->rip; break; case PAL_EVENT_MEMFAULT: /* TODO: SGX1 doesn't provide fault address but SGX2 does (with lower bits masked) */ break; default: - /* nothing */ break; } PAL_EVENT_HANDLER upcall = _DkGetExceptionHandler(event_num); if (upcall) { - (*upcall)(arg, &ctx); + (*upcall)(ADDR_IN_PAL(uc->rip), addr, &ctx); } restore_pal_context(uc, &ctx); } +/* TODO: remove this function. It's not an exception handling, it's just returning an error from + * PAL... */ void _DkRaiseFailure(int error) { PAL_EVENT_HANDLER upcall = _DkGetExceptionHandler(PAL_EVENT_FAILURE); if (upcall) { - (*upcall)(error, /*context=*/NULL); + (*upcall)(/*is_in_pal=*/false, error, /*context=*/NULL); } } +/* TODO: shouldn't this function ignore sync events??? + * actually what is the point of this function? + * Tracked: https://github.com/oscarlab/graphene/issues/2140 */ noreturn void _DkHandleExternalEvent(PAL_NUM event, sgx_cpu_context_t* uc, PAL_XREGS_STATE* xregs_state) { assert(event > 0 && event < PAL_EVENT_NUM_BOUND); @@ -289,14 +293,10 @@ noreturn void _DkHandleExternalEvent(PAL_NUM event, sgx_cpu_context_t* uc, PAL_CONTEXT ctx; save_pal_context(&ctx, uc, xregs_state); - ctx.err = 0; - ctx.trapno = event; /* TODO: event is a PAL event; is that what LibOS/app wants to see? */ - ctx.oldmask = 0; - ctx.cr2 = 0; PAL_EVENT_HANDLER upcall = _DkGetExceptionHandler(event); if (upcall) { - (*upcall)(/*arg=*/0, &ctx); + (*upcall)(ADDR_IN_PAL(uc->rip), /*addr=*/0, &ctx); } /* modification to PAL_CONTEXT is discarded; it is assumed that LibOS won't change context diff --git a/Pal/src/host/Linux-SGX/sgx_exception.c b/Pal/src/host/Linux-SGX/sgx_exception.c index 26bca613..3e799fe0 100644 --- a/Pal/src/host/Linux-SGX/sgx_exception.c +++ b/Pal/src/host/Linux-SGX/sgx_exception.c @@ -51,7 +51,7 @@ __attribute__((visibility("hidden"))) void __restore_rt(void); void sgx_entry_return(void); -static const int ASYNC_SIGNALS[] = {SIGTERM, SIGINT, SIGCONT}; +static const int ASYNC_SIGNALS[] = {SIGTERM, SIGCONT}; static int block_signal(int sig, bool block) { int how = block ? SIG_BLOCK : SIG_UNBLOCK; @@ -103,17 +103,15 @@ static int get_pal_event(int sig) { return PAL_EVENT_ILLEGAL; case SIGTERM: return PAL_EVENT_QUIT; - case SIGINT: - return PAL_EVENT_SUSPEND; case SIGCONT: - return PAL_EVENT_RESUME; + return PAL_EVENT_INTERRUPTED; default: return -1; } } static bool interrupted_in_enclave(struct ucontext* uc) { - unsigned long rip = pal_ucontext_get_ip(uc); + unsigned long rip = ucontext_get_ip(uc); /* in case of AEX, RIP can point to any instruction in the AEP/ERESUME trampoline code, i.e., * RIP can point to anywhere in [async_exit_pointer, async_exit_pointer_end) interval */ @@ -139,7 +137,7 @@ static void handle_sync_signal(int signum, siginfo_t* info, struct ucontext* uc) } /* exception happened in untrusted PAL code (during syscall handling): fatal in Graphene */ - unsigned long rip = pal_ucontext_get_ip(uc); + unsigned long rip = ucontext_get_ip(uc); switch (signum) { case SIGSEGV: urts_log_error("Segmentation Fault in Untrusted Code (RIP = %08lx)\n", rip); @@ -180,8 +178,7 @@ static void handle_async_signal(int signum, siginfo_t* info, struct ucontext* uc * was interrupted by calling sgx_entry_return(syscall_return_value=-EINTR, event) */ /* TODO: we abandon PAL state here (possibly still holding some locks, etc) and return to * enclave; ideally we must unwind/fix the state and only then jump into enclave */ - greg_t func_args[2] = {-EINTR, event}; - pal_ucontext_set_function_parameters(uc, sgx_entry_return, /*func_args_num=*/2, func_args); + ucontext_set_function_parameters(uc, sgx_entry_return, -EINTR, event); } static void handle_dummy_signal(int signum, siginfo_t* info, struct ucontext* uc) { @@ -229,10 +226,6 @@ int sgx_signal_setup(void) { if (ret < 0) goto err; - ret = set_signal_handler(SIGINT, handle_async_signal); - if (ret < 0) - goto err; - ret = set_signal_handler(SIGCONT, handle_async_signal); if (ret < 0) goto err; diff --git a/Pal/src/host/Linux/db_exception.c b/Pal/src/host/Linux/db_exception.c index dc3cfee2..3b3900b2 100644 --- a/Pal/src/host/Linux/db_exception.c +++ b/Pal/src/host/Linux/db_exception.c @@ -41,7 +41,7 @@ __asm__( __attribute__((visibility("hidden"))) void __restore_rt(void); #endif /* defined(__x86_64__) */ -static const int ASYNC_SIGNALS[] = {SIGTERM, SIGINT, SIGCONT}; +static const int ASYNC_SIGNALS[] = {SIGTERM, SIGCONT}; static int block_signal(int sig, bool block) { int how = block ? SIG_BLOCK : SIG_UNBLOCK; @@ -93,34 +93,26 @@ static int get_pal_event(int sig) { return PAL_EVENT_ILLEGAL; case SIGTERM: return PAL_EVENT_QUIT; - case SIGINT: - return PAL_EVENT_SUSPEND; case SIGCONT: - return PAL_EVENT_RESUME; + return PAL_EVENT_INTERRUPTED; default: return -1; } } -static void perform_signal_handling(int event, siginfo_t* info, ucontext_t* uc) { +/* + * This function must be reentrant and thread-safe - this includes `upcall` too! Technically, + * only for cases when the exception arrived while in Graphene code; if signal arrived while in + * the user app, this function doesn't need to be reentrant and thread-safe. + */ +static void perform_signal_handling(int event, bool is_in_pal, PAL_NUM addr, ucontext_t* uc) { PAL_EVENT_HANDLER upcall = _DkGetExceptionHandler(event); if (!upcall) return; - PAL_NUM arg = 0; - if (info) { - switch (event) { - case PAL_EVENT_ARITHMETIC_ERROR: - case PAL_EVENT_MEMFAULT: - case PAL_EVENT_ILLEGAL: - arg = (PAL_NUM)info->si_addr; - break; - } - } - PAL_CONTEXT context; ucontext_to_pal_context(&context, uc); - (*upcall)(arg, &context); + (*upcall)(is_in_pal, addr, &context); pal_context_to_ucontext(uc, &context); } @@ -128,10 +120,10 @@ static void handle_sync_signal(int signum, siginfo_t* info, struct ucontext* uc) int event = get_pal_event(signum); assert(event > 0); - uintptr_t rip = pal_ucontext_get_ip(uc); + uintptr_t rip = ucontext_get_ip(uc); if (!ADDR_IN_PAL(rip)) { /* exception happened in application or LibOS code, normal benign case */ - perform_signal_handling(event, info, uc); + perform_signal_handling(event, /*is_in_pal=*/false, (PAL_NUM)info->si_addr, uc); return; } @@ -157,56 +149,21 @@ static void handle_sync_signal(int signum, siginfo_t* info, struct ucontext* uc) } static void handle_async_signal(int signum, siginfo_t* info, struct ucontext* uc) { + __UNUSED(info); + int event = get_pal_event(signum); assert(event > 0); - uintptr_t rip = pal_ucontext_get_ip(uc); - if (!ADDR_IN_PAL(rip)) { - /* signal arrived while in application or LibOS code, normal benign case */ - perform_signal_handling(event, info, uc); - return; - } - - /* signal arrived while in PAL code, add as pending for this thread; note that there is no race - * on pending_events as TCB is thread-local and async signals are blocked during signal - * handling */ - PAL_TCB_LINUX* tcb = get_tcb_linux(); - assert(tcb); - if (tcb->pending_events_num < MAX_SIGNAL_LOG) - tcb->pending_events[tcb->pending_events_num++] = event; -} - -void __check_pending_event(void) { - PAL_TCB_LINUX* tcb = get_tcb_linux(); - assert(tcb); - - if (!tcb->pending_events_num) - return; - - /* block signals while delivering pending events to avoid races on pending_events */ - int ret = block_async_signals(/*block=*/true); - if (IS_ERR(ret)) - return; - - /* LibOS signal handler may call PAL functions which in turn may call this function again, - * so we force TCB's pending_events_num to zero to avoid nesting of this function */ - int num = tcb->pending_events_num; - tcb->pending_events_num = 0; - - for (int i = 0; i < num; i++) { - PAL_EVENT_HANDLER upcall = _DkGetExceptionHandler(tcb->pending_events[i]); - if (upcall) { - (*upcall)(/*arg=*/0, /*context=*/NULL); - } - } - - (void)block_async_signals(/*block=*/false); + uintptr_t rip = ucontext_get_ip(uc); + perform_signal_handling(event, ADDR_IN_PAL(rip), /*addr=*/0, uc); } +/* TODO: remove this function. It's not an exception handling, it's just returning an error from + * PAL... */ void _DkRaiseFailure(int error) { PAL_EVENT_HANDLER upcall = _DkGetExceptionHandler(PAL_EVENT_FAILURE); if (upcall) { - (*upcall)(error, /*context=*/NULL); + (*upcall)(/*is_in_pal=*/false, error, /*context=*/NULL); } } @@ -244,17 +201,11 @@ void signal_setup(void) { goto err; /* register asynchronous signals in host Linux */ - ret = set_signal_handler(SIGTERM, handle_async_signal); - if (ret < 0) - goto err; - - ret = set_signal_handler(SIGINT, handle_async_signal); - if (ret < 0) - goto err; - - ret = set_signal_handler(SIGCONT, handle_async_signal); - if (ret < 0) - goto err; + for (size_t i = 0; i < ARRAY_SIZE(ASYNC_SIGNALS); i++) { + ret = set_signal_handler(ASYNC_SIGNALS[i], handle_async_signal); + if (ret < 0) + goto err; + } return; err: diff --git a/Pal/src/host/Linux/pal_host.h b/Pal/src/host/Linux/pal_host.h index 02d42a20..27b1bc06 100644 --- a/Pal/src/host/Linux/pal_host.h +++ b/Pal/src/host/Linux/pal_host.h @@ -154,16 +154,14 @@ typedef struct pal_handle { #define HANDLE_TYPE(handle) ((handle)->hdr.type) -extern void __check_pending_event(void); - +/* TODO: remove these + * Tracked: https://github.com/oscarlab/graphene/issues/2140 */ #define LEAVE_PAL_CALL() \ do { \ - __check_pending_event(); \ } while (0) #define LEAVE_PAL_CALL_RETURN(retval) \ do { \ - __check_pending_event(); \ return (retval); \ } while (0) diff --git a/Pal/src/host/Linux/pal_linux.h b/Pal/src/host/Linux/pal_linux.h index 77eed560..f16bafec 100644 --- a/Pal/src/host/Linux/pal_linux.h +++ b/Pal/src/host/Linux/pal_linux.h @@ -136,14 +136,10 @@ extern char __text_start, __text_end, __data_start, __data_end; #define ADDR_IN_PAL(addr) \ ((void*)(addr) > TEXT_START && (void*)(addr) < TEXT_END) -#define MAX_SIGNAL_LOG 32 - typedef struct pal_tcb_linux { PAL_TCB common; struct { /* private to Linux PAL */ - int pending_events[MAX_SIGNAL_LOG]; - int pending_events_num; PAL_HANDLE handle; void* alt_stack; int (*callback)(void*); diff --git a/Pal/src/host/Skeleton/db_exception.c b/Pal/src/host/Skeleton/db_exception.c index adb2d952..1d0be5f5 100644 --- a/Pal/src/host/Skeleton/db_exception.c +++ b/Pal/src/host/Skeleton/db_exception.c @@ -20,8 +20,7 @@ int (*_DkExceptionHandlers[PAL_EVENT_NUM_BOUND])(int, PAL_UPCALL, int) = { /* MemFault */ NULL, /* Illegal */ NULL, /* Quit */ NULL, - /* Suspend */ NULL, - /* Resume */ NULL, + /* Interrupt */ NULL, /* Failure */ NULL, }; diff --git a/Scripts/Makefile.rules b/Scripts/Makefile.rules index 88b06e44..78910cba 100644 --- a/Scripts/Makefile.rules +++ b/Scripts/Makefile.rules @@ -43,7 +43,7 @@ CLEAN_FILES += generated-offsets.s generated-offsets.s.d # Convert inline assembly into preprocessor directives. # The source line is something like: -# .ascii "GENERATED_INTEGER SHIM_TCB_OFFSET $8 " +# .ascii "GENERATED_INTEGER SHIM_TCB_OFF $8 " # (see the DEFINE macro Pal/include/pal/generated-offsets-build.h) # # Because of clang compatibility, we need to: