fork+exec child setup. On Apple this runs ONLY for the exec-failure * fallback (see cbm_posix_spawn_apple), which preserves the documented * "bogus binary => child exits 127" contract across platforms. */
| 903 | * fallback (see cbm_posix_spawn_apple), which preserves the documented |
| 904 | * "bogus binary => child exits 127" contract across platforms. */ |
| 905 | static void cbm_posix_child_exec(cbm_subprocess_t *process, int input, int output, long max_fd) { |
| 906 | if (setpgid(0, 0) < 0) { |
| 907 | _exit(127); |
| 908 | } |
| 909 | cbm_posix_reset_child_signals(); |
| 910 | |
| 911 | /* Never let a worker consume the MCP transport inherited as stdin. Only |
| 912 | * async-signal-safe calls are used between fork and exec. */ |
| 913 | if (input < 0 || output < 0 || dup2(input, STDIN_FILENO) < 0 || |
| 914 | dup2(output, STDOUT_FILENO) < 0 || dup2(output, STDERR_FILENO) < 0) { |
| 915 | _exit(127); |
| 916 | } |
| 917 | if (input > STDERR_FILENO) { |
| 918 | (void)close(input); |
| 919 | } |
| 920 | if (output > STDERR_FILENO) { |
| 921 | (void)close(output); |
| 922 | } |
| 923 | for (int fd = STDERR_FILENO + 1; fd < max_fd; fd++) { |
| 924 | (void)close(fd); |
| 925 | } |
| 926 | /* A fixed literal tool name (for example "git" or "curl") uses the |
| 927 | * caller's normal PATH without introducing a shell. An explicit path |
| 928 | * still has execvp's exact-path semantics because it contains '/'. */ |
| 929 | execvp(process->bin, process->argv); |
| 930 | _exit(127); |
| 931 | } |
| 932 | |
| 933 | static int cbm_posix_fd_at_least_three(int fd) { |
| 934 | if (fd < 0 || fd > STDERR_FILENO) { |
no test coverage detected