Weird GPF on signal return

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
layzy
Posts: 5
Joined: Sat Feb 21, 2026 6:41 am
Libera.chat IRC: layzy37

Weird GPF on signal return

Post by layzy »

Hey everyone!
So I've been trying to implement signals for a week now, and for the last 5 days I've been very confused about why my code doesn't work.
The signal handling part does seem to work (for non RT signals at least) and the signal handler wrapper is correctly called.
What is confusing is that after the signal return syscall, the next context switch will result in a GPF with a nonsense (0xedc0) segment selector on the iretq instruction.
Here is my context push/pop code:

Code: Select all

void task_setup_stack(thread_t* task, uint64_t entry_point)
{
    task_context_stack_push(task, task->rsp);

    LOG(DEBUG, "Pushed context:");
    log_context(task);

    // if (task->context_sp >= TASK_KERNEL_STACK_BOTTOM_ADDRESS + 16) return;

    task_stack_push(task, (task->ring == 0) ? KERNEL_DATA_SEGMENT : USER_DATA_SEGMENT);
    task_stack_push(task, task->rsp + 8);
    task_stack_push(task, 0x202);  // get_rflags()
    task_stack_push(task, (task->ring == 0) ? KERNEL_CODE_SEGMENT : USER_CODE_SEGMENT);
    task_stack_push(task, entry_point);

    task_stack_push(task, (uint64_t)iretq_instruction);

    task_stack_push(task, (uint64_t)unlock_scheduler);
    task_stack_push(task, (uint64_t)cleanup_tasks);
    task_stack_push(task, (uint64_t)end_context_switch);

    task_stack_push(task, 0);           // rax
    task_stack_push(task, 0);           // rbx
    task_stack_push(task, 0);           // rcx
    task_stack_push(task, (task->ring == 0) ? KERNEL_DATA_SEGMENT : USER_DATA_SEGMENT);    // rdx
    task_stack_push(task, 0);           // r8
    task_stack_push(task, 0);           // r9
    task_stack_push(task, 0);           // r10
    task_stack_push(task, 0);           // r11
    task_stack_push(task, 0);           // r12
    task_stack_push(task, 0);           // r13
    task_stack_push(task, 0);           // r14
    task_stack_push(task, 0);           // r15
    task_stack_push(task, 0);           // rbp
}
void task_unsetup_stack(thread_t* task)
{
    task->rsp = task_context_stack_pop(task);
    LOG(DEBUG, "Popped context:");
    log_context(task);
}

void task_context_stack_push(thread_t* task, uint64_t rsp)
{
    LOG(TRACE, "task_context_stack_push(%p, %#" PRIx64 ")", task, rsp);
    task_write_at_address_8b(task, task->context_sp, rsp);
    task->context_sp += 8;
}
uint64_t task_context_stack_pop(thread_t* task)
{
    task->context_sp -= 8;
    uint64_t ret = task_read_at_address_8b(task, task->context_sp);
    LOG(TRACE, "task_context_stack_pop(%p) = %#" PRIx64 " (%#" PRIx64 ")", task, ret, task->rsp);
    return ret;
}
As you can see I have a stack used for saving contexts which grows upwards from the lower end of the kernel queue.
Here are the logs:

Code: Select all

2026-02-21 12:55:03.058 - [Debug] 	Sending signal 2 to pgrp 1
2026-02-21 12:55:03.058 - [Debug] 	pid 1 receiving signal 2
2026-02-21 12:55:03.058 - [Debug] 	Signal was sent to the process
2026-02-21 12:55:03.059 - [Trace] 	task_context_stack_push(0xffff8000029ec020, 0x7fffffbfeb98)
2026-02-21 12:55:03.059 - [Debug] 	Pushed context:
2026-02-21 12:55:03.059 - [Debug] 	log_context (rsp = 0x00007fffffbfeb98)
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfeb98 (rsp + 0) = 0x00007fffffbfec18
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfeba0 (rsp + 1) = 0x0000000000000400
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfeba8 (rsp + 2) = 0xffffffffffe37520
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfebb0 (rsp + 3) = 0xffffffffffe373c4
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfebb8 (rsp + 4) = 0xffffffffffe37410
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfebc0 (rsp + 5) = 0xffffffffffe17510
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfebc8 (rsp + 6) = 0x0000000000000130
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfebd0 (rsp + 7) = 0xffff80000ffe0060
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfebd8 (rsp + 8) = 0xffff8000029e5f50
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfebe0 (rsp + 9) = 0x0000000000000010
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfebe8 (rsp + 10) = 0x00000000ffffffff
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfebf0 (rsp + 11) = 0xffff8000029ec020
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfebf8 (rsp + 12) = 0x0000000000000207
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfec00 (rsp + 13) = 0xffffffffffe0feff
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfec08 (rsp + 14) = 0xffffffffffe373c4
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfec10 (rsp + 15) = 0x0000000000000015
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfec18 (rsp + 16) = 0x00007fffffbfec38
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfec20 (rsp + 17) = 0xffffffffffe0b887
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfec28 (rsp + 18) = 0x0000000000000246
2026-02-21 12:55:03.059 - [Debug] 	0x00007fffffbfec30 (rsp + 19) = 0xffffffffffe373c4
2026-02-21 12:55:03.086 - [Trace] 	task_context_stack_pop(0xffff8000029ec020) = 0x7fffffbfeb98 (0x7fffffbfed40)
2026-02-21 12:55:03.086 - [Debug] 	Popped context:
2026-02-21 12:55:03.086 - [Debug] 	log_context (rsp = 0x00007fffffbfeb98)
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfeb98 (rsp + 0) = 0x00007fffffbfec18
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfeba0 (rsp + 1) = 0x0000000000000400
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfeba8 (rsp + 2) = 0xffffffffffe37520
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfebb0 (rsp + 3) = 0xffffffffffe373c4
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfebb8 (rsp + 4) = 0xffffffffffe37410
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfebc0 (rsp + 5) = 0xffffffffffe17510
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfebc8 (rsp + 6) = 0x0000000000000130
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfebd0 (rsp + 7) = 0xffff80000ffe0060
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfebd8 (rsp + 8) = 0xffff8000029e5f50
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfebe0 (rsp + 9) = 0x0000000000000010
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfebe8 (rsp + 10) = 0x00000000ffffffff
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfebf0 (rsp + 11) = 0xffff8000029ec020
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfebf8 (rsp + 12) = 0x0000000000000207
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfec00 (rsp + 13) = 0xffffffffffe0feff
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfec08 (rsp + 14) = 0xffffffffffe373c4
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfec10 (rsp + 15) = 0x0000000000000015
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfec18 (rsp + 16) = 0x00007fffffbfec38
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfec20 (rsp + 17) = 0xffffffffffe0b887
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfec28 (rsp + 18) = 0x0000000000000246
2026-02-21 12:55:03.086 - [Debug] 	0x00007fffffbfec30 (rsp + 19) = 0xffffffffffe373c4
2026-02-21 12:55:03.126 - [Warn] 	[task "/sbin/init" (pid 1)]: Fault : Exception number : 13 ; Error : GENERAL_PROTECTION_FAULT ; Error code = 0xedc0 ; cr2 = 0x0 ; cr3 = 0x2e4b000 ; rip = 0xffffffffffe2d497
2026-02-21 12:55:03.128 - [Warn] 	CS: 0x0000000000000008 DS: 0x0000000000000010 SS: 0x0000000000000010
2026-02-21 12:55:03.129 - [Info] 	FS BASE: 0x00000000008bf9e0 GS BASE: 0x0000000000000000 KERNEL GS BASE: 0xffffffffffe43190
2026-02-21 12:55:03.130 - [Fatal] 	Kernel panic
2026-02-21 12:55:03.132 - [Info] 	RSP=0x00007fffffbfed38 RBP=0x00007fffffbfed90 RAX=0x0000000000000038 RBX=0xffff8000029e9df0 RCX=0x0000000000000018 RDX=0x0000000000000060
2026-02-21 12:55:03.134 - [Info] 	R8=0xffffffffffe11488 R9=0x00007fffffbfed20 R10=0xffff80000001b1c8 R11=0x0000000000000000 R12=0xffffffffffe1f981 R13=0xffffffffffe373c4 R14=0xffffffffffe37520 R15=0x0000000000000400
2026-02-21 12:55:03.136 - [Info] 	RDI=0x00007fffffbfed30 RSI=0xffffffffffe09a42
2026-02-21 12:55:03.137 - [Info] 	FS BASE: 0x00000000008bf9e0 GS BASE: 0x0000000000000000 KERNEL GS BASE: 0xffffffffffe43190
2026-02-21 12:55:03.139 - [Info] 	Stack trace : 
2026-02-21 12:55:03.140 - [Info] 	rip : 0xffffffffffe2d497 [iretq_instruction.dont_swapgs2]
2026-02-21 12:55:03.142 - [Info] 	rip : 0xffff8000029ec020 | rbp : 0x7fffffbfed90 
As you can see the stack is correctly restored after the signal return syscall but the crash happens as if it was corrupted.
If i uncomment the line that disabled signals (by not pushing the new context) the code works (and no signal is generated) so it means the stack itself isn't wrong.

I hope someone can see what could be happening because this is very annoying and i really have no idea what to do
layzy
Posts: 5
Joined: Sat Feb 21, 2026 6:41 am
Libera.chat IRC: layzy37

Re: Weird GPF on signal return

Post by layzy »

So here's an update:
After adding a LOG line in the sigret syscall handler the problem changes:

Code: Select all

2026-02-21 12:58:41.504 - [Debug] 	pid 1 receiving signal 2
2026-02-21 12:58:41.504 - [Debug] 	Signal was sent to the process
2026-02-21 12:58:41.505 - [Trace] 	task_context_stack_push(0xffff8000029ec020, 0x7fffffbfeba8)
2026-02-21 12:58:41.505 - [Debug] 	Pushed context:
2026-02-21 12:58:41.505 - [Debug] 	log_context (rsp = 0x00007fffffbfeba8)
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfeba8 (rsp + 0) = 0x00007fffffbfec28
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfebb0 (rsp + 1) = 0x0000000000000400
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfebb8 (rsp + 2) = 0xffffffffffe37280
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfebc0 (rsp + 3) = 0xffffffffffe37124
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfebc8 (rsp + 4) = 0xffffffffffe37170
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfebd0 (rsp + 5) = 0xffffffffffe17250
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfebd8 (rsp + 6) = 0x0000000000000130
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfebe0 (rsp + 7) = 0xffff80000ffe0060
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfebe8 (rsp + 8) = 0xffff8000029e5f50
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfebf0 (rsp + 9) = 0x0000000000000010
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfebf8 (rsp + 10) = 0x00000000ffffffff
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfec00 (rsp + 11) = 0xffff8000029ec020
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfec08 (rsp + 12) = 0x0000000000000207
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfec10 (rsp + 13) = 0xffffffffffe0fc5f
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfec18 (rsp + 14) = 0xffffffffffe37124
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfec20 (rsp + 15) = 0x0000000000000015
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfec28 (rsp + 16) = 0x00007fffffbfec48
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfec30 (rsp + 17) = 0xffffffffffe0b5e7
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfec38 (rsp + 18) = 0x0000000000000246
2026-02-21 12:58:41.505 - [Debug] 	0x00007fffffbfec40 (rsp + 19) = 0xffffffffffe37124
2026-02-21 12:58:41.520 - [Trace] 	syscall SYS_SIGRET
2026-02-21 12:58:41.521 - [Trace] 	task_context_stack_pop(0xffff8000029ec020) = 0x7fffffbfeba8 (0x7fffffbfed50)
2026-02-21 12:58:41.521 - [Debug] 	Popped context:
2026-02-21 12:58:41.521 - [Debug] 	log_context (rsp = 0x00007fffffbfeba8)
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfeba8 (rsp + 0) = 0x00007fffffbfed58
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfebb0 (rsp + 1) = 0x00000000ffffbb49
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfebb8 (rsp + 2) = 0xffffffffffe2d8b7
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfebc0 (rsp + 3) = 0x0000000000000053
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfebc8 (rsp + 4) = 0x0000000000000002
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfebd0 (rsp + 5) = 0x00007fffffbfec80
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfebd8 (rsp + 6) = 0x0000000000000001
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfebe0 (rsp + 7) = 0x0000000000000001
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfebe8 (rsp + 8) = 0x0000000000000002
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfebf0 (rsp + 9) = 0x0000000000000053
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfebf8 (rsp + 10) = 0x0000000000000080
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfec00 (rsp + 11) = 0x0000000000000000
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfec08 (rsp + 12) = 0xffffffffffe1708a
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfec10 (rsp + 13) = 0x0000000000000008
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfec18 (rsp + 14) = 0x0000000000000246
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfec20 (rsp + 15) = 0x00007fffffbfec38
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfec28 (rsp + 16) = 0x0000000000000010
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfec30 (rsp + 17) = 0xffffffffffe2ed48
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfec38 (rsp + 18) = 0xffffffffffe171d6
2026-02-21 12:58:41.521 - [Debug] 	0x00007fffffbfec40 (rsp + 19) = 0x0000000000000030
2026-02-21 12:58:41.560 - [Warn] 	[task "/sbin/init" (pid 1)]: Fault : Exception number : 13 ; Error : GENERAL_PROTECTION_FAULT ; Error code = 0x50 ; cr2 = 0x0 ; cr3 = 0x2e4b000 ; rip = 0xffffffffffe2d2a3
2026-02-21 12:58:41.562 - [Warn] 	CS: 0x0000000000000008 DS: 0x0000000000000010 SS: 0x0000000000000010
2026-02-21 12:58:41.563 - [Info] 	FS BASE: 0x0000000000000000 GS BASE: 0x0000000000000000 KERNEL GS BASE: 0xffffffffffe42ef0
2026-02-21 12:58:41.564 - [Fatal] 	Kernel panic
2026-02-21 12:58:41.564 - [Info] 	RSP=0x00007fffffbfec10 RBP=0x00007fffffbfed58 RAX=0xffffffffffe1708a RBX=0x0000000000000000 RCX=0x0000000000000080 RDX=0x0000000000000053
2026-02-21 12:58:41.564 - [Info] 	R8=0x0000000000000002 R9=0x0000000000000001 R10=0x0000000000000001 R11=0x00007fffffbfec80 R12=0x0000000000000002 R13=0x0000000000000053 R14=0xffffffffffe2d8b7 R15=0x00000000ffffbb49
2026-02-21 12:58:41.564 - [Info] 	RDI=0xffff8000029e6c60 RSI=0xffff8000029ec020
2026-02-21 12:58:41.564 - [Info] 	FS BASE: 0x0000000000000000 GS BASE: 0x0000000000000000 KERNEL GS BASE: 0xffffffffffe42ef0
2026-02-21 12:58:41.564 - [Info] 	Stack trace : 
2026-02-21 12:58:41.564 - [Info] 	rip : 0xffffffffffe2d2a3 [context_switch.end]
This is very strange, it seems that the stack is altered between the signal handler being called and the sigret.
Here is more info:
Here is the full context switch code:

Code: Select all

void full_context_switch(thread_t* next)
{
    // log_context(next);

    thread_t* old_task = current_task;
    last_task = old_task;
    current_task = next;

    if (old_task->ring != 0)
        swapgs();

    old_task->fs_base = rdfsbase();
    old_task->gs_base = rdgsbase();

    fpu_save_state(old_task->fpu_state);
    
    context_switch(old_task, current_task, (current_task->ring == 0) ? KERNEL_DATA_SEGMENT : USER_DATA_SEGMENT);

    end_context_switch();
}

void end_context_switch()
{
    fpu_restore_state(current_task->fpu_state);

    wrfsbase(current_task->fs_base);
    wrgsbase(current_task->gs_base);

    if (current_task->ring != 0)
        swapgs();

    // LOG(DEBUG, "Saved context of the last task:");
    // log_context(last_task);
}

Code: Select all

void switch_task()
{
    if (task_count == 0)
    {
        LOG(DEBUG, "No processes left");
        abort();
    }

    if (task_lock_depth > 0)
    {
        queued_ts = true;
        return;
    }

    // ! Should never log anything here

    lock_scheduler();

    thread_t* next_task = find_next_task();
    if (current_task != next_task)
        full_context_switch(next_task);

    cleanup_tasks();

    unlock_scheduler();
}
void cleanup_tasks()
{
    lock_scheduler();

    if (forked_tasks)
    {
        thread_queue_item_t* cur_forked_task = forked_tasks;
        do
        {
            thread_t* task_to_fork = cur_forked_task->data;
            cur_forked_task = cur_forked_task->next;
            if (task_to_fork != current_task)
            {
                move_task_to_running_queue(&forked_tasks, cur_forked_task);
                duplicate_task(task_to_fork);
            }
        }
        while (forked_tasks && cur_forked_task != forked_tasks);
    }

    if (pending_signal_tasks)
    {
        thread_queue_item_t* it = pending_signal_tasks;
        do
        {
            thread_t* cur = it->data;
            it = it->next;
            if (cur != current_task)
            {
                if (cur->pending_signal_handler)
                    task_setup_stack(cur, (uint64_t)sighandler);
                else
                    task_unsetup_stack(cur);
                move_task_to_running_queue(&pending_signal_tasks, it);
            }
        }
        while (pending_signal_tasks && it != pending_signal_tasks);
    }

    if (reapable_tasks)
    {
        thread_queue_item_t* cur_reapable_task = reapable_tasks;
        do
        {
            thread_t* task_to_kill = cur_reapable_task->data;
            if (cur_reapable_task->data != current_task)
            {
                thread_queue_item_t* removed_item = cur_reapable_task;
                cur_reapable_task = cur_reapable_task->next;
                task_destroy(task_to_kill);
                free(task_to_kill);
                task_count--;
                thread_queue_remove(&reapable_tasks, removed_item);
            }
            else
                cur_reapable_task = cur_reapable_task->next;
        }
        while (reapable_tasks != NULL && cur_reapable_task->prev != cur_reapable_task);
    }

    unlock_scheduler();
}

Code: Select all

global context_switch
context_switch:
; * RDI, RSI, RDX are arguments (they are caller saved so no need to push them)
    push rax
    push rbx
    push rcx
    push rdx
    push r8
    push r9
    push r10
    push r11
    push r12
    push r13
    push r14
    push r15
    push rbp

    mov rbx, qword [rel task_rsp_offset]
    ; $rdi = (uint64_t)old_tcb
    mov [rdi + rbx], rsp                ; rdi->rsp = $rsp

    ; $rsi = (uint64_t)next_tcb

    ; $rdx = ds

    mov rsp, [rsi + rbx]                ; $rsp = rsi->rsp

    mov rbx, qword [rel task_cr3_offset]

    mov rcx, cr3
    mov [rdi + rbx], rcx
    mov rax, [rsi + rbx]

    cmp rax, rcx
    je .end
    mov cr3, rax

.end:
    pop rbp
    pop r15
    pop r14
    pop r13
    pop r12
    pop r11
    pop r10
    pop r9
    pop r8
    pop rdx
    pop rcx
    pop rbx
    pop rax

    mov ds, dx
    mov es, dx
    mov fs, dx
    mov gs, dx

    ret
And the actual signal handling code:

Code: Select all

...
LOG(DEBUG, "Signal was sent to the process");
    thread->pending_signal_handler = (act->sa_flags & SA_SIGINFO) ? (uint64_t)act->sa_sigaction : (uint64_t)act->sa_handler;
    move_task_to_queue(&pending_signal_tasks, thread);
    if (thread == current_task)
        switch_task();
    unlock_scheduler();
Octocontrabass
Member
Member
Posts: 6249
Joined: Mon Mar 25, 2013 7:01 pm

Re: Weird GPF on signal return

Post by Octocontrabass »

Your code is very strange and I don't understand what you're doing. Why do you have a "stack for saving contexts"? Doesn't each task (thread) already have its own stack?
layzy wrote: Sat Feb 21, 2026 7:34 amThis is very strange, it seems that the stack is altered between the signal handler being called and the sigret.
Did you build your ring 0 code with "-mno-red-zone"? Did you account for the red zone when manipulating ring 3 stacks?
layzy wrote: Sat Feb 21, 2026 7:34 am

Code: Select all

context_switch:
Why does this function save and restore so many unnecessary registers? (I'd also argue that this function does too much work: the caller should be responsible for CR3 and for figuring out where to save and load RSP.)
layzy
Posts: 5
Joined: Sat Feb 21, 2026 6:41 am
Libera.chat IRC: layzy37

Re: Weird GPF on signal return

Post by layzy »

Thanks for you answer,
Octocontrabass wrote: Sat Feb 21, 2026 7:42 pm Why do you have a "stack for saving contexts"?
This is just a per thread stack that keeps tracks of the rsp value of contexts (so i can return from the signal).
I'm not really sure in what way it is weird since it's the simplest way i could think about that would allow for signal handling/return with nested signals but maybe I'm misunderstanding something.
In my case it's just growing up from the bottom of the kernel stack so it doesn't interfere with anything.
Octocontrabass wrote: Sat Feb 21, 2026 7:42 pm Did you build your ring 0 code with "-mno-red-zone"?
Yes I did: Here's a list of flags i used

Code: Select all

-mno-red-zone -mgeneral-regs-only -march=x86-64 -O2
Octocontrabass wrote: Sat Feb 21, 2026 7:42 pm Why does this function save and restore so many unnecessary registers? (I'd also argue that this function does too much work: the caller should be responsible for CR3 and for figuring out where to save and load RSP.)
Well it is basically the function from Brendan's Multitasking tutorial but adapted to work with sysv abi on x86_64.
After your comment if read the "x86 calling conventions" wikipedia article (again) and realized i missed this line:
If the callee wishes to use registers RBX, RSP, RBP, and R12–R15, it must restore their original values before returning control to the caller. All other registers must be saved by the caller if it wishes to preserve their values.
So I'll probably simplify my code to account for this but still it doesn't explain the stack being corrupted afaik.
layzy
Posts: 5
Joined: Sat Feb 21, 2026 6:41 am
Libera.chat IRC: layzy37

Re: Weird GPF on signal return

Post by layzy »

Okay i understand the problem and it's stupid...
Since the context is pushed on top of the other one it is on the kernel stack (which gets overriden on any interrupt)
Thanks for your help anyways
Octocontrabass
Member
Member
Posts: 6249
Joined: Mon Mar 25, 2013 7:01 pm

Re: Weird GPF on signal return

Post by Octocontrabass »

layzy wrote: Sun Feb 22, 2026 3:26 amThis is just a per thread stack that keeps tracks of the rsp value of contexts (so i can return from the signal).
I'm not really sure in what way it is weird since it's the simplest way i could think about that would allow for signal handling/return with nested signals but maybe I'm misunderstanding something.
Each task (thread) already has its own stack. All you need to do is push the task's context onto the task's stack and it will be there later when it's time to return from the signal handler.
layzy wrote: Sun Feb 22, 2026 3:26 amWell it is basically the function from Brendan's Multitasking tutorial but adapted to work with sysv abi on x86_64.
Yes, that's my point. With few exceptions, code on the wiki is only meant to help illustrate specific concepts. It isn't meant to be a good example, and in many cases it isn't even functional. (For example, see if you can spot why Brendan's switch_to_task can't work with SMP.)
layzy
Posts: 5
Joined: Sat Feb 21, 2026 6:41 am
Libera.chat IRC: layzy37

Re: Weird GPF on signal return

Post by layzy »

Yes I understand it now.
If the Linux kernel determines that an unblocked signal is pending for a process, then,
at the next transition back to user mode in that process (e.g., upon return from a system call or when the process
is rescheduled onto the CPU), it creates a new frame on the user-space stack where it saves various pieces of process
context (processor status word, registers, signal mask, and signal stack settings).
(from the sigreturn man page)
Octocontrabass wrote: Sun Feb 22, 2026 2:56 pm code on the wiki is only meant to help illustrate specific concepts
I understand this, but in my case it does works (i fixed my code to only push/pop the necessary registers) and i am not planning on implementing SMP soon.
Octocontrabass wrote: Sun Feb 22, 2026 2:56 pm Brendan's switch_to_task can't work with SMP
My code locks the scheduler on context switch (which disables interrupts and acquires a spinlock) so i don't really see why it wouldn't be.
But then obviously my task queue code is not atomic everywhere (most of my code isn't tbh) and i should have a per cpu core/hyperthread task queue. The scheduler also needs to be SMP aware and everything is a lot more complex.
But then again my scheduler doesn't even do load balancing/cache optimizations so i don't see why i should care for now.
Octocontrabass
Member
Member
Posts: 6249
Joined: Mon Mar 25, 2013 7:01 pm

Re: Weird GPF on signal return

Post by Octocontrabass »

layzy wrote: Sun Feb 22, 2026 3:22 pmBut then again my scheduler doesn't even do load balancing/cache optimizations so i don't see why i should care for now.
It depends on what you want to do with your OS. If you want to eventually support SMP so you can use all the cores in modern CPUs, you should plan for it now. If you don't want SMP, you don't need to worry about it.
Post Reply