Skip to content

Unprivileged foreground-group hijack via open(/dev/console) #262

@sysheap

Description

@sysheap

Summary

crates/kernel/src/syscalls/fs_ops.rs (commit 5680ed4, on branch busybox-init) sets the console TTY's foreground process group to the opener's pgid on every open(\"/dev/console\"):

```rust
let descriptor = if let Some(dev) = node.char_device()
&& crate::io::uart::is_console_char_device(&dev)
{
let caller_pgid = self.current_process.with_lock(|p| p.pgid());
crate::io::tty_device::console_tty()
.lock()
.set_fg_pgid(caller_pgid);
FileDescriptor::Tty(crate::io::tty_device::console_tty().clone())
} else {
...
};
```

This was introduced while swapping Solaya's Rust init for busybox init. Busybox's `console::respawn` child calls `setsid()` before opening `/dev/console`, so dash's job-control startup saw `fg_pgid != getpgrp()` and self-stopped via SIGTTIN. The hack unblocked the bring-up but is coarser than the correct fix.

Problem

Any process — not just a session leader, not just root — can open `/dev/console` and silently take over the foreground process group. The currently-interactive shell then receives SIGTTIN/SIGTTOU on its next read/write and stops. There is no permission check, no ownership check, and no requirement that the opener be a session leader without a controlling tty.

Correct fix

Implement `TIOCSCTTY` (ioctl on a tty fd) with the Linux semantics:

  • Caller must be a session leader (`getsid(caller) == getpid(caller)`).
  • Caller must not already have a controlling tty.
  • The tty must not already be the controlling tty of another session (unless the `force` arg is set and caller has CAP_SYS_ADMIN).
  • On success, the tty becomes the caller's session's controlling tty and the foreground process group is set to the caller's process group.

Then remove the blanket `set_fg_pgid` from `do_openat`'s console branch. Busybox already calls `ioctl(fd, TIOCSCTTY, 0)` after opening `/dev/console`, so once TIOCSCTTY is implemented, the inittab flow keeps working without the hack.

Related

Mentioned in the existing deferred-work tracker #250 (item 5, TTY/UART decoupling) but deserves its own issue because it's a security-relevant regression, not just a refactor.


Created by Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions