Skip to content

Unexpected termination of rv32emu when non-SDL Linux userspace programs exit via syscall_exit() #596

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
ChinYikMing opened this issue May 14, 2025 · 0 comments
Labels
bug Something isn't working

Comments

@ChinYikMing
Copy link
Collaborator

Problem Statement

According to the RISC-V libc implementation, when a Linux userspace program exits via exit() [1] or via _exit()[2], the crt ultimately triggers the exit_group() [3] system call, thus fallthrough to SET_CAUSE_AND_TVAL_THEN_TRAP(). For example, the ls command exits via the exit_group() syscall. In contrast, some programs—such as our Doom application—may directly exits via inline assembly to trigger a syscall for trap-and-emulate cleanup routines.

The syscall number 93 is handled by rv32emu. In the SDL-enabled configuration, syscall_exit() performs SDL cleanup and return and does not halt the RISC-V hart, allowing the emulator to continue running. However, for Linux userspace programs that directly invoke syscall 93 or syscall _exit() [4] (and are not SDL-enabled and are not via libc's exit() or _exit()), the syscall_handler will call rv_halt(), halting the hart and causing rv32emu to exit before emulating the next instruction block. The example victim program is zstd which available from Buildroot (other Linux userspace programs that exit via syscall 93 or syscall _exit() will also be affected).

This bug was unexpectedly created after #551.

The issue originates from the following code snippets:
src/emulate.c

void ecall_handler(riscv_t *rv)
{
    ...
    if (rv->priv_mode == RV_PRIV_U_MODE) {
        switch (rv_get_reg(
            rv,
            rv_reg_a7)) { /* trap guestOS's SDL-oriented application syscall */
        case 0xBEEF:
        case 0xC0DE:
        case 0xFEED:
        case 0xBABE:
        case 0xD00D:
        case 93:
            syscall_handler(rv);
            rv->PC += 4;
            break;
        default:
            SET_CAUSE_AND_TVAL_THEN_TRAP(rv, ECALL_U, 0);
            break;
        }
    ...
}

src/syscall.c

static void syscall_exit(riscv_t *rv)
{
#if RV32_HAS(SDL) && RV32_HAS(SYSTEM) && !RV32_HAS(ELF_LOADER)
    /*
     * The guestOS may repeatedly open and close the SDL window,
     * and the user could close the application using the application’s
     * built-in exit function. Need to trap the built-in exit and
     * ensure the SDL window and SDL mixer are destroyed properly.
     */
    extern void sdl_video_audio_cleanup();
    sdl_video_audio_cleanup();
    return;
#endif

    /* simply halt cpu and save exit code.
     * the application decides the usage of exit code
     */
    rv_halt(rv);

    vm_attr_t *attr = PRIV(rv);
    attr->exit_code = rv_get_reg(rv, rv_reg_a0);
}

[1] exit(3)
[2] _exit(3)
[3] exit_group(2)
[4] _exit(2)

@ChinYikMing ChinYikMing added the bug Something isn't working label May 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant