Skip to content

process visible to ps but /proc/<pid> missing + empty comm for Rosetta (amd64) processes #2677

Description

@TomHoepping

Describe the bug

On Apple Silicon under Rosetta, short-lived amd64 processes appear in the
process table (enumerable via ps, and as socket peers / getpid) while
their /proc/<pid> node does not exist and their comm reads empty.
/proc is transiently inconsistent with the process table for emulated
processes. Docker Desktop on the same host does not show this.

This breaks SAP HANA hard: the nameserver, during its "set active"
handshake with the daemon, reads the peer's name via /proc/<pid>/comm,
gets empty, and aborts (SIGABRT). The daemon restarts it, the target PID
rotates each attempt, and the appliance never finishes starting. It
starts reliably on Docker Desktop.

Environment

  • OrbStack: <output of orb version>
  • macOS: 26.5.2 (Apple Silicon)
  • Image arch: linux/amd64 via Rosetta

Direct evidence (inside a running amd64 container)
ps lists PIDs that have no /proc entry and empty comm, alongside
stable processes that read back correctly:

# ps enumerates 838/839/840 but /proc/<pid> is missing:
MISSING /proc/838 ()
MISSING /proc/839 ()
MISSING /proc/840 ()
# a stable process reads back fine:
pid 835: comm=[hdbnameserver] stat2=[(hdbnameserver)]

Reproduce (any amd64 image that forks frequently; here the SAP appliance):

for p in $(ps -eo pid= | tr -d ' '); do
  [ -e /proc/$p/comm ] || echo "MISSING /proc/$p ($(ps -o comm= -p $p 2>/dev/null))"
done; echo checked

Note: reading a stable Rosetta process's /proc/<pid>/comm always
works (a tight self-child read loop shows 0/300 misses). The defect is
specific to short-lived / mid-fork emulated processes, which is why it
only bites software that introspects peer PIDs.

Real-world impact: SAP HANA nameserver abort
Image sapse/abap-cloud-developer-trial:2025 (amd64) never converges on
OrbStack; starts fine on Docker Desktop.

e DaemonClient Network.cpp(00452): Unable to write process name for
  pid 808: exception 1: no.1000000
Unable to find process name for pid 808; $condition$=res
0: TrexDaemonClient::sendDaemonMessage(...)          Network.cpp:448
1: TrexDaemon::Client::sendProgramStartedMessage(...) Network.cpp:602
2: NameServer::...::setActiveToDaemon(...)            TNSClient.cpp:3422
-> Diagnose::ThrowAssertError / ltt::logic_error   (SIGABRT)

The complained-about PID differs every run (808, 809, 838-840…),
matching the transient-process window above.

Ruled out

  • seccomp: identical failure with --security-opt seccomp=unconfined.
  • macOS/Rosetta version: current (26.5.2).
  • fd limits: --ulimit nofile=1048576:1048576 set.
  • Docker Desktop, same host/image, Rosetta = <on/off>: starts fine.

Expected
A PID that is enumerable / reachable as a peer has a consistent
/proc/<pid> entry with a readable comm, as on native Linux and
Docker Desktop.

Actual
For short-lived Rosetta-emulated processes, /proc/<pid> is missing and
comm is empty during a window in which the PID is still enumerable.

To Reproduce

Minimal repro — no SAP image required. On an Apple Silicon Mac with
OrbStack, run an amd64 container that forks rapidly, and concurrently
scan for PIDs that ps lists but /proc is missing:

  1. Start a forking amd64 workload and a /proc-vs-ps checker in one container:

    docker run -it --rm --platform linux/amd64 debian:bookworm bash -c '
      # background fork storm: many short-lived emulated processes
      ( while true; do for j in $(seq 1 50); do (sleep 0.05 &) ; done; done ) &
      # scan: PID enumerable by ps but no /proc/<pid> node
      for n in $(seq 1 400); do
        for p in $(ps -eo pid= | tr -d " "); do
          [ -e /proc/$p/comm ] || echo "MISSING /proc/$p comm=[$(cat /proc/$p/comm 2>/dev/null)]"
        done
      done
      echo done'
    
  2. Observe MISSING /proc/<pid> comm=[] lines: PIDs present in the
    process table with no /proc entry and empty comm.

  3. Run the identical command under Docker Desktop (same Mac, same image,
    Rosetta enabled) → no MISSING lines.

Optional stricter check — a process reachable as a socket/handshake peer
whose /proc/<pid>/comm reads empty is what breaks real software; the
scan above captures the same window those peers are read in.

Real-world reproduction (SAP HANA appliance, amd64):

  1. Pull sapse/abap-cloud-developer-trial:2025 (~30 GB).

  2. Run it under OrbStack:

    docker run --stop-timeout 3600 -i --name a4h -h vhcala4hci \
      --platform linux/amd64 --security-opt seccomp=unconfined \
      --ulimit nofile=1048576:1048576 \
      -p 3200:3200 -p 3300:3300 -p 8443:8443 \
      -p 30213:30213 -p 50000:50000 -p 50001:50001 \
      sapse/abap-cloud-developer-trial:2025 -skip-limits-check -agree-to-sap-license
    
  3. Startup never converges; the nameserver trace repeatedly logs
    Unable to find process name for pid <N>; $condition$=res → SIGABRT,
    with <N> changing every restart.

  4. While it loops, in a second terminal, confirm the /proc inconsistency:

    docker exec -it a4h bash -c '
      for p in $(ps -eo pid= | tr -d " "); do
        [ -e /proc/$p/comm ] || echo "MISSING /proc/$p ($(ps -o comm= -p $p 2>/dev/null))"
      done; echo checked'
    
  5. The same image starts and reaches "have fun!" on Docker Desktop.

Expected: no MISSING lines; every enumerable PID has a consistent
/proc/<pid> with a readable comm (as on Docker Desktop / native Linux).

Actual on OrbStack: short-lived amd64 processes are enumerable while
/proc/<pid> is absent and comm is empty.

Expected behavior

A PID that is observable to userspace must have a consistent /proc/<pid>
entry for as long as it is observable. Specifically, if a process is:

  • enumerable via ps / /proc directory listing, or
  • returned by getpid()/gettid(), or
  • reachable as a peer over a socket (i.e. another process holds its PID),

then /proc/<pid>/ must exist and /proc/<pid>/comm must return the
process's name — exactly as on native Linux and under Docker Desktop on
the same host.

/proc and the process table must not diverge for Rosetta-emulated
(amd64) processes. There should be no window in which a PID is
enumerable or reachable while its /proc/<pid> node is absent or its
comm reads empty.

This matters because peer-process introspection via /proc/<pid>/comm
(and /proc/<pid>/stat, /proc/<pid>/cmdline) is a standard,
widely-relied-upon Linux primitive. Software that reads a peer's name
during startup — SAP HANA's daemon/nameserver handshake being one
concrete case — treats an empty result as a fatal error and aborts.
The behaviour should match native Linux and Docker Desktop, where these
reads are consistent and this software starts normally.

Diagnostic report (REQUIRED)

OrbStack info:
Version: 2.2.3
Commit: c83556b0ef8f1ba9a33abbb194622b6b7a1c0307 (v2.2.3)

System info:
macOS: 26.5.2 (25F84)
CPU: arm64, 10 cores
CPU model: Apple M1 Max
Model: MacBookPro18,4
Memory: 64 GiB

Full report: https://orbstack.dev/_admin/diag/orbstack-diagreport_2026-08-28T19-32-53.335034Z.zip

Screenshots and additional context (optional)

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    t/bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions