MCPcopy Create free account
hub / github.com/DeusData/codebase-memory-mcp / cbm_posix_spawn_apple

Function cbm_posix_spawn_apple

src/foundation/subprocess.c:1092–1151  ·  view source on GitHub ↗

macOS: spawn instead of fork+exec. * * fork() duplicates the parent's whole address space bookkeeping, and an * ASan-instrumented parent carries an enormous shadow mapping. Past a * footprint threshold the child is killed (jetsam) BEFORE exec replaces the * image, so the call fails with the child already gone (ESRCH on reap) — a * spawn failure that looks like the launched tool crashing. The

Source from the content-addressed store, hash-verified

1090 * because dup2 clears close-on-exec.
1091 * posix_spawnp keeps execvp's PATH semantics for a bare tool name. */
1092static int cbm_posix_spawn_apple(cbm_subprocess_t *process, int input, int output, pid_t *pid_out) {
1093 /* posix_spawn is the PRIMARY path on macOS; fork+exec is only the
1094 * exec-class fallback. Injection therefore has to live here too, or a test
1095 * on macOS exercises nothing. */
1096#ifdef CBM_ENABLE_TEST_SEAMS
1097 if (cbm_spawn_eagain_injected()) {
1098 return CBM_SPAWN_RETRY;
1099 }
1100#endif
1101 posix_spawn_file_actions_t actions;
1102 posix_spawnattr_t attr;
1103 if (posix_spawn_file_actions_init(&actions) != 0) {
1104 return -1;
1105 }
1106 if (posix_spawnattr_init(&attr) != 0) {
1107 (void)posix_spawn_file_actions_destroy(&actions);
1108 return -1;
1109 }
1110 sigset_t empty_mask;
1111 sigset_t all_signals;
1112 sigemptyset(&empty_mask);
1113 sigfillset(&all_signals);
1114 short flags = (short)(POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK |
1115 POSIX_SPAWN_CLOEXEC_DEFAULT);
1116 bool configured = posix_spawnattr_setflags(&attr, flags) == 0 &&
1117 posix_spawnattr_setpgroup(&attr, 0) == 0 &&
1118 posix_spawnattr_setsigmask(&attr, &empty_mask) == 0 &&
1119 posix_spawnattr_setsigdefault(&attr, &all_signals) == 0 &&
1120 posix_spawn_file_actions_adddup2(&actions, input, STDIN_FILENO) == 0 &&
1121 posix_spawn_file_actions_adddup2(&actions, output, STDOUT_FILENO) == 0 &&
1122 posix_spawn_file_actions_adddup2(&actions, output, STDERR_FILENO) == 0;
1123 pid_t pid = -1;
1124 int rc =
1125 configured ? posix_spawnp(&pid, process->bin, &actions, &attr, process->argv, environ) : -1;
1126 (void)posix_spawn_file_actions_destroy(&actions);
1127 (void)posix_spawnattr_destroy(&attr);
1128 if (configured && rc == 0 && pid > 0) {
1129 *pid_out = pid;
1130 return 0;
1131 }
1132 /* posix_spawn reports an unusable binary itself, where fork+exec instead
1133 * produces a child that exits 127. Callers (and tests) rely on the latter:
1134 * "spawn_failed" means the SPAWN mechanism failed, not that the tool was
1135 * missing. Fall back to fork+exec for exec-class errors so macOS and Linux
1136 * classify a bogus binary identically; the ASan-fork hazard does not apply
1137 * here, since this child exits immediately. */
1138 if (configured && (rc == ENOENT || rc == EACCES || rc == ENOEXEC || rc == EISDIR ||
1139 rc == ELOOP || rc == ENAMETOOLONG || rc == ENOTDIR)) {
1140 return 1;
1141 }
1142 /* EAGAIN/ENOMEM are the kernel saying "not right now", not "never": the
1143 * process table or a per-user limit is momentarily full. Reporting
1144 * spawn_failed for that turns transient load into a user-visible error —
1145 * a git or LSP probe failing on a busy laptop for no reason the user can
1146 * see or act on. Retry briefly. Everything else stays a hard failure. */
1147 if (configured && (rc == EAGAIN || rc == ENOMEM)) {
1148 return CBM_SPAWN_RETRY;
1149 }

Callers 1

Calls 1

Tested by

no test coverage detected