1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 04:27:44 +00:00

UserspaceEmulator: Update signal handlers to use the new stack layout

The kernel sets up the stack like this, so set it up the same way.
This commit is contained in:
Ali Mohammad Pur 2022-02-28 00:00:47 +03:30 committed by Andreas Kling
parent baf7038919
commit 1125cbe336
2 changed files with 122 additions and 48 deletions

View file

@ -616,31 +616,81 @@ void Emulator::dispatch_one_pending_signal()
reportln("\n=={}== Got signal {} ({}), handler at {:p}", getpid(), signum, strsignal(signum), handler.handler); reportln("\n=={}== Got signal {} ({}), handler at {:p}", getpid(), signum, strsignal(signum), handler.handler);
auto old_esp = m_cpu->esp(); auto old_esp = m_cpu->esp().value();
u32 stack_alignment = (m_cpu->esp().value() - 52) % 16; ucontext_t ucontext {
m_cpu->set_esp(shadow_wrap_as_initialized(m_cpu->esp().value() - stack_alignment)); .uc_link = nullptr,
.uc_sigmask = m_signal_mask,
.uc_stack = {
.ss_sp = bit_cast<void*>(old_esp),
.ss_flags = 0,
.ss_size = 0,
},
.uc_mcontext = {
.eax = m_cpu->eax().value(),
.ecx = m_cpu->ecx().value(),
.edx = m_cpu->edx().value(),
.ebx = m_cpu->ebx().value(),
.esp = m_cpu->esp().value(),
.ebp = m_cpu->ebp().value(),
.esi = m_cpu->esi().value(),
.edi = m_cpu->edi().value(),
.eip = m_cpu->eip(),
.eflags = m_cpu->eflags(),
.cs = m_cpu->cs(),
.ss = m_cpu->ss(),
.ds = m_cpu->ds(),
.es = m_cpu->es(),
// ???
.fs = 0,
.gs = 0,
},
};
m_cpu->push32(shadow_wrap_as_initialized(m_cpu->eflags())); // FIXME: Set these fields.
m_cpu->push32(shadow_wrap_as_initialized(m_cpu->eip())); siginfo_t signal_info {
m_cpu->push32(m_cpu->eax()); .si_signo = signum,
m_cpu->push32(m_cpu->ecx()); .si_code = 0,
m_cpu->push32(m_cpu->edx()); .si_errno = 0,
m_cpu->push32(m_cpu->ebx()); .si_pid = 0,
m_cpu->push32(old_esp); .si_uid = 0,
m_cpu->push32(m_cpu->ebp()); .si_addr = 0,
m_cpu->push32(m_cpu->esi()); .si_status = 0,
m_cpu->push32(m_cpu->edi()); .si_band = 0,
.si_value = {
.sival_int = 0,
},
};
// FIXME: Push old signal mask here. // Align the stack to 16 bytes.
// Note that we push some elements on to the stack before the return address,
// so we need to account for this here.
constexpr static FlatPtr elements_pushed_on_stack_before_handler_address = 1; // one slot for a saved register
FlatPtr const extra_bytes_pushed_on_stack_before_handler_address = sizeof(ucontext_t) + sizeof(siginfo_t);
FlatPtr stack_alignment = (old_esp - elements_pushed_on_stack_before_handler_address * sizeof(FlatPtr) + extra_bytes_pushed_on_stack_before_handler_address) % 16;
// Also note that we have to skip the thread red-zone (if needed), so do that here.
old_esp -= stack_alignment;
m_cpu->set_esp(shadow_wrap_with_taint_from(old_esp, m_cpu->esp()));
m_cpu->push32(shadow_wrap_as_initialized(0u)); // syscall return value slot
m_cpu->push_buffer(bit_cast<u8 const*>(&ucontext), sizeof(ucontext_t));
auto pointer_to_ucontext = m_cpu->esp().value();
m_cpu->push_buffer(bit_cast<u8 const*>(&signal_info), sizeof(siginfo_t));
auto pointer_to_signal_info = m_cpu->esp().value();
// FPU state, leave a 512-byte gap. FIXME: Fill this in.
m_cpu->set_esp({ m_cpu->esp().value() - 512, m_cpu->esp().shadow() });
// Leave one empty slot to align the stack for a handler call.
m_cpu->push32(shadow_wrap_as_initialized(0u)); m_cpu->push32(shadow_wrap_as_initialized(0u));
m_cpu->push32(shadow_wrap_as_initialized(pointer_to_ucontext));
m_cpu->push32(shadow_wrap_as_initialized(pointer_to_signal_info));
m_cpu->push32(shadow_wrap_as_initialized(static_cast<u32>(signum)));
m_cpu->push32(shadow_wrap_as_initialized((u32)signum)); m_cpu->push32(shadow_wrap_as_initialized<u32>(handler.handler));
m_cpu->push32(shadow_wrap_as_initialized(handler.handler));
VERIFY((m_cpu->esp().value() % 16) == 0);
m_cpu->push32(shadow_wrap_as_initialized(0u));
m_cpu->set_eip(m_signal_trampoline); m_cpu->set_eip(m_signal_trampoline);
} }
@ -654,21 +704,34 @@ NEVER_INLINE void signal_trampoline_dummy()
// blocking syscall, that syscall may return some special error code in eax; // blocking syscall, that syscall may return some special error code in eax;
// This error code would likely be overwritten by the signal handler, so it's // This error code would likely be overwritten by the signal handler, so it's
// necessary to preserve it here. // necessary to preserve it here.
constexpr static auto offset_to_first_register_slot = sizeof(__ucontext) + sizeof(siginfo) + 512 + 4 * sizeof(FlatPtr);
asm( asm(
".intel_syntax noprefix\n" ".intel_syntax noprefix\n"
".globl asm_signal_trampoline\n"
"asm_signal_trampoline:\n" "asm_signal_trampoline:\n"
"push ebp\n" // stack state: 0, ucontext, signal_info, (alignment = 16), fpu_state (alignment = 16), 0, ucontext*, siginfo*, signal, (alignment = 16), handler
"mov ebp, esp\n"
"push eax\n" // we have to store eax 'cause it might be the return value from a syscall // Pop the handler into ecx
"sub esp, 4\n" // align the stack to 16 bytes "pop ecx\n" // save handler
"mov eax, [ebp+12]\n" // push the signal code // we have to save eax 'cause it might be the return value from a syscall
"push eax\n" "mov [esp+%P2], eax\n"
"call [ebp+8]\n" // call the signal handler // Note that the stack is currently aligned to 16 bytes as we popped the extra entries above.
"add esp, 8\n" // and it's already setup to call the handler with the expected values on the stack.
// call the signal handler
"call ecx\n"
// drop the 4 arguments
"add esp, 16\n"
// Current stack state is just saved_eax, ucontext, signal_info, fpu_state?.
// syscall SC_sigreturn
"mov eax, %P0\n" "mov eax, %P0\n"
"int 0x82\n" // sigreturn syscall "int 0x82\n"
".globl asm_signal_trampoline_end\n"
"asm_signal_trampoline_end:\n" "asm_signal_trampoline_end:\n"
".att_syntax" ::"i"(Syscall::SC_sigreturn)); ".att_syntax"
:
: "i"(Syscall::SC_sigreturn),
"i"(offset_to_first_register_slot),
"i"(offset_to_first_register_slot - sizeof(FlatPtr)));
} }
extern "C" void asm_signal_trampoline(void); extern "C" void asm_signal_trampoline(void);

View file

@ -1400,32 +1400,43 @@ int Emulator::virt$sigprocmask(int how, FlatPtr set, FlatPtr old_set)
int Emulator::virt$sigreturn() int Emulator::virt$sigreturn()
{ {
u32 stack_ptr = m_cpu->esp().value(); u32 stack_ptr = m_cpu->esp().value();
auto local_pop = [&]() -> ValueWithShadow<u32> { auto local_pop = [&]<typename T>() {
auto value = m_cpu->read_memory32({ m_cpu->ss(), stack_ptr }); auto value = m_cpu->read_memory<T>({ m_cpu->ss(), stack_ptr });
stack_ptr += sizeof(u32); stack_ptr += sizeof(T);
return value; return value;
}; };
auto smuggled_eax = local_pop(); // State from signal trampoline (note that we're assuming i386 here):
// saved_ax, ucontext, signal_info, fpu_state.
stack_ptr += 4 * sizeof(u32); // Drop the FPU state
// FIXME: Read and restore from this.
stack_ptr += 512;
m_signal_mask = local_pop().value(); // Drop the signal info
stack_ptr += sizeof(siginfo_t);
m_cpu->set_edi(local_pop()); auto ucontext = local_pop.operator()<ucontext_t>();
m_cpu->set_esi(local_pop());
m_cpu->set_ebp(local_pop());
m_cpu->set_esp(local_pop());
m_cpu->set_ebx(local_pop());
m_cpu->set_edx(local_pop());
m_cpu->set_ecx(local_pop());
m_cpu->set_eax(local_pop());
m_cpu->set_eip(local_pop().value()); auto eax = local_pop.operator()<u32>();
m_cpu->set_eflags(local_pop());
// FIXME: We're losing shadow bits here. m_signal_mask = ucontext.value().uc_sigmask;
return smuggled_eax.value();
auto mcontext_slice = ucontext.slice<&ucontext_t::uc_mcontext>();
m_cpu->set_edi(mcontext_slice.slice<&__mcontext::edi>());
m_cpu->set_esi(mcontext_slice.slice<&__mcontext::esi>());
m_cpu->set_ebp(mcontext_slice.slice<&__mcontext::ebp>());
m_cpu->set_esp(mcontext_slice.slice<&__mcontext::esp>());
m_cpu->set_ebx(mcontext_slice.slice<&__mcontext::ebx>());
m_cpu->set_edx(mcontext_slice.slice<&__mcontext::edx>());
m_cpu->set_ecx(mcontext_slice.slice<&__mcontext::ecx>());
m_cpu->set_eax(mcontext_slice.slice<&__mcontext::eax>());
m_cpu->set_eip(mcontext_slice.value().eip);
m_cpu->set_eflags(mcontext_slice.slice<&__mcontext::eflags>());
// FIXME: We're dropping the shadow bits here.
return eax.value();
} }
int Emulator::virt$getpgrp() int Emulator::virt$getpgrp()