Stained Glass OS

0014. Cross-process operations in the shared wineserver

Context

Multi-user debt D16 and D19. Some Windows operations require the kernel object server to reach into another process: ReadProcessMemory / WriteProcessMemory across processes, DebugActiveProcess (which writes the debuggee's PEB and reads its modules), hardware-breakpoint debug registers (Get/SetThreadContext), and asynchronous procedure calls delivered to a thread that is not currently waiting in the server (an alertable wait interrupted, some overlapped-I/O completions).

Wine performs these in the server, with ptrace and tgkill on the target (server/ptrace.c: read_process_memory, write_process_memory, get_thread_context, send_thread_signal). Upstream this is fine — the server is the same Unix user as its clients. Stained Glass runs one machine-level wineserver as sgsystem (patches 0004/0005), so for an ordinary user's process these are a cross-uid ptrace/tgkill, which the kernel refuses (EPERM) for an unprivileged process. Measured 2026-09-23: DebugActiveProcess on one's own child, and cross-process ReadProcessMemory, fail with ERROR_ACCESS_DENIED for ordinary users; same-uid operations succeed (proved with a standalone PTRACE_ATTACH and with Get/SetThreadContext working inside a session).

David's constraint: the behaviour a Windows program sees must be identical to Windows. So "leave it as debt" is not acceptable, and whatever we do must reproduce Windows' rules exactly:

Options

A small unprivileged helper, sg-procagent, runs as each session user. The server delegates the four cross-process primitives to the agent for the uid that owns the target; the agent, being that user, performs the ptrace/kill itself (same uid → the kernel allows it) and returns the result.

B. A privileged helper (CAP_SYS_PTRACE + CAP_KILL)

One helper with the capabilities, called by the server. Simpler, but a compromised sgsystem could name any pid and the helper would ptrace it — near-root. It cannot safely verify a pid "belongs to the prefix". Rejected: it recreates the risk patch 0005 removed.

C. Give the wineserver the capabilities directly

Worst: the one process that parses requests from every user becomes root-equivalent. Rejected on sight (this is why patch 0005 exists).

D. Accept as debt

Rejected by David's compatibility requirement.

Decision

A. The server delegates cross-uid read_process_memory, write_process_memory, send_thread_signal, and the debug-register get/set_thread_context to a per-user sg-procagent. Same-uid operations are still done directly by the server. No component gains a capability.

Design

Consequences