ProcessPoolExecutor fails to construct when os.sysconf("SC_SEM_NSEMS_MAX") raises PermissionError
還沒有人認領這個 Issue。
- 主要語言
- Python
- 星號
- 77.2k
- 分支
- 36k
- 平均合併
- 1 天 9 小時
- 30 天內合併 PR
- 558
描述
Bug report
Bug description:
concurrent.futures.ProcessPoolExecutor cannot be constructed on macOS under
any sandbox profile that denies sysctl reads, even though every primitive it
depends on is fully functional in that environment.
_check_system_limits() in Lib/concurrent/futures/process.py reads the
semaphore limit like this:
try:
nsems_max = os.sysconf("SC_SEM_NSEMS_MAX")
except (AttributeError, ValueError):
# sysconf not available or setting not available
return
The handler catches AttributeError and ValueError. It does not catch
OSError. On macOS, SC_SEM_NSEMS_MAX is backed by a sysctl, so a sandbox that
denies sysctl reads makes this call raise PermissionError (an OSError
subclass). The exception propagates out of ProcessPoolExecutor.__init__ before
any worker is created.
The inconsistency is with the function's own stated intent. The existing
except clause already treats "the limit cannot be determined" as benign and
returns — as does the nsems_max == -1 branch immediately below, commented
"indetermined limit, assume that limit is determined by available memory only".
A denied read is the same condition as an unavailable one, arriving as a
different exception type, but it is handled as a fatal error instead.
The practical result is that ProcessPoolExecutor becomes unavailable while
multiprocessing.Pool — same platform, same named semaphores, same spawn
machinery — works correctly. multiprocessing.Pool differs only in that it
never calls _check_system_limits.
This is a robustness bug, not a security issue. The sandbox behaves correctly by
denying the sysctl; the reproduction below denies it explicitly.
Reproduction
repro.py (attached) is standalone, has no third-party dependencies, and does
no monkeypatching. deny_sysctl.sb is a minimal profile that allows everything
except sysctl reads, so the outcome cannot be attributed to any other
restriction:
(version 1)
(allow default)
(deny sysctl-read)
$ python3 repro.py # baseline
$ sandbox-exec -f deny_sysctl.sb python3 repro.py # one capability denied
Actual behaviour
Under (deny sysctl-read) on macOS 26.5.2 (Darwin 25.5.0), CPython 3.13.12:
introspection (queries about a capability)
os.sysconf(SC_SEM_NSEMS_MAX) FAIL PermissionError: [Errno 1] Operation not permitted (errno=1)
_check_system_limits() FAIL PermissionError: [Errno 1] Operation not permitted (errno=1)
direct (exercises the capability)
multiprocessing.Semaphore OK acquire/release
multiprocessing.Process(spawn) OK spawn/join exit=0
multiprocessing.Pool(spawn) OK [1, 4, 9, 16]
concurrent.futures.ProcessPool FAIL PermissionError: [Errno 1] Operation not permitted (errno=1)
Traceback:
File "concurrent/futures/process.py", line ..., in __init__
_check_system_limits()
File "concurrent/futures/process.py", line ..., in _check_system_limits
nsems_max = os.sysconf("SC_SEM_NSEMS_MAX")
PermissionError: [Errno 1] Operation not permitted
Baseline run with no sandbox: all six checks pass.
Expected behaviour
ProcessPoolExecutor should construct and run. An unreadable semaphore limit is
already treated as benign when the read fails for other reasons; a denied read
should be treated the same way.
Suggested fix
Add OSError to the caught exceptions:
try:
nsems_max = os.sysconf("SC_SEM_NSEMS_MAX")
except (AttributeError, ValueError, OSError):
# sysconf not available, setting not available, or the read was denied
# (e.g. a sandbox profile that denies sysctl reads on macOS)
return
This preserves the genuine "too few semaphores" check, which raises
NotImplementedError with an explanatory message and is unaffected. It only
extends the existing "limit cannot be determined" path to cover denial.
If the narrower change is preferred, catching PermissionError alone fixes the
observed case, though any denied sysconf read raises some OSError and the
broader catch matches the comment's intent.
A regression test could patch os.sysconf to raise PermissionError and assert
that ProcessPoolExecutor still constructs — no sandbox required in CI.
Environment
- macOS 26.5.2 (Darwin 25.5.0), arm64
Verified on three interpreters on the same host, all with identical results —
sysconf denied, ProcessPoolExecutor unconstructible, multiprocessing.Pool
working:
| Interpreter | Result |
|---|---|
CPython 3.9.6 (/usr/bin/python3, Apple system Python) |
reproduces |
| CPython 3.12.13 | reproduces |
| CPython 3.13.12 (python.org framework build) | reproduces |
The handler predates all three, so this is longstanding rather than a
regression. Note that the version shipped with macOS is affected.
CPython versions tested on:
3.12, 3.13
Operating systems tested on:
macOS
Linked PRs
- gh-155955
- gh-156303
貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
研究方向
從 Lib/concurrent/futures/process.py 和 _check_system_limits() 開始,然後檢查現有的 concurrent.futures 行程測試。當遭拒的 sysconf 讀取不再阻止建構 ProcessPoolExecutor,且為 PermissionError 提供回歸測試涵蓋時,即表示完成;相關的 PR gh-155955 和 gh-156303 顯示這項工作已經在進行中。
由索引模型根據 Issue 內容生成。
評估
- 技術堆疊
- python
- 領域
- operating-systems
- Issue 類型
- 缺陷
- 難度
- 2/5
- 預估耗時
- 1-3 小時
- 活躍度
- 停滯
- 描述清晰度
- 描述清楚
- 新手友好度
- 25/100