Why below user space code making loop?

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
User avatar
gamingjam60
Member
Member
Posts: 39
Joined: Sat Aug 24, 2024 10:06 pm
Libera.chat IRC: gamingjam60
Location: India
GitHub: https://github.com/baponkar
Contact:

Why below user space code making loop?

Post by gamingjam60 »

I want to implement MSR based System Call so I have written below code,

switch_to_user.c

Code: Select all

#define KERNEL_CS 0x08 | 0
#define KERNEL_SS 0x10 | 0

#define USER_CS 0x18 | 3    // 0x1B
#define USER_SS 0x20 | 3    // 0x23

#define STACK_SIZE 0x4000   // 16 kb

uint64_t stack_addr;
uint64_t code_addr;

extern void user_stub();
extern void user_stub_end(); 
extern void switch_to_user_mode(uint64_t stack_addr, uint64_t code_addr);



uint64_t create_user_function() {
    uint64_t user_addr = (uint64_t) uheap_alloc(0x1000); // Allocate a page in user space
    if (!user_addr) return 0;
    // printf("Created a userspace pointer at: %x\n", user_addr);

    uint64_t user_stub_size = ((uint64_t)&user_stub_end - (uint64_t)&user_stub);
    memcpy((void *)user_addr, (void *)&user_stub, user_stub_size); // Copy function code
    // printf("memory copied from kernel function: %x into user function: %x\n", (uint64_t)&user_stub, (uint64_t) user_addr);

    return user_addr; // Return the user-accessible function pointer
}


void init_user_mode(){
    stack_addr = (uint64_t) uheap_alloc(STACK_SIZE);
    code_addr = (uint64_t) create_user_function();

    printf("Starting Switching to the user mode\n");
    switch_to_user_mode(stack_addr, code_addr);
    syscall(1, (uint64_t)(char *)"Successfully Switch To User_mode implemented\n", 0);
}


user_stub.asm

Code: Select all



section .text
global user_stub
global user_stub_end

user_stub:
    mov rax, 1
    mov rdi, msg
    syscall

    mov rax, 3
    syscall

.hang:
    hlt
    jmp .hang

user_stub_end:


section .data
msg: db "Hello from userspace!", 10, 0  ; 10 is asci code of newline character \n

syscall.c

Code: Select all

#define MSR_EFER     0xC0000080
#define MSR_STAR     0xC0000081
#define MSR_LSTAR    0xC0000082
#define MSR_SFMASK   0xC0000084

#define EFER_SCE  (1 << 0)  // Enable SYSCALL/SYSRET

#define USER_CS      0x1B   // User mode code selector (0x18 | 3)
#define KERNEL_CS    0x08   // Kernel mode code selector

extern void syscall_entry(); // from syscall_entry.asm
extern ring_buffer_t* keyboard_buffer; // define into keyboard.c

static inline uint64_t read_msr(uint32_t msr) {
    uint32_t low, high;
    asm volatile ("rdmsr" : "=a"(low), "=d"(high) : "c"(msr));
    return ((uint64_t)high << 32) | low;
}

static inline void write_msr(uint32_t msr, uint64_t value) {
    uint32_t low = value & 0xFFFFFFFF;
    uint32_t high = value >> 32;
    asm volatile ("wrmsr" :: "c"(msr), "a"(low), "d"(high));
}


void init_syscall() {
    // Enable SYSCALL/SYSRET by setting SCE in IA32_EFER.
    uint64_t efer = read_msr(MSR_EFER);
    efer |= EFER_SCE;
    write_msr(MSR_EFER, efer);

    // STAR: sets up CS/SS for kernel (bits 32-47) and user (bits 48-63)
    uint64_t star = ((uint64_t)USER_CS << 48) | ((uint64_t)KERNEL_CS << 32);
    write_msr(MSR_STAR, star);

    // LSTAR: address of our syscall entry point.
    write_msr(MSR_LSTAR, (uint64_t)&syscall_entry);

    // SFMASK: mask IF (and maybe other flags) when transitioning.
    write_msr(MSR_SFMASK, 1 << 9);
}



void syscall_handler(uint64_t syscall_num, uint64_t arg1, uint64_t arg2) {
    switch (syscall_num) {
        case SYSCALL_PRINT: {
            const char* str = (const char*)arg1;
            printf(str);  
            break;
        }
        case SYSCALL_READ: {
            char* user_buf = (char*)arg1;
            uint64_t size = arg2;
        }        
        case SYSCALL_EXIT: {
            printf("User requested shell exit.\n");
            while (1) __asm__("hlt");
            break;
        }
        default:
            printf("Unknown syscall: %d\n", (int)syscall_num);
            break;
    }
}



void syscall(uint64_t num, uint64_t arg1, uint64_t arg2) {
    __asm__ volatile (
        "syscall"
        :
        : "a"(num), "D"(arg1), "S"(arg2)
        : "rcx", "r11", "memory"
    );
}

syscall_entry.c

Code: Select all


global syscall_entry
extern syscall_handler

section .text
syscall_entry:
    ; Don't touch RCX or R11! They are used by sysretq.

    ; Save other registers if needed
    push rdi
    push rsi
    push rdx
    push rax

    ; Arguments to syscall_handler(syscall_num, arg1)
    mov rsi, rdi         ; user argument → RSI
    mov rdi, rax         ; syscall number → RDI

    call syscall_handler

    ; Restore registers
    pop rax
    pop rdx
    pop rsi
    pop rdi

    ; Return to user — RCX and R11 must still hold original values
    sysretq
When I am calling below from kernel

Code: Select all

init_syscall();
    init_user_mode();

    char *temp = "Hello\n";
    syscall(1, (uint64_t)temp, 0);
  
The output is showing the last system call never called as init_user_mode() is causing a loop and printing below output

Code: Select all

Starting Switching to the user mode
Hello from userspace!
Hello from userspace!
Hello from userspace!
Hello from userspace!
Hello from userspace!
Hello from userspace!
Hello from userspace!
Hello from userspace!
Hello from userspace!
....
What is causing this loop? I am expecting only once print "Hello from userspace".
nullplan
Member
Member
Posts: 1867
Joined: Wed Aug 30, 2017 8:24 am

Re: Why below user space code making loop?

Post by nullplan »

You have correctly identified that RCX and R11 are needed by sysretq. Unfortunately, you don't save them to stack in your syscall handler, and both registers are classified as volatile in the SysV ABI, so they might be overwritten by your kernel code. That's just the first thing I noticed.
Carpe diem!
User avatar
gamingjam60
Member
Member
Posts: 39
Joined: Sat Aug 24, 2024 10:06 pm
Libera.chat IRC: gamingjam60
Location: India
GitHub: https://github.com/baponkar
Contact:

Re: Why below user space code making loop?

Post by gamingjam60 »

nullplan wrote: Thu Apr 10, 2025 8:54 am You have correctly identified that RCX and R11 are needed by sysretq. Unfortunately, you don't save them to stack in your syscall handler, and both registers are classified as volatile in the SysV ABI, so they might be overwritten by your kernel code. That's just the first thing I noticed.
I am observing some peculiar things but unable to detect the root cause of the problem. Suppose I am calling

Code: Select all

 char *temp = "Hello\n";
 syscall(1, (uint64_t)temp, 0);
 
from kernel space the output is showing hello once correctly but unnecessarily jumping into user_stub function although I never been called intentionally. When I am calling syscall from kernel space the step should be

Code: Select all

 
 syscall function-> syscall(asm) -> syscall_entry-> syscall_handler->print("Hello\n") 
but unfortunetly it is jumping into user_stub nowhere even I did not called it and printing messages containing "Hello from userspace" inside of user_stub. Even I have tryied by calling the below

Code: Select all

init_syscall();
init_user_mode();
It also creating loop with printing recursively "Hello from userspace".

When I am trying to copy a kernelspace c function into userspace and try to switch into userspace which showing page_fault.

Does MSR based system call is good with compare to interrupt based system call?
User avatar
gamingjam60
Member
Member
Posts: 39
Joined: Sat Aug 24, 2024 10:06 pm
Libera.chat IRC: gamingjam60
Location: India
GitHub: https://github.com/baponkar
Contact:

Re: Why below user space code making loop?

Post by gamingjam60 »

nullplan wrote: Thu Apr 10, 2025 8:54 am You have correctly identified that RCX and R11 are needed by sysretq. Unfortunately, you don't save them to stack in your syscall handler, and both registers are classified as volatile in the SysV ABI, so they might be overwritten by your kernel code. That's just the first thing I noticed.
I am observing some peculiar things but unable to detect the root cause of the problem. Suppose I am calling

Code: Select all

 char *temp = "Hello\n";
 syscall(1, (uint64_t)temp, 0);
 
from kernel space the output is showing hello once correctly but unnecessarily jumping into user_stub function although I never been called intentionally. When I am calling syscall from kernel space the step should be

Code: Select all

 
 syscall function-> syscall(asm) -> syscall_entry-> syscall_handler->print("Hello\n") 
but unfortunetly it is jumping into user_stub nowhere even I did not called it and printing messages containing "Hello from userspace" inside of user_stub. Even I have tryied by calling the below

Code: Select all

init_syscall();
init_user_mode();
It also creating loop with printing recursively "Hello from userspace".

When I am trying to copy a kernelspace c function into userspace and try to switch into userspace which showing page_fault.

Does MSR based system call is good with compare to interrupt based system call?
nullplan
Member
Member
Posts: 1867
Joined: Wed Aug 30, 2017 8:24 am

Re: Why below user space code making loop?

Post by nullplan »

Another issue is that you are not putting parentheses around your segment definitions (USER_DS and such), yet you use them in expressions later. Since shifts bind more tightly than bit logic, that will not do what you want. So you are also writing the wrong value to the STAR. Which you're doing anyway, because the value that gets written to the user's CS on sysret is the "sysret CS" + 16. And the value written to SS is "sysret CS + 8".

Your syscall entry code is not switching to kernel stack. Thus you remain on user stack for the syscall, and that is a huge issue. Shouldn't cause a loop at this point, but maybe the not-saving of RCX is doing that.
Carpe diem!
User avatar
gamingjam60
Member
Member
Posts: 39
Joined: Sat Aug 24, 2024 10:06 pm
Libera.chat IRC: gamingjam60
Location: India
GitHub: https://github.com/baponkar
Contact:

Re: Why below user space code making loop?

Post by gamingjam60 »

Yes it works! I have tried to fix all the problem mentioned by you.
Thanks :D
Post Reply