mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 05:31:20 +00:00 
			
		
		
		
	[3.14] gh-91153: prevent a crash in bytearray.__setitem__(ind, ...) when ind.__index__ has side-effects (GH-132379) (#136581)
				
					
				
			gh-91153: prevent a crash in `bytearray.__setitem__(ind, ...)` when `ind.__index__` has side-effects (GH-132379)
(cherry picked from commit 5e1e21dee3)
Co-authored-by: Bast <52266665+bast0006@users.noreply.github.com>
Co-authored-by: Bénédikt Tran <10796600+picnixz@users.noreply.github.com>
			
			
This commit is contained in:
		
							parent
							
								
									c71ecd1418
								
							
						
					
					
						commit
						d5866a8e84
					
				
					 3 changed files with 42 additions and 2 deletions
				
			
		|  | @ -1899,6 +1899,8 @@ def test_repeat_after_setslice(self): | ||||||
|         self.assertEqual(b3, b'xcxcxc') |         self.assertEqual(b3, b'xcxcxc') | ||||||
| 
 | 
 | ||||||
|     def test_mutating_index(self): |     def test_mutating_index(self): | ||||||
|  |         # bytearray slice assignment can call into python code | ||||||
|  |         # that reallocates the internal buffer | ||||||
|         # See gh-91153 |         # See gh-91153 | ||||||
| 
 | 
 | ||||||
|         class Boom: |         class Boom: | ||||||
|  | @ -1916,6 +1918,39 @@ def __index__(self): | ||||||
|             with self.assertRaises(IndexError): |             with self.assertRaises(IndexError): | ||||||
|                 self._testlimitedcapi.sequence_setitem(b, 0, Boom()) |                 self._testlimitedcapi.sequence_setitem(b, 0, Boom()) | ||||||
| 
 | 
 | ||||||
|  |     def test_mutating_index_inbounds(self): | ||||||
|  |         # gh-91153 continued | ||||||
|  |         # Ensure buffer is not broken even if length is correct | ||||||
|  | 
 | ||||||
|  |         class MutatesOnIndex: | ||||||
|  |             def __init__(self): | ||||||
|  |                 self.ba = bytearray(0x180) | ||||||
|  | 
 | ||||||
|  |             def __index__(self): | ||||||
|  |                 self.ba.clear() | ||||||
|  |                 self.new_ba = bytearray(0x180)  # to catch out-of-bounds writes | ||||||
|  |                 self.ba.extend([0] * 0x180)     # to check bounds checks | ||||||
|  |                 return 0 | ||||||
|  | 
 | ||||||
|  |         with self.subTest("skip_bounds_safety"): | ||||||
|  |             instance = MutatesOnIndex() | ||||||
|  |             instance.ba[instance] = ord("?") | ||||||
|  |             self.assertEqual(instance.ba[0], ord("?"), "Assigned bytearray not altered") | ||||||
|  |             self.assertEqual(instance.new_ba, bytearray(0x180), "Wrong object altered") | ||||||
|  | 
 | ||||||
|  |         with self.subTest("skip_bounds_safety_capi"): | ||||||
|  |             instance = MutatesOnIndex() | ||||||
|  |             instance.ba[instance] = ord("?") | ||||||
|  |             self._testlimitedcapi.sequence_setitem(instance.ba, instance, ord("?")) | ||||||
|  |             self.assertEqual(instance.ba[0], ord("?"), "Assigned bytearray not altered") | ||||||
|  |             self.assertEqual(instance.new_ba, bytearray(0x180), "Wrong object altered") | ||||||
|  | 
 | ||||||
|  |         with self.subTest("skip_bounds_safety_slice"): | ||||||
|  |             instance = MutatesOnIndex() | ||||||
|  |             instance.ba[instance:1] = [ord("?")] | ||||||
|  |             self.assertEqual(instance.ba[0], ord("?"), "Assigned bytearray not altered") | ||||||
|  |             self.assertEqual(instance.new_ba, bytearray(0x180), "Wrong object altered") | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| class AssortedBytesTest(unittest.TestCase): | class AssortedBytesTest(unittest.TestCase): | ||||||
|     # |     # | ||||||
|  |  | ||||||
|  | @ -0,0 +1 @@ | ||||||
|  | Fix a crash when a :class:`bytearray` is concurrently mutated during item assignment. | ||||||
|  | @ -709,7 +709,9 @@ bytearray_ass_subscript_lock_held(PyObject *op, PyObject *index, PyObject *value | ||||||
|     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op); |     _Py_CRITICAL_SECTION_ASSERT_OBJECT_LOCKED(op); | ||||||
|     PyByteArrayObject *self = _PyByteArray_CAST(op); |     PyByteArrayObject *self = _PyByteArray_CAST(op); | ||||||
|     Py_ssize_t start, stop, step, slicelen; |     Py_ssize_t start, stop, step, slicelen; | ||||||
|     char *buf = PyByteArray_AS_STRING(self); |     // Do not store a reference to the internal buffer since
 | ||||||
|  |     // index.__index__() or _getbytevalue() may alter 'self'.
 | ||||||
|  |     // See https://github.com/python/cpython/issues/91153.
 | ||||||
| 
 | 
 | ||||||
|     if (_PyIndex_Check(index)) { |     if (_PyIndex_Check(index)) { | ||||||
|         Py_ssize_t i = PyNumber_AsSsize_t(index, PyExc_IndexError); |         Py_ssize_t i = PyNumber_AsSsize_t(index, PyExc_IndexError); | ||||||
|  | @ -744,7 +746,7 @@ bytearray_ass_subscript_lock_held(PyObject *op, PyObject *index, PyObject *value | ||||||
|         } |         } | ||||||
|         else { |         else { | ||||||
|             assert(0 <= ival && ival < 256); |             assert(0 <= ival && ival < 256); | ||||||
|             buf[i] = (char)ival; |             PyByteArray_AS_STRING(self)[i] = (char)ival; | ||||||
|             return 0; |             return 0; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  | @ -805,6 +807,7 @@ bytearray_ass_subscript_lock_held(PyObject *op, PyObject *index, PyObject *value | ||||||
|             /* Delete slice */ |             /* Delete slice */ | ||||||
|             size_t cur; |             size_t cur; | ||||||
|             Py_ssize_t i; |             Py_ssize_t i; | ||||||
|  |             char *buf = PyByteArray_AS_STRING(self); | ||||||
| 
 | 
 | ||||||
|             if (!_canresize(self)) |             if (!_canresize(self)) | ||||||
|                 return -1; |                 return -1; | ||||||
|  | @ -845,6 +848,7 @@ bytearray_ass_subscript_lock_held(PyObject *op, PyObject *index, PyObject *value | ||||||
|             /* Assign slice */ |             /* Assign slice */ | ||||||
|             Py_ssize_t i; |             Py_ssize_t i; | ||||||
|             size_t cur; |             size_t cur; | ||||||
|  |             char *buf = PyByteArray_AS_STRING(self); | ||||||
| 
 | 
 | ||||||
|             if (needed != slicelen) { |             if (needed != slicelen) { | ||||||
|                 PyErr_Format(PyExc_ValueError, |                 PyErr_Format(PyExc_ValueError, | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Miss Islington (bot)
						Miss Islington (bot)