diff --git a/tests/tools/test_find_shell.py b/tests/tools/test_find_shell.py index 4e29eb99c604a..5dd987e9763b9 100644 --- a/tests/tools/test_find_shell.py +++ b/tests/tools/test_find_shell.py @@ -145,6 +145,32 @@ def fake_run(argv, **kwargs): assert local_mod._bash_starts(r"C:\Git\bin\bash.exe") is True assert calls[0][0][-1] == "/usr/bin/true; /usr/bin/cat --version >/dev/null" + def test_probe_never_inherits_host_stdin(self, monkeypatch): + """The probe must not inherit fd 0 (#73693). + + Under a JSON-RPC stdio host — ACP, or the TUI gateway of #14036 — + stdin is the protocol pipe. MSYS bash blocks on it at startup, and + the probe's own timeout only reaps the Git wrapper process, so the + surviving mingw child holds the pipe and hangs the turn. + """ + import tools.environments.local as local_mod + + local_mod._bash_starts_cache.clear() + local_mod._bash_probe_details_cache.clear() + calls = [] + + def fake_run(argv, **kwargs): + calls.append(kwargs) + return subprocess.CompletedProcess(argv, 0, stdout="", stderr="") + + monkeypatch.setattr(local_mod.subprocess, "run", fake_run) + monkeypatch.setattr(local_mod, "_IS_WINDOWS", True) + + local_mod._bash_starts(r"C:\Git\bin\bash.exe") + + assert calls, "expected the probe to spawn bash" + assert calls[0].get("stdin") is subprocess.DEVNULL + def test_aslr_failure_surfaces_targeted_windows_command( self, tmp_path, monkeypatch ): diff --git a/tools/environments/local.py b/tools/environments/local.py index 330d46c53dd6a..9ef49ebd73aa7 100644 --- a/tools/environments/local.py +++ b/tools/environments/local.py @@ -845,6 +845,7 @@ def _mandatory_aslr_enabled() -> "bool | None": "(Get-ProcessMitigation -System).Aslr.ForceRelocateImages.ToString()", ], capture_output=True, + stdin=subprocess.DEVNULL, text=True, encoding="utf-8", errors="replace", timeout=10, creationflags=windows_hide_flags(), @@ -911,6 +912,12 @@ def _bash_starts(bash: str) -> bool: result = subprocess.run( [bash, "--noprofile", "--norc", "-c", _BASH_EXTERNAL_PROGRAM_PROBE], capture_output=True, + # Never let the probe inherit the host's stdin. Under a JSON-RPC + # stdio host (ACP/TUI gateway) fd 0 is the protocol pipe; MSYS + # bash blocks on it at startup, and the timeout below then kills + # only the Git\bin wrapper — the surviving mingw child keeps the + # pipe open and communicate() hangs the turn. See #73693. + stdin=subprocess.DEVNULL, text=True, encoding="utf-8", errors="replace", timeout=15, creationflags=windows_hide_flags() if _IS_WINDOWS else 0,