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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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