You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
TypeError: unsupported operand type(s) for +: 'NoneType' and 'list' in subprocess.Popen when using shell=True under strict Java SecurityManager #418
When running Jython within a sandboxed JVM environment that utilizes a strict java.lang.SecurityManager (specifically, one that denies java.io.FilePermission for reading system directories like /bin/),
executing commands via the subprocess module with shell=True (or using wrappers like os.popen or os.system) crashes with a cryptic Python TypeError rather than throwing a descriptive OSError or
Java SecurityException.
Environment
Jython Version: 2.5.x, 2.7.x (including 2.7.3 and current development branch)
OS: Linux/Unix (where _shell_command resolution relies on /bin/sh)
JVM State: Running with a SecurityManager that denies read access to standard system executable paths.
Steps to Reproduce
Run a Jython script that attempts to use os.popen() or subprocess.Popen(..., shell=True) inside a JVM where the SecurityManager denies file read access to /bin/sh.
importsubprocess# This will crash with a TypeErrorsubprocess.Popen(["whoami"], shell=True)
**Stack Trace:**
test_sm.py:16: RuntimeWarning: Unable to determine _shell_command for underlying os: posix
import subprocess
Traceback (most recent call last):
File "test_sm.py", line 17, in <module>
subprocess.Popen(["whoami"], shell=True)
File ".../Lib/subprocess$py.class", line 892, in __init__
File ".../Lib/subprocess$py.class", line 1352, in _execute_child
TypeError: unsupported operand type(s) for +: 'NoneType' and 'list'
Root Cause Analysis
The bug resides in Lib/subprocess.py and stems from how Jython caches the default system shell executable during module initialization.
Initialization (_setup_platform): When the subprocess module is loaded, it attempts to determine the system shell by iterating through known shell paths (e.g., /bin/sh). It validates these paths
using os.path.exists(executable).
SecurityManager Intervention: Because the JVM is sandboxed, os.path.exists("/bin/sh") triggers the SecurityManager, which throws an AccessControlException.
Silent Failure: Jython's os.path.exists() implementation catches this Java security exception and simply returns False.
Invalid State: Because os.path.exists() returns False for all candidate shells, the global variable _shell_command is never assigned a valid list (like ["/bin/sh", "-c"]) and remains
initialized to None.
The Crash (_execute_child and system): Later, when the user invokes subprocess.Popen(..., shell=True) or os.system(), the code attempts to prepend the shell command to the user's arguments:
ifshell:
args=_shell_command+args# <--- CRASH HERE
Because `_shell_command` is `None` and `args` is a list, Python throws the `TypeError`.
Expected Behavior
Instead of a cryptic TypeError about concatenating NoneType and list, Jython should gracefully handle the scenario where a shell cannot be located (whether due to actual absence or SecurityManager
restrictions).
Activity