From e64816b0a24d57eb26297f492cf9d59c4fc72972 Mon Sep 17 00:00:00 2001 From: Petr Shumilov Date: Wed, 23 Oct 2024 20:24:06 +0300 Subject: [PATCH] Add user context manipulation routines for linux x86_64 Signed-off-by: Petr Shumilov --- CMakeLists.txt | 2 +- common/common.cmake | 11 +- common/dl-utils-lite.cpp | 8 ++ common/dl-utils-lite.h | 1 + common/server/crash-dump.cpp | 2 +- .../aarch64/context.cpp} | 23 ++-- common/ucontext/darwin/aarch64/context.h | 54 +++++++++ common/ucontext/darwin/x86_64/context.h | 25 ++++ common/ucontext/linux/aarch64/context.h | 26 ++++ common/ucontext/linux/x86_64/context.h | 113 ++++++++++++++++++ common/ucontext/linux/x86_64/defs.h | 101 ++++++++++++++++ common/ucontext/linux/x86_64/getcontext.S | 53 ++++++++ common/ucontext/linux/x86_64/makecontext.cpp | 77 ++++++++++++ common/ucontext/linux/x86_64/setcontext.S | 48 ++++++++ common/ucontext/linux/x86_64/startcontext.S | 30 +++++ common/ucontext/linux/x86_64/swapcontext.S | 86 +++++++++++++ common/ucontext/ucontext-arm.h | 37 ------ common/ucontext/ucontext-portable.h | 76 +++++++++--- server/json-logger.cpp | 16 +-- server/php-queries.cpp | 3 +- server/php-runner.cpp | 32 +++-- server/php-runner.h | 2 +- server/signal-handlers.cpp | 18 +-- server/signal-handlers.h | 2 +- 24 files changed, 739 insertions(+), 107 deletions(-) rename common/ucontext/{ucontext-arm.cpp => darwin/aarch64/context.cpp} (93%) create mode 100644 common/ucontext/darwin/aarch64/context.h create mode 100644 common/ucontext/darwin/x86_64/context.h create mode 100644 common/ucontext/linux/aarch64/context.h create mode 100644 common/ucontext/linux/x86_64/context.h create mode 100644 common/ucontext/linux/x86_64/defs.h create mode 100644 common/ucontext/linux/x86_64/getcontext.S create mode 100644 common/ucontext/linux/x86_64/makecontext.cpp create mode 100644 common/ucontext/linux/x86_64/setcontext.S create mode 100644 common/ucontext/linux/x86_64/startcontext.S create mode 100644 common/ucontext/linux/x86_64/swapcontext.S delete mode 100644 common/ucontext/ucontext-arm.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a44c5e272..560450ebef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.16) project(kphp VERSION 1.0.1 - LANGUAGES CXX + LANGUAGES CXX ASM DESCRIPTION "Compiler for PHP (aka KPHP)" HOMEPAGE_URL https://github.com/VKCOM/kphp) diff --git a/common/common.cmake b/common/common.cmake index 03d167f231..4553af279e 100644 --- a/common/common.cmake +++ b/common/common.cmake @@ -35,9 +35,16 @@ prepend(COMMON_TL_METHODS_SOURCES ${COMMON_DIR}/tl/methods/ rwm.cpp string.cpp) -if (NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") +if (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64") prepend(COMMON_UCONTEXT_SOURCES ${COMMON_DIR}/ucontext/ - ucontext-arm.cpp) + linux/x86_64/startcontext.S + linux/x86_64/getcontext.S + linux/x86_64/setcontext.S + linux/x86_64/swapcontext.S + linux/x86_64/makecontext.cpp) +else() + prepend(COMMON_UCONTEXT_SOURCES ${COMMON_DIR}/ucontext/ + darwin/aarch64/ucontext-arm.cpp) endif() set(COMMON_ALL_SOURCES diff --git a/common/dl-utils-lite.cpp b/common/dl-utils-lite.cpp index 8648274d6c..0e5acbf3af 100644 --- a/common/dl-utils-lite.cpp +++ b/common/dl-utils-lite.cpp @@ -178,6 +178,14 @@ void dl_allow_all_signals () { dl_passert (err != -1, "failed to allow all signals"); } +void dl_unblock_signal (int sig) { + sigset_t mask; + sigemptyset(&mask); + sigaddset(&mask, sig); + int err = sigprocmask(SIG_UNBLOCK, &mask, nullptr); + dl_passert (err != -1, "failed to unblock signal"); +} + static void runtime_handler (const int sig, siginfo_t *info __attribute__((unused)), void *ucontext) { fprintf (stderr, "%s caught, terminating program\n", strsignal(sig)); crash_dump_write(static_cast(ucontext)); diff --git a/common/dl-utils-lite.h b/common/dl-utils-lite.h index d9edcc9c32..b964e4acb1 100644 --- a/common/dl-utils-lite.h +++ b/common/dl-utils-lite.h @@ -23,6 +23,7 @@ void dl_signal (int sig, void (*handler) (int)); void dl_restore_signal_mask (); void dl_block_all_signals (); void dl_allow_all_signals (); +void dl_unblock_signal (int sig); void dl_print_backtrace(void **trace, int trace_size); void dl_print_backtrace(); diff --git a/common/server/crash-dump.cpp b/common/server/crash-dump.cpp index e16943016e..318ebfd1e8 100644 --- a/common/server/crash-dump.cpp +++ b/common/server/crash-dump.cpp @@ -11,7 +11,7 @@ #include #include "common/kprintf.h" -#include "common/ucontext/ucontext-portable.h" +#include struct crash_dump_buffer { char scratchpad[1024]; diff --git a/common/ucontext/ucontext-arm.cpp b/common/ucontext/darwin/aarch64/context.cpp similarity index 93% rename from common/ucontext/ucontext-arm.cpp rename to common/ucontext/darwin/aarch64/context.cpp index b5ffb8781d..13c6233bb5 100644 --- a/common/ucontext/ucontext-arm.cpp +++ b/common/ucontext/darwin/aarch64/context.cpp @@ -3,11 +3,10 @@ // Copyright (c) 2023 LLC «V Kontakte» // Distributed under the GPL v3 License, see LICENSE.notice.txt -#include "common/ucontext/ucontext-arm.h" +#include "context.h" #include #include -#include #include enum { SP_OFFSET = 432, PC_OFFSET = 440, PSTATE_OFFSET = 448, FPSIMD_CONTEXT_OFFSET = 464 }; @@ -31,6 +30,8 @@ static_assert(offsetof(libucontext_ucontext, uc_mcontext.sp) == SP_OFFSET, "SP_O static_assert(offsetof(libucontext_ucontext, uc_mcontext.pc) == PC_OFFSET, "PC_OFFSET is invalid"); static_assert(offsetof(libucontext_ucontext, uc_mcontext.pstate) == PSTATE_OFFSET, "PSTATE_OFFSET is invalid"); +extern "C" int libucontext_setcontext(const libucontext_ucontext *); + __attribute__((visibility("hidden"))) void libucontext_trampoline() { libucontext_ucontext *uc_link = nullptr; @@ -43,7 +44,7 @@ __attribute__((visibility("hidden"))) void libucontext_trampoline() { libucontext_setcontext(uc_link); } -void libucontext_makecontext(libucontext_ucontext *ucp, void (*func)(), int argc, ...) { +void portable_makecontext(libucontext_ucontext *ucp, void (*func)(), int argc, ...) { unsigned long *sp; unsigned long *regp; va_list va; @@ -73,9 +74,9 @@ void libucontext_makecontext(libucontext_ucontext *ucp, void (*func)(), int argc va_end(va); } -asm(".global " NAME(libucontext_getcontext) ";\n" +asm(".global " NAME(portable_getcontext) ";\n" ".align 2;\n" - NAME(libucontext_getcontext) ":\n" + NAME(portable_getcontext) ":\n" "str xzr, [x0, #((184) + ((0) * (8)))]\n" // #REG_OFFSET(0) /* save GPRs */ "stp x0, x1, [x0, #((184) + ((0) * (8)))]\n" // REG_OFFSET(0) @@ -109,9 +110,9 @@ asm(".global " NAME(libucontext_getcontext) ";\n" "mov x0, #0\n" "ret\n"); -asm(".global " NAME(libucontext_setcontext) ";\n" +asm(".global " NAME(portable_setcontext) ";\n" ".align 2;\n" - NAME(libucontext_setcontext) ":\n" + NAME(portable_setcontext) ":\n" /* restore GPRs */ "ldp x18, x19, [x0, #((184) + ((18) * (8)))]\n" // REG_OFFSET(18) "ldp x20, x21, [x0, #((184) + ((20) * (8)))]\n" // REG_OFFSET(20) @@ -138,9 +139,9 @@ asm(".global " NAME(libucontext_setcontext) ";\n" /* jump to new PC */ "br x16\n"); -asm(".global " NAME(libucontext_swapcontext) ";\n" +asm(".global " NAME(portable_swapcontext) ";\n" ".align 2;\n" - NAME(libucontext_swapcontext) ":\n" + NAME(portable_swapcontext) ":\n" "str xzr, [x0, #((184) + ((0) * (8)))]\n" // REG_OFFSET(0) /* save GPRs */ "stp x2, x3, [x0, #((184) + ((2) * (8)))]\n" // REG_OFFSET(2) @@ -175,7 +176,7 @@ asm(".global " NAME(libucontext_swapcontext) ";\n" "mov x28, x30\n" /* move x1 to x0 and call setcontext */ "mov x0, x1\n" - "bl " NAME(libucontext_setcontext) "\n" + "bl " NAME(portable_setcontext) "\n" /* hmm, we came back here try to return */ "mov x30, x28\n" - "ret\n"); + "ret\n"); \ No newline at end of file diff --git a/common/ucontext/darwin/aarch64/context.h b/common/ucontext/darwin/aarch64/context.h new file mode 100644 index 0000000000..b12341c1cd --- /dev/null +++ b/common/ucontext/darwin/aarch64/context.h @@ -0,0 +1,54 @@ +// Compiler for PHP (aka KPHP) +// Copyright (c) 2018-2022 Ariadne Conill +// https://github.com/kaniini/libucontext/tree/master (copied as third-party and slightly modified) +// Copyright (c) 2023 LLC «V Kontakte» +// Distributed under the GPL v3 License, see LICENSE.notice.txt + +#pragma once + +#include + +struct libucontext_mcontext { + unsigned long fault_address; + unsigned long regs[29]; + unsigned long fp, lr, sp, pc, pstate; + long double __reserved[256]; +}; + +struct libucontext_stack { + void *ss_sp; + int ss_flags; + size_t ss_size; +}; + +struct libucontext_ucontext { + unsigned long uc_flags; + struct libucontext_ucontext *uc_link; + libucontext_stack uc_stack; + unsigned char __pad[136]; + libucontext_mcontext uc_mcontext; +}; + +inline constexpr void *get_context_stack_ptr_portable(const libucontext_ucontext &ctx) noexcept { + return ctx.uc_stack.ss_sp; +} + +inline constexpr size_t get_context_stack_size_portable(const libucontext_ucontext &ctx) noexcept { + return ctx.uc_stack.ss_size; +} + +inline constexpr void set_context_stack_ptr_portable(libucontext_ucontext &ctx, void *sp) noexcept { + ctx.uc_stack.ss_sp = sp; +} + +inline constexpr void set_context_stack_size_portable(libucontext_ucontext &ctx, size_t size) noexcept { + ctx.uc_stack.ss_size = size; +} + +inline constexpr void set_context_link_portable(libucontext_ucontext &ctx, libucontext_ucontext *link) noexcept { + ctx.uc_link = link; +} + +inline void *get_context_stack_base_ptr_portable(const libucontext_ucontext &ctx) noexcept { + return reinterpret_cast(ctx.uc_mcontext.fp); +} \ No newline at end of file diff --git a/common/ucontext/darwin/x86_64/context.h b/common/ucontext/darwin/x86_64/context.h new file mode 100644 index 0000000000..3f771e74a9 --- /dev/null +++ b/common/ucontext/darwin/x86_64/context.h @@ -0,0 +1,25 @@ +#include + +inline constexpr void *get_context_stack_ptr_portable(const ucontext_t &ctx) noexcept { + return ctx.uc_stack.ss_sp; +} + +inline constexpr size_t get_context_stack_size_portable(const ucontext_t &ctx) noexcept { + return ctx.uc_stack.ss_size; +} + +inline constexpr void set_context_stack_ptr_portable(ucontext_t &ctx, void *sp) noexcept { + ctx.uc_stack.ss_sp = sp; +} + +inline constexpr void set_context_stack_size_portable(ucontext_t &ctx, size_t size) noexcept { + ctx.uc_stack.ss_size = size; +} + +inline constexpr void set_context_link_portable(ucontext_t &ctx, ucontext_t *link) noexcept { + ctx.uc_link = link; +} + +inline void *get_context_stack_base_ptr_portable(const ucontext_t &ctx) noexcept { + return reinterpret_cast(ctx.uc_mcontext->__ss.__rbp); +} \ No newline at end of file diff --git a/common/ucontext/linux/aarch64/context.h b/common/ucontext/linux/aarch64/context.h new file mode 100644 index 0000000000..71b290cefa --- /dev/null +++ b/common/ucontext/linux/aarch64/context.h @@ -0,0 +1,26 @@ +#include + +inline constexpr void *get_context_stack_ptr_portable(const ucontext_t &ctx) noexcept { + return ctx.uc_stack.ss_sp; +} + +inline constexpr size_t get_context_stack_size_portable(const ucontext_t &ctx) noexcept { + return ctx.uc_stack.ss_size; +} + +inline constexpr void set_context_stack_ptr_portable(ucontext_t &ctx, void *sp) noexcept { + ctx.uc_stack.ss_sp = sp; +} + +inline constexpr void set_context_stack_size_portable(ucontext_t &ctx, size_t size) noexcept { + ctx.uc_stack.ss_size = size; +} + +inline constexpr void set_context_link_portable(ucontext_t &ctx, ucontext_t *link) noexcept { + ctx.uc_link = link; +} + +inline void *get_context_stack_base_ptr_portable(const ucontext_t &ctx) noexcept { + // 29 -- magic constant, based on libc implementation from `sysdeps/unix/sysv/linux/aarch64/ucontext-internal.h` + return reinterpret_cast(ctx.uc_mcontext.regs[29]); +} \ No newline at end of file diff --git a/common/ucontext/linux/x86_64/context.h b/common/ucontext/linux/x86_64/context.h new file mode 100644 index 0000000000..7801b76f2d --- /dev/null +++ b/common/ucontext/linux/x86_64/context.h @@ -0,0 +1,113 @@ +#include +#include +#include "defs.h" + +#pragma once + +// Type for general register +using kgreg_t = uint64_t; + +// Container for all general registers +using kgregset_t = kgreg_t[NUM_GENERAL_REG]; + +// Prohibit packing +#pragma pack(push, 1) + +struct kfpxreg { + uint16_t significand[4]; + uint16_t exponent; + uint16_t reserved1[3]; +}; + +struct kxmmreg { + uint32_t element[4]; +}; + +struct kfpstate { + uint16_t cwd; + uint16_t swd; + uint16_t ftw; + uint16_t fop; + uint16_t rip; + uint16_t rdp; + uint16_t mxcsr; + uint16_t mxcr_mask; + kfpxreg st[8]; + kxmmreg xmm[16]; + uint32_t reserved1[24]; +}; + +// Structure to describe FPU registers +using kfpregset_t = kfpstate *; + +// Context to describe whole processor state +struct kmcontext_t { + kgregset_t gregs; + // Note that fpregs is a pointer + kfpregset_t fpregs; + uint64_t reserved1[8]; +}; + +struct kstack_t { + void *sp; + uint64_t size; +}; + +// Userlevel KPHP context +struct kcontext_t { + uint64_t flags; + kcontext_t *link; + kstack_t stack; + kmcontext_t mcontext; + kfpstate fpregs_mem; +}; + +#pragma pack(pop) + +inline constexpr void *get_context_stack_ptr_portable(const kcontext_t &ctx) noexcept { + return ctx.stack.sp; +} + +inline constexpr uint64_t get_context_stack_size_portable(const kcontext_t &ctx) noexcept { + return ctx.stack.size; +} + +inline constexpr void set_context_stack_ptr_portable(kcontext_t &ctx, void *sp) noexcept { + ctx.stack.sp = sp; +} + +inline constexpr void set_context_stack_size_portable(kcontext_t &ctx, uint64_t size) noexcept { + ctx.stack.size = size; +} + +inline constexpr void set_context_link_portable(kcontext_t &ctx, kcontext_t *link) noexcept { + ctx.link = link; +} + +inline void *get_context_stack_base_ptr_portable(const kcontext_t &ctx) noexcept { + return reinterpret_cast(ctx.mcontext.gregs[GREG_RBP]); +} + +// Zero-cost offsets double-checking +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RBP]) == oRBP, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RSP]) == oRSP, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RBX]) == oRBX, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R8]) == oR8, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R9]) == oR9, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R10]) == oR10, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R11]) == oR11, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R12]) == oR12, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R13]) == oR13, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R14]) == oR14, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_R15]) == oR15, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RDI]) == oRDI, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RSI]) == oRSI, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RDX]) == oRDX, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RAX]) == oRAX, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RCX]) == oRCX, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_RIP]) == oRIP, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs[GREG_EFL]) == oEFL, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.gregs) == KCONTEXT_MCONTEXT_GREGS, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, mcontext.fpregs) == KCONTEXT_MCONTEXT_FPREGS, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, fpregs_mem) == KCONTEXT_FPREGS_MEM, "Invalid offset assumption"); +static_assert(offsetof(kcontext_t, fpregs_mem.mxcsr) == KCONTEXT_FPREGS_MEM_MXCSR, "Invalid offset assumption"); diff --git a/common/ucontext/linux/x86_64/defs.h b/common/ucontext/linux/x86_64/defs.h new file mode 100644 index 0000000000..ec0c0449c5 --- /dev/null +++ b/common/ucontext/linux/x86_64/defs.h @@ -0,0 +1,101 @@ +#ifndef TYPE +# ifdef __clang__ +# define TYPE(name) // .type not supported +# else +# define TYPE(name) .type name, @function; +# endif +#endif + +#define ALIGNARG(log2) 1 << log2 + +// Define an entry point visible from C/C++. +#define ENTRY_P2ALIGN(name, alignment) \ + .globl name; \ + TYPE(name) \ + .align ALIGNARG(alignment); \ + name: + +// Common entry 16 byte aligns +#define ENTRY(name) ENTRY_P2ALIGN(name, 4) +#define END(name) .size name, .- name; + +// Number of general registers +#define NUM_GENERAL_REG 23 + +// Register size +#define GREG_SIZE 8 + +// General registers +#define GREG_R8 0 +#define GREG_R9 1 +#define GREG_R10 2 +#define GREG_R11 3 +#define GREG_R12 4 +#define GREG_R13 5 +#define GREG_R14 6 +#define GREG_R15 7 +#define GREG_RDI 8 +#define GREG_RSI 9 +#define GREG_RBP 10 +#define GREG_RBX 11 +#define GREG_RDX 12 +#define GREG_RAX 13 +#define GREG_RCX 14 +#define GREG_RSP 15 +#define GREG_RIP 16 +#define GREG_EFL 17 +#define GREG_CSGSFS 18 +#define GREG_ERR 19 +#define GREG_TRAPNO 20 +#define GREG_OLDMASK 21 +#define GREG_CR2 22 + +// KPHP user context layout + +/* struct kcontext_t { OFFSET*/ +/* uint64_t flags 0 */ +/* kcontext_t* link 8 */ +/* kstack_t stack 16 */ +/* struct kmcontext_t mcontext { */ +/* kgregset_t gregs 32 */ #define KCONTEXT_MCONTEXT_GREGS (32) +/* kfpregset_t fpregs 216 */ #define KCONTEXT_MCONTEXT_FPREGS (KCONTEXT_MCONTEXT_GREGS + (GREG_SIZE * NUM_GENERAL_REG)) +/* uint64_t reserved1[8] 224 */ +/* } */ +/* struct kfpstate fpregs_mem { */ +/* uint16_t cwd 288 */ #define KCONTEXT_FPREGS_MEM (KCONTEXT_MCONTEXT_FPREGS + 8 + (8 * 8)) +/* uint16_t swd 290 */ +/* uint16_t ftw 292 */ +/* uint16_t fop 294 */ +/* uint16_t rip 296 */ +/* uint16_t rdp 298 */ +/* uint16_t mxcsr 300 */ #define KCONTEXT_FPREGS_MEM_MXCSR (KCONTEXT_FPREGS_MEM + (2 * 6)) +/* uint16_t mxcr_mask */ +/* kfpxreg st[8] */ +/* kxmmreg xmm[16] */ +/* uint32_t reserved1[24] */ +/* } */ +/* } */ + +// Offset of register inside context +#define KCONTEXT_MCONTEXT_GREG_OFFSET(reg) (KCONTEXT_MCONTEXT_GREGS + ((reg) * GREG_SIZE)) +#define oRBP KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RBP) +#define oRSP KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RSP) +#define oRBX KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RBX) +#define oR8 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R8) +#define oR9 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R9) +#define oR10 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R10) +#define oR11 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R11) +#define oR12 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R12) +#define oR13 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R13) +#define oR14 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R14) +#define oR15 KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_R15) +#define oRDI KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RDI) +#define oRSI KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RSI) +#define oRDX KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RDX) +#define oRAX KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RAX) +#define oRCX KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RCX) +#define oRIP KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_RIP) +#define oEFL KCONTEXT_MCONTEXT_GREG_OFFSET(GREG_EFL) +#define oFPREGS KCONTEXT_MCONTEXT_FPREGS +#define oFPREGSMEM KCONTEXT_FPREGS_MEM +#define oMXCSR KCONTEXT_FPREGS_MEM_MXCSR \ No newline at end of file diff --git a/common/ucontext/linux/x86_64/getcontext.S b/common/ucontext/linux/x86_64/getcontext.S new file mode 100644 index 0000000000..1baed5248e --- /dev/null +++ b/common/ucontext/linux/x86_64/getcontext.S @@ -0,0 +1,53 @@ +#include "defs.h" + +/* + * int getcontext_portable(kcontext_t *kcp) + + Stores a user context in the same manner as in libc but without signals state storing. + Main goal of getting rid of manipulation with signals state -- remove syscall and performance gain. + IMPORTANT! USER HAVE TO MANUALLY CONTROL SIGNALS STATE! + + In any case returns 0 +*/ + +ENTRY(getcontext_portable) + // Save the registers, the registers used for passing args, and the return address + movq %rbx, oRBX(%rdi) + movq %rbp, oRBP(%rdi) + movq %r12, oR12(%rdi) + movq %r13, oR13(%rdi) + movq %r14, oR14(%rdi) + movq %r15, oR15(%rdi) + movq %rdi, oRDI(%rdi) + movq %rsi, oRSI(%rdi) + movq %rdx, oRDX(%rdi) + movq %rcx, oRCX(%rdi) + movq %r8, oR8(%rdi) + movq %r9, oR9(%rdi) + /* + %rsp -> +-----------------------+ / \ + | Return address | | + 8(%rsp) -> +-----------------------+ | + | Caller local var #N | | + +-----------------------+ | + | ... | | + +-----------------------+ 0xFFFFFFFF + */ + movq (%rsp), %rcx + movq %rcx, oRIP(%rdi) + leaq 8(%rsp), %rcx + movq %rcx, oRSP(%rdi) + + // We have separate floating-point register content memory on the + // stack. We use the fpregs_mem block in the context + leaq oFPREGSMEM(%rdi), %rcx + movq %rcx, oFPREGS(%rdi) + // Save the floating-point environment + fnstenv (%rcx) + fldenv (%rcx) + stmxcsr oMXCSR(%rdi) + + // All done, return 0 for success + xorl %eax, %eax + ret +END(getcontext_portable) \ No newline at end of file diff --git a/common/ucontext/linux/x86_64/makecontext.cpp b/common/ucontext/linux/x86_64/makecontext.cpp new file mode 100644 index 0000000000..3f86820a92 --- /dev/null +++ b/common/ucontext/linux/x86_64/makecontext.cpp @@ -0,0 +1,77 @@ +#include "context.h" +#include +#include + +/* This implementation can handle any ARGC value but only + normal integer parameters. + makecontext sets up a stack and the registers for the + user context. The stack looks like this: + + %rsp -> +-----------------------+ + | trampoline address | / \ + +-----------------------+ | + | parameter 7-n | | + +-----------------------+ | + | next context | | + +-----------------------+ stack.sp + stack.size + + The registers are set up like this: + %rdi,%rsi,%rdx,%rcx,%r8,%r9: parameter 1 to 6 + %rbx : address of next context + %rsp : stack pointer. +*/ + +extern "C" void start_context_portable(void) __attribute__((visibility("hidden"))); + +extern "C" void makecontext_portable(kcontext_t *kcp, void (*func)(void), int argc, ...) { + kgreg_t *sp; + va_list ap; + uint32_t argc_on_stack = (argc > 6 ? argc - 6 : 0); // Prev 6 args passed via regs + uint32_t link = argc_on_stack + 1; + int i; + + // Generate room on stack for parameter if needed and link + sp = reinterpret_cast(reinterpret_cast(kcp->stack.sp) + kcp->stack.size); + sp -= argc_on_stack + 1; + // Align stack and make space for trampoline address + sp = reinterpret_cast((reinterpret_cast(sp) & -16L) - 8); + + // Address to jump to context entrypoint + kcp->mcontext.gregs[GREG_RIP] = reinterpret_cast(func); + // Setup rbx + kcp->mcontext.gregs[GREG_RBX] = reinterpret_cast(&sp[link]); + kcp->mcontext.gregs[GREG_RSP] = reinterpret_cast(sp); + + /* Setup stack. */ + sp[0] = reinterpret_cast(&start_context_portable); + sp[link] = reinterpret_cast(kcp->link); + + va_start(ap, argc); + /* Handle arguments. */ + for (i = 0; i < argc; ++i) + switch (i) { + case 0: + kcp->mcontext.gregs[GREG_RDI] = va_arg(ap, kgreg_t); + break; + case 1: + kcp->mcontext.gregs[GREG_RSI] = va_arg(ap, kgreg_t); + break; + case 2: + kcp->mcontext.gregs[GREG_RDX] = va_arg(ap, kgreg_t); + break; + case 3: + kcp->mcontext.gregs[GREG_RCX] = va_arg(ap, kgreg_t); + break; + case 4: + kcp->mcontext.gregs[GREG_R8] = va_arg(ap, kgreg_t); + break; + case 5: + kcp->mcontext.gregs[GREG_R9] = va_arg(ap, kgreg_t); + break; + default: + /* Put value on stack. */ + sp[i - 5] = va_arg(ap, kgreg_t); + break; + } + va_end(ap); +} \ No newline at end of file diff --git a/common/ucontext/linux/x86_64/setcontext.S b/common/ucontext/linux/x86_64/setcontext.S new file mode 100644 index 0000000000..975ecd9553 --- /dev/null +++ b/common/ucontext/linux/x86_64/setcontext.S @@ -0,0 +1,48 @@ +#include "defs.h" + +/* + * int setcontext_portable(kcontext_t *kcp) + + Restores a user context in the same manner as in libc but without signals state restoring. + Main goal of getting rid of manipulation with signals state -- remove syscall and performance gain. + IMPORTANT! USER HAVE TO MANUALLY CONTROL SIGNALS STATE! + + In any case returns 0 +*/ + +ENTRY(setcontext_portable) + // Move the pointer into RDX. The choice is arbitrary, but + // leaving RDI and RSI available for use later can avoid + // shuffling values + movq %rdi, %rdx + + // Restore the floating-point context + movq oFPREGS(%rdx), %rcx + fldenv (%rcx) + ldmxcsr oMXCSR(%rdx) + + // Load the new stack pointer, the preserved registers and registers used for passing args + movq oRSP(%rdx), %rsp + movq oRBX(%rdx), %rbx + movq oRBP(%rdx), %rbp + movq oR12(%rdx), %r12 + movq oR13(%rdx), %r13 + movq oR14(%rdx), %r14 + movq oR15(%rdx), %r15 + + // The following ret should return to the address set with getcontext. Therefore, push the address on the stack + movq oRIP(%rdx), %rcx + pushq %rcx + + movq oRSI(%rdx), %rsi + movq oRDI(%rdx), %rdi + movq oRCX(%rdx), %rcx + movq oR8(%rdx), %r8 + movq oR9(%rdx), %r9 + movq oRDX(%rdx), %rdx + + // All done, return 0 for success + xorl %eax, %eax + ret +END(setcontext_portable) + diff --git a/common/ucontext/linux/x86_64/startcontext.S b/common/ucontext/linux/x86_64/startcontext.S new file mode 100644 index 0000000000..01db662596 --- /dev/null +++ b/common/ucontext/linux/x86_64/startcontext.S @@ -0,0 +1,30 @@ +#include "defs.h" + +/* + * void start_context_portable() + The trampoline for a `next` context which is located in `link` of kcontext_t. + It is placed into user-created stack by `makecontext`. + + %rsp -> +---------------------------+ + | &start_context_portable | / \ + +---------------------------+ | + | entrypoint parameter 7-n | | + %rbx -> +---------------------------+ | + | next context | | + +---------------------------+ stack.sp + stack.size +*/ + +ENTRY(start_context_portable) + // This removes the parameters passed to the function given to + // 'makecontext' from the stack. RBX contains the address + // on the stack pointer for the next context. */ + movq %rbx, %rsp + + // Don't use pop here so that stack is aligned to 16 bytes + movq (%rsp), %rdi // This is the next context + testq %rdi, %rdi + je 2f // If it is null then exit + call setcontext_portable +2: + call exit +END(start_context_portable) diff --git a/common/ucontext/linux/x86_64/swapcontext.S b/common/ucontext/linux/x86_64/swapcontext.S new file mode 100644 index 0000000000..633bb767dd --- /dev/null +++ b/common/ucontext/linux/x86_64/swapcontext.S @@ -0,0 +1,86 @@ +#include "defs.h" + +/* + * int swapcontext_portable(kcontext_t *out_kcp, kcontext_t *kcp) + + Stores a user context into `out_kcp` and restores context from `kcp` in the same manner as in libc. + But without signals state storing/restoring. + Main goal of getting rid of manipulation with signals state -- remove syscall and performance gain. + IMPORTANT! USER HAVE TO MANUALLY CONTROL SIGNALS STATE! + + In any case returns 0 +*/ + +ENTRY(swapcontext_portable) + // Save the preserved registers, the registers used for passing args, and the return address + movq %rbx, oRBX(%rdi) + movq %rbp, oRBP(%rdi) + movq %r12, oR12(%rdi) + movq %r13, oR13(%rdi) + movq %r14, oR14(%rdi) + movq %r15, oR15(%rdi) + movq %rdi, oRDI(%rdi) + movq %rsi, oRSI(%rdi) + movq %rdx, oRDX(%rdi) + movq %rcx, oRCX(%rdi) + movq %r8, oR8(%rdi) + movq %r9, oR9(%rdi) + /* + %rsp -> +-----------------------+ / \ + | Return address | | + 8(%rsp) -> +-----------------------+ | + | Caller local var #N | | + +-----------------------+ | + | ... | | + +-----------------------+ 0xFFFFFFFF + */ + movq (%rsp), %rcx + movq %rcx, oRIP(%rdi) + leaq 8(%rsp), %rcx + movq %rcx, oRSP(%rdi) + + // We have separate floating-point register content memory on the + // stack. We use the fpregs_mem block in the context + leaq oFPREGSMEM(%rdi), %rcx + movq %rcx, oFPREGS(%rdi) + // Save the floating-point environment + fnstenv (%rcx) + stmxcsr oMXCSR(%rdi) + + // Move restored ctx pointer to %rdx + movq %rsi, %rdx + + // Restore the floating-point context + movq oFPREGS(%rdx), %rcx + fldenv (%rcx) + ldmxcsr oMXCSR(%rdx) + + // Load the new stack pointer and the preserved registers + movq oRSP(%rdx), %rsp + movq oRBX(%rdx), %rbx + movq oRBP(%rdx), %rbp + movq oR12(%rdx), %r12 + movq oR13(%rdx), %r13 + movq oR14(%rdx), %r14 + movq oR15(%rdx), %r15 + + + // The following ret should return to the address set with + // getcontext. Therefore. push the address on the stack + movq oRIP(%rdx), %rcx + pushq %rcx + + // Setup registers used for passing args + movq oRDI(%rdx), %rdi + movq oRSI(%rdx), %rsi + movq oRCX(%rdx), %rcx + movq oR8(%rdx), %r8 + movq oR9(%rdx), %r9 + movq oRDX(%rdx), %rdx + + // Clear rax to indicate success + xorl %eax, %eax + ret +END(swapcontext_portable) + + diff --git a/common/ucontext/ucontext-arm.h b/common/ucontext/ucontext-arm.h deleted file mode 100644 index fae5b18da6..0000000000 --- a/common/ucontext/ucontext-arm.h +++ /dev/null @@ -1,37 +0,0 @@ -// Compiler for PHP (aka KPHP) -// Copyright (c) 2018-2022 Ariadne Conill -// https://github.com/kaniini/libucontext/tree/master (copied as third-party and slightly modified) -// Copyright (c) 2023 LLC «V Kontakte» -// Distributed under the GPL v3 License, see LICENSE.notice.txt - -#pragma once - -#include - -struct libucontext_mcontext { - unsigned long fault_address; - unsigned long regs[29]; - unsigned long fp, lr, sp, pc, pstate; - long double __reserved[256]; -}; - -struct libucontext_stack { - void *ss_sp; - int ss_flags; - size_t ss_size; -}; - -struct libucontext_ucontext { - unsigned long uc_flags; - struct libucontext_ucontext *uc_link; - libucontext_stack uc_stack; - unsigned char __pad[136]; - libucontext_mcontext uc_mcontext; -}; - -extern "C" { -void libucontext_makecontext(libucontext_ucontext *, void (*)(), int, ...); -int libucontext_getcontext(libucontext_ucontext *); -int libucontext_setcontext(const libucontext_ucontext *); -int libucontext_swapcontext(libucontext_ucontext *, const libucontext_ucontext *); -} diff --git a/common/ucontext/ucontext-portable.h b/common/ucontext/ucontext-portable.h index 864c0f1cee..a5b3c4ac80 100644 --- a/common/ucontext/ucontext-portable.h +++ b/common/ucontext/ucontext-portable.h @@ -1,27 +1,65 @@ // Compiler for PHP (aka KPHP) // Copyright (c) 2021 LLC «V Kontakte» // Distributed under the GPL v3 License, see LICENSE.notice.txt - #pragma once +#include + #ifdef __x86_64__ -// for x86 mac or x86/arm linux, we just use makecontext(), ucontext_t and other native functions -#include - -#define ucontext_t_portable ucontext_t -#define setcontext_portable setcontext -#define getcontext_portable getcontext -#define makecontext_portable makecontext -#define swapcontext_portable swapcontext - -#else -// for M1, we can't use native makecontext() and others: they compile, but hang up when called -#include "common/ucontext/ucontext-arm.h" - -#define ucontext_t_portable libucontext_ucontext -#define setcontext_portable libucontext_setcontext -#define getcontext_portable libucontext_getcontext -#define makecontext_portable libucontext_makecontext -#define swapcontext_portable libucontext_swapcontext +# ifdef __linux__ +/* + * + * For X86_64 Linux platform we use own implementation of user context manipulation routines in the same manner as in libc. + * But without signals state saving/restoring. + * IMPORTANT! USER HAVE TO MANUALLY CONTROL SIGNALS STATE! + * + */ +# include "linux/x86_64/context.h" + using ucontext_t_portable = kcontext_t; + + extern "C" { + int getcontext_portable(ucontext_t_portable *); + void makecontext_portable(ucontext_t_portable *, void (*)(), int, ...); + int setcontext_portable(const ucontext_t_portable *); + int swapcontext_portable(ucontext_t_portable *, const ucontext_t_portable *); + } + +# elif __APPLE__ +# include "darwin/x86_64/context.h" + using ucontext_t_portable = ucontext_t; + +# define getcontext_portable getcontext +# define makecontext_portable makecontext +# define setcontext_portable setcontext +# define swapcontext_portable swapcontext + +# else + static_assert(false, "Unsupported OS for x86_64 platform"); +# endif + +#elif defined(__aarch64__) || defined(__arm64__) + +# ifdef __linux__ +# include "linux/aarch64/context.h" + using ucontext_t_portable = ucontext_t; + +# define getcontext_portable getcontext +# define makecontext_portable makecontext +# define setcontext_portable setcontext +# define swapcontext_portable swapcontext + +# elif __APPLE__ +// For M1, we can't use native makecontext() and others: they compile, but hang up when called +# include "darwin/aarch64/context.h" + using ucontext_t_portable = libucontext_ucontext; + + int getcontext_portable(ucontext_t_portable *); + void makecontext_portable(ucontext_t_portable *, void (*)(), int, ...); + int setcontext_portable(const ucontext_t_portable *); + int swapcontext_portable(ucontext_t_portable *, const ucontext_t_portable *); + +# else + static_assert(false, "Unsupported OS for aarch64 platform"); +# endif #endif diff --git a/server/json-logger.cpp b/server/json-logger.cpp index d5e7614154..f0a6a6c935 100644 --- a/server/json-logger.cpp +++ b/server/json-logger.cpp @@ -11,6 +11,7 @@ #include "common/algorithms/find.h" #include "common/fast-backtrace.h" #include "common/wrappers/likely.h" +#include "common/ucontext/ucontext-portable.h" #include "runtime/kphp-backtrace.h" #include "server/server-config.h" #include "server/json-logger.h" @@ -81,20 +82,7 @@ int script_backtrace(void **buffer, int size) { return 0; } const ucontext_t_portable &context = PhpScript::current_script->run_context; -#if defined(__APPLE__) -#if defined(__arm64__) - void *rbp = reinterpret_cast(context.uc_mcontext.fp); -#else - void *rbp = reinterpret_cast(context.uc_mcontext->__ss.__rbp); -#endif -#elif defined(__x86_64__) - void *rbp = reinterpret_cast(context.uc_mcontext.gregs[REG_RBP]); -#elif defined(__aarch64__) || defined(__arm64__) - void *rbp = reinterpret_cast(context.uc_mcontext.fp); -#else - void *rbp = nullptr; - size = 0; -#endif + void *rbp = get_context_stack_base_ptr_portable(context); char *stack_start = PhpScript::current_script->script_stack.get_stack_ptr(); char *stack_end = stack_start + PhpScript::current_script->script_stack.get_stack_size(); return fast_backtrace_by_bp(rbp, stack_end, buffer, size); diff --git a/server/php-queries.cpp b/server/php-queries.cpp index ad3d24d95d..bd18efeaf2 100644 --- a/server/php-queries.cpp +++ b/server/php-queries.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "common/precise-time.h" @@ -1012,7 +1013,7 @@ php_net_query_packet_answer_t *php_net_query_get(int connection_id, const char * } void script_error() { - PhpScript::error("script_error called", script_error_t::unclassified_error); + PhpScript::error("script_error called", script_error_t::unclassified_error, /* no triggered by signal */ std::nullopt); } void http_set_result(const char *headers, int headers_len, const char *body, int body_len, int exit_code) { diff --git a/server/php-runner.cpp b/server/php-runner.cpp index d02d04d3ef..ac6bb497d2 100644 --- a/server/php-runner.cpp +++ b/server/php-runner.cpp @@ -4,6 +4,7 @@ #include "server/php-runner.h" +#include "common/ucontext/ucontext-portable.h" #include #include #include @@ -80,13 +81,24 @@ void send_slow_net_event_stats(const net_event_t &event, double time_sec) noexce } // namespace -void PhpScript::error(const char *error_message, script_error_t error_type) noexcept { +void PhpScript::error(const char *error_message, script_error_t error_type, const std::optional &triggered_by_signal) noexcept { assert (in_script_context == true); in_script_context = false; current_script->state = run_state_t::error; current_script->error_message = error_message; current_script->error_type = error_type; - stack_end = reinterpret_cast(exit_context.uc_stack.ss_sp) + exit_context.uc_stack.ss_size; + stack_end = reinterpret_cast(get_context_stack_ptr_portable(exit_context)) + get_context_stack_size_portable(exit_context); + +#if defined(__linux__) && defined(__x86_64__) + // The error may be produced in process of signal handling. The default behavior on Linux-based platforms + // consider to block a signal during handler execution and unblock after handler ending. + // For x86_64 arch we have context replacement implementation where the signals manipulations is omitted by design, + // e.g. we do not save signals state in context replacement. + // Therefore, we need manual control for signal state, especially, we have to unblock blocked signals at logical end of handler. + if (triggered_by_signal.has_value()) { + dl_unblock_signal(triggered_by_signal.value()); + } +#endif #if ASAN_ENABLED __sanitizer_start_switch_fiber(nullptr, main_thread_stack, main_thread_stacksize); #endif @@ -103,7 +115,7 @@ void PhpScript::try_run_shutdown_functions_on_timeout() noexcept { return; } if (vk::singleton::get().is_running()) { - perform_error_if_running("timeout exit in OOM handler\n", script_error_t::timeout); + perform_error_if_running("timeout exit in OOM handler\n", script_error_t::timeout, /* no triggered by signal */std::nullopt); return; } @@ -116,14 +128,14 @@ void PhpScript::try_run_shutdown_functions_on_timeout() noexcept { state = run_state_t::running; run_shutdown_functions_from_timeout(); } - perform_error_if_running("timeout exit\n", script_error_t::timeout); + perform_error_if_running("timeout exit\n", script_error_t::timeout, /* no triggered by signal */ std::nullopt); } void PhpScript::check_net_context_errors() noexcept { php_assert(PhpScript::in_script_context); if (memory_limit_exceeded) { vk::singleton::get().invoke(); - perform_error_if_running("memory limit exit\n", script_error_t::memory_limit); + perform_error_if_running("memory limit exit\n", script_error_t::memory_limit, /* no triggered by signal */std::nullopt); } try_run_shutdown_functions_on_timeout(); } @@ -197,9 +209,9 @@ void PhpScript::init(script_t *script, php_query_data_t *data_to_set) noexcept { assert_state(run_state_t::before_init); getcontext_portable(&run_context); - run_context.uc_stack.ss_sp = script_stack.get_stack_ptr(); - run_context.uc_stack.ss_size = script_stack.get_stack_size(); - run_context.uc_link = nullptr; + set_context_stack_ptr_portable(run_context, script_stack.get_stack_ptr()); + set_context_stack_size_portable(run_context, script_stack.get_stack_size()); + set_context_link_portable(run_context, nullptr); makecontext_portable(&run_context, &script_context_entrypoint, 0); run_main = script; @@ -225,7 +237,7 @@ void PhpScript::init(script_t *script, php_query_data_t *data_to_set) noexcept { } int PhpScript::swapcontext_helper(ucontext_t_portable *oucp, const ucontext_t_portable *ucp) { - stack_end = reinterpret_cast(ucp->uc_stack.ss_sp) + ucp->uc_stack.ss_size; + stack_end = reinterpret_cast(get_context_stack_ptr_portable(*ucp)) + get_context_stack_size_portable(*ucp); return swapcontext_portable(oucp, ucp); } @@ -477,7 +489,7 @@ void PhpScript::run() noexcept { php_assert(CurException.is_null()); CurException = saved_exception; } - error(php_uncaught_exception_error(CurException), script_error_t::exception); + error(php_uncaught_exception_error(CurException), script_error_t::exception, /* no triggered by signal */ std::nullopt); } } diff --git a/server/php-runner.h b/server/php-runner.h index ce4394a4f9..cd728b1e16 100644 --- a/server/php-runner.h +++ b/server/php-runner.h @@ -130,7 +130,7 @@ class PhpScript { script_result *res{nullptr}; static void script_context_entrypoint() noexcept; - static void error(const char *error_message, script_error_t error_type) noexcept; + static void error(const char *error_message, script_error_t error_type, const std::optional &triggered_by_signal) noexcept; PhpScript(size_t mem_size, double oom_handling_memory_ratio, size_t stack_size) noexcept; ~PhpScript() noexcept; diff --git a/server/signal-handlers.cpp b/server/signal-handlers.cpp index 79a07fdd80..ea1b2b4d1d 100644 --- a/server/signal-handlers.cpp +++ b/server/signal-handlers.cpp @@ -55,7 +55,7 @@ void default_sigalrm_handler(int signum) { if (is_json_log_on_timeout_enabled) { vk::singleton::get().write_log_with_backtrace("Maximum execution time exceeded", E_ERROR); } - perform_error_if_running("timeout exit\n", script_error_t::timeout); + perform_error_if_running("timeout exit\n", script_error_t::timeout, signum); } } } @@ -88,13 +88,13 @@ void sigalrm_handler(int signum) { setitimer(ITIMER_REAL, &timer, nullptr); } else { // if there's no shutdown functions terminate script now - perform_error_if_running("soft timeout exit\n", script_error_t::timeout); + perform_error_if_running("soft timeout exit\n", script_error_t::timeout, signum); } } else { kwrite_str(2, "hard timeout expired\n"); // [3] code in script context and this is the second timeout // time to start shutdown functions has expired, emergency shutdown - perform_error_if_running("hard timeout exit\n", script_error_t::timeout); + perform_error_if_running("hard timeout exit\n", script_error_t::timeout, signum); } } } @@ -104,7 +104,7 @@ void sigusr2_handler(int signum) { if (check_signal_critical_section(signum, "SIGUSR2")) { PhpScript::memory_limit_exceeded = true; if (PhpScript::in_script_context) { - perform_error_if_running("memory limit exit\n", script_error_t::memory_limit); + perform_error_if_running("memory limit exit\n", script_error_t::memory_limit, signum); } } } @@ -112,14 +112,14 @@ void sigusr2_handler(int signum) { void php_assert_handler(int signum) { kwrite_str(2, "in php_assert_handler (SIGRTMIN+1 signal)\n"); if (check_signal_critical_section(signum, "SIGRTMIN+1")) { - perform_error_if_running("php assert error\n", script_error_t::php_assert); + perform_error_if_running("php assert error\n", script_error_t::php_assert,signum); } } void stack_overflow_handler(int signum) { kwrite_str(2, "in stack_overflow_handler (SIGRTMIN+2 signal)\n"); if (check_signal_critical_section(signum, "SIGRTMIN+2")) { - perform_error_if_running("stack overflow error\n", script_error_t::stack_overflow); + perform_error_if_running("stack overflow error\n", script_error_t::stack_overflow, signum); } } @@ -187,7 +187,7 @@ void sigsegv_handler(int signum, siginfo_t *info, void *ucontext) { kwrite_str(2, "In critical section: calling _exit (124)\n"); _exit(124); } else { - PhpScript::error("sigsegv(stack overflow)", script_error_t::stack_overflow); + PhpScript::error("sigsegv(stack overflow)", script_error_t::stack_overflow, signum); } } else { const char *msg = signum == SIGBUS ? "SIGBUS terminating program" : "SIGSEGV terminating program"; @@ -227,10 +227,10 @@ void sigabrt_handler(int, siginfo_t *info, void *) { //mark no return -void perform_error_if_running(const char *msg, script_error_t error_type) { +void perform_error_if_running(const char *msg, script_error_t error_type, const std::optional &triggered_by_signal) { if (PhpScript::in_script_context) { kwrite_str(2, msg); - PhpScript::error(msg, error_type); + PhpScript::error(msg, error_type, triggered_by_signal); assert ("unreachable point" && 0); } } diff --git a/server/signal-handlers.h b/server/signal-handlers.h index 76596570d9..1587d953d9 100644 --- a/server/signal-handlers.h +++ b/server/signal-handlers.h @@ -5,7 +5,7 @@ #include "server/php-runner.h" #include "server/workers-control.h" -void perform_error_if_running(const char *msg, script_error_t error_type); +void perform_error_if_running(const char *msg, script_error_t error_type, const std::optional &triggered_by_signal); void init_handlers(); void worker_global_init_handlers(WorkerType worker_type);