Skip to content

UB if noreturn syscall actually returns #1433

Open
@rusty-snake

Description

@rusty-snake

#[inline]
pub(in crate::backend) unsafe fn syscall1_noreturn(nr: SyscallNumber<'_>, a0: ArgReg<'_, A0>) -> ! {
asm!(
"syscall",
in("rax") nr.to_asm(),
in("rdi") a0.to_asm(),
options(nostack, noreturn)
)
}

#[inline]
pub(crate) fn exit_thread(code: c::c_int) -> ! {
unsafe { syscall_noreturn!(__NR_exit, c_int(code)) }
}

Every syscall may return

Every syscall may return with any value because of seccomp-bpf.

This includes pure syscalls like getuid that may return the current uid as 4294967295_u32 (-EPERM) or exit/exit_group returning. While such seccomp-bpf filters will have huge compatibility problems and are unlikely in real-world, they must be handled in a safe way.

In the case of exit/exit_group there are two safe possibilities that maintain -> ! to the caller:

  1. Infinity loop
    // Note that this is a pseudo example. If the `sys_exit` function is `-> !`,
    // Rust is free to remove the loop because returning from `-> !` is UB.
    // The loop must be implemented in assembly with a backward jump
    // if the `asm!` is marked `noreturn` as return from the `noreturn`-`asm!`
    // is already UB.
    loop {
        sys_exit(code)
    }
  2. Segfault, i.e. ud2 (x86-64) after the syscall.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions