Skip to content

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

Open
@ChinYikMing

Description

@ChinYikMing

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions