mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	gh-77377: Ensure multiprocessing SemLock is valid for spawn-based Process before serializing it (#107275)
Ensure multiprocessing SemLock is valid for spawn Process before serializing it. Creating a multiprocessing SemLock with a fork context, and then trying to pass it to a spawn-created Process, would segfault if not detected early. --------- Co-authored-by: blurb-it[bot] <43283697+blurb-it[bot]@users.noreply.github.com> Co-authored-by: Antoine Pitrou <pitrou@free.fr>
This commit is contained in:
		
							parent
							
								
									5d18715765
								
							
						
					
					
						commit
						1700d34d31
					
				
					 3 changed files with 30 additions and 2 deletions
				
			
		|  | @ -50,8 +50,8 @@ class SemLock(object): | ||||||
|     def __init__(self, kind, value, maxvalue, *, ctx): |     def __init__(self, kind, value, maxvalue, *, ctx): | ||||||
|         if ctx is None: |         if ctx is None: | ||||||
|             ctx = context._default_context.get_context() |             ctx = context._default_context.get_context() | ||||||
|         name = ctx.get_start_method() |         self.is_fork_ctx = ctx.get_start_method() == 'fork' | ||||||
|         unlink_now = sys.platform == 'win32' or name == 'fork' |         unlink_now = sys.platform == 'win32' or self.is_fork_ctx | ||||||
|         for i in range(100): |         for i in range(100): | ||||||
|             try: |             try: | ||||||
|                 sl = self._semlock = _multiprocessing.SemLock( |                 sl = self._semlock = _multiprocessing.SemLock( | ||||||
|  | @ -103,6 +103,11 @@ def __getstate__(self): | ||||||
|         if sys.platform == 'win32': |         if sys.platform == 'win32': | ||||||
|             h = context.get_spawning_popen().duplicate_for_child(sl.handle) |             h = context.get_spawning_popen().duplicate_for_child(sl.handle) | ||||||
|         else: |         else: | ||||||
|  |             if self.is_fork_ctx: | ||||||
|  |                 raise RuntimeError('A SemLock created in a fork context is being ' | ||||||
|  |                                    'shared with a process in a spawn context. This is ' | ||||||
|  |                                    'not supported. Please use the same context to create ' | ||||||
|  |                                    'multiprocessing objects and Process.') | ||||||
|             h = sl.handle |             h = sl.handle | ||||||
|         return (h, sl.kind, sl.maxvalue, sl.name) |         return (h, sl.kind, sl.maxvalue, sl.name) | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -5421,6 +5421,28 @@ def test_preload_resources(self): | ||||||
|             print(err) |             print(err) | ||||||
|             self.fail("failed spawning forkserver or grandchild") |             self.fail("failed spawning forkserver or grandchild") | ||||||
| 
 | 
 | ||||||
|  |     @unittest.skipIf(sys.platform == "win32", | ||||||
|  |                      "Only Spawn on windows so no risk of mixing") | ||||||
|  |     @only_run_in_spawn_testsuite("avoids redundant testing.") | ||||||
|  |     def test_mixed_startmethod(self): | ||||||
|  |         # Fork-based locks cannot be used with spawned process | ||||||
|  |         for process_method in ["spawn", "forkserver"]: | ||||||
|  |             queue = multiprocessing.get_context("fork").Queue() | ||||||
|  |             process_ctx = multiprocessing.get_context(process_method) | ||||||
|  |             p = process_ctx.Process(target=close_queue, args=(queue,)) | ||||||
|  |             err_msg = "A SemLock created in a fork" | ||||||
|  |             with self.assertRaisesRegex(RuntimeError, err_msg): | ||||||
|  |                 p.start() | ||||||
|  | 
 | ||||||
|  |         # non-fork-based locks can be used with all other start methods | ||||||
|  |         for queue_method in ["spawn", "forkserver"]: | ||||||
|  |             for process_method in multiprocessing.get_all_start_methods(): | ||||||
|  |                 queue = multiprocessing.get_context(queue_method).Queue() | ||||||
|  |                 process_ctx = multiprocessing.get_context(process_method) | ||||||
|  |                 p = process_ctx.Process(target=close_queue, args=(queue,)) | ||||||
|  |                 p.start() | ||||||
|  |                 p.join() | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| @unittest.skipIf(sys.platform == "win32", | @unittest.skipIf(sys.platform == "win32", | ||||||
|                  "test semantics don't make sense on Windows") |                  "test semantics don't make sense on Windows") | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | Ensure that multiprocessing synchronization objects created in a fork context are not sent to a different process created in a spawn context. This changes a segfault into an actionable RuntimeError in the parent process. | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 albanD
						albanD