@@ -2624,18 +2624,12 @@ def test_execve_invalid_env(self):
26242624 with self .assertRaises (ValueError ):
26252625 os .execve (args [0 ], args , newenv )
26262626
2627- def test_execve_env_concurrent_mutation_with_fspath (self ):
2627+ @unittest .skipIf (os .name == "nt" , "POSIX-specific test" )
2628+ def test_execve_env_concurrent_mutation_with_fspath_posix (self ):
26282629 # Prevent crash when mutating environment during parsing.
26292630 # Regression test for https://github.com/python/cpython/issues/143309.
26302631
2631- if os .name == "nt" :
2632- # See https://github.com/python/cpython/pull/143314
2633- # to understand why we cannot use spaces in strings
2634- # when using subprocess and os.execve() on Windows.
2635- message = "123456"
2636- else :
2637- message = "hello from execve"
2638-
2632+ message = "hello from execve"
26392633 code = """
26402634 import os, sys
26412635
@@ -2647,14 +2641,54 @@ def __fspath__(self):
26472641 mutated = KEYS = VALUES = [MyPath()]
26482642
26492643 class MyEnv:
2644+ def __getitem__(self): raise RuntimeError("must not be called")
26502645 def __len__(self): return 1
2651- def __getitem__(self): return 1
26522646 def keys(self): return KEYS
26532647 def values(self): return VALUES
26542648
26552649 args = [sys.executable, '-c', "print({message!r})"]
26562650 os.execve(args[0], args, MyEnv())
2657- """ .format (message = message )
2651+ """ .format (message = "hello from execve" )
2652+
2653+ # Use '__cleanenv' to signal to assert_python_ok() not
2654+ # to do a copy of os.environ on its own.
2655+ rc , out , _ = assert_python_ok ('-c' , code , __cleanenv = True )
2656+ self .assertEqual (rc , 0 )
2657+ self .assertIn (bytes (message , "ascii" ), out )
2658+
2659+ @unittest .skipUnless (os .name == "nt" , "Windows-specific test" )
2660+ def test_execve_env_concurrent_mutation_with_fspath_posix (self ):
2661+ # See https://github.com/python/cpython/pull/143314
2662+ # to understand why we cannot use spaces in strings
2663+ # when using subprocess and os.execve() on Windows.
2664+ message = "123456"
2665+
2666+ code = """
2667+ import os, sys
2668+
2669+ assert list(os.environ.keys()) == ["SYSTEMROOT"]
2670+ assert list(os.environ.values()) == [{SYSTEMROOT}]
2671+
2672+ class MyPath:
2673+ def __fspath__(self):
2674+ mutated1.clear()
2675+ mutated2.clear()
2676+ return b"pwn"
2677+
2678+ # Python requires "SYSTEMROOT" to be set so we make sure
2679+ # that the side effect happens afterwards.
2680+ mutated1 = KEYS = ["SYSTEMROOT", MyPath()]
2681+ mutated2 = VALUES = [{SYSTEMROOT}, b"ed"]
2682+
2683+ class MyEnv:
2684+ def __getitem__(self): raise RuntimeError("must not be called")
2685+ def __len__(self): return 2
2686+ def keys(self): return KEYS
2687+ def values(self): return VALUES
2688+
2689+ args = [sys.executable, '-c', "print({message!r})"]
2690+ os.execve(args[0], args, MyEnv())
2691+ """ .format (message = message , SYSTEMROOT = os .environ ["SYSTEMROOT" ])
26582692
26592693 # Use '__cleanenv' to signal to assert_python_ok() not
26602694 # to do a copy of os.environ on its own.
0 commit comments