Skip to content

Commit 3952cfe

Browse files
authored
Merge branch 'main' into fix/sqlite/uaf-in-cursor-143198
2 parents cc0f483 + 9976c2b commit 3952cfe

File tree

11 files changed

+161
-19
lines changed

11 files changed

+161
-19
lines changed

Doc/library/mmap.rst

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ To map anonymous memory, -1 should be passed as the fileno along with the length
212212
Writable :term:`bytes-like object` is now accepted.
213213

214214

215-
.. method:: flush([offset[, size]])
215+
.. method:: flush([offset[, size]], *, flags=MS_SYNC)
216216

217217
Flushes changes made to the in-memory copy of a file back to disk. Without
218218
use of this call there is no guarantee that changes are written back before
@@ -221,6 +221,12 @@ To map anonymous memory, -1 should be passed as the fileno along with the length
221221
whole extent of the mapping is flushed. *offset* must be a multiple of the
222222
:const:`PAGESIZE` or :const:`ALLOCATIONGRANULARITY`.
223223

224+
The *flags* parameter specifies the synchronization behavior.
225+
*flags* must be one of the :ref:`MS_* constants <ms-constants>` available
226+
on the system.
227+
228+
On Windows, the *flags* parameter is ignored.
229+
224230
``None`` is returned to indicate success. An exception is raised when the
225231
call failed.
226232

@@ -235,6 +241,9 @@ To map anonymous memory, -1 should be passed as the fileno along with the length
235241
specified alone, and the flush operation will extend from *offset*
236242
to the end of the mmap.
237243

244+
.. versionchanged:: next
245+
Added *flags* parameter to control synchronization behavior.
246+
238247

239248
.. method:: madvise(option[, start[, length]])
240249

@@ -461,3 +470,22 @@ MAP_* Constants
461470
:data:`MAP_TPRO`, :data:`MAP_TRANSLATED_ALLOW_EXECUTE`, and
462471
:data:`MAP_UNIX03` constants.
463472

473+
.. _ms-constants:
474+
475+
MS_* Constants
476+
++++++++++++++
477+
478+
.. data:: MS_SYNC
479+
MS_ASYNC
480+
MS_INVALIDATE
481+
482+
These flags control the synchronization behavior for :meth:`mmap.flush`:
483+
484+
* :data:`MS_SYNC` - Synchronous flush: writes are scheduled and the call
485+
blocks until they are physically written to storage.
486+
* :data:`MS_ASYNC` - Asynchronous flush: writes are scheduled but the call
487+
returns immediately without waiting for completion.
488+
* :data:`MS_INVALIDATE` - Invalidate cached data: invalidates other mappings
489+
of the same file so they can see the changes.
490+
491+
.. versionadded:: next

Doc/library/os.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5993,7 +5993,7 @@ Miscellaneous System Information
59935993

59945994
.. versionchanged:: 3.13
59955995
If :option:`-X cpu_count <-X>` is given or :envvar:`PYTHON_CPU_COUNT` is set,
5996-
:func:`cpu_count` returns the overridden value *n*.
5996+
:func:`cpu_count` returns the override value *n*.
59975997

59985998

59995999
.. function:: getloadavg()
@@ -6015,7 +6015,7 @@ Miscellaneous System Information
60156015
in the **system**.
60166016

60176017
If :option:`-X cpu_count <-X>` is given or :envvar:`PYTHON_CPU_COUNT` is set,
6018-
:func:`process_cpu_count` returns the overridden value *n*.
6018+
:func:`process_cpu_count` returns the override value *n*.
60196019

60206020
See also the :func:`sched_getaffinity` function.
60216021

Lib/test/test_bytes.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2092,6 +2092,19 @@ def make_case():
20922092
with self.assertRaises(BufferError):
20932093
ba.rsplit(evil)
20942094

2095+
def test_hex_use_after_free(self):
2096+
# Prevent UAF in bytearray.hex(sep) with re-entrant sep.__len__.
2097+
# Regression test for https://github.com/python/cpython/issues/143195.
2098+
ba = bytearray(b'\xAA')
2099+
2100+
class S(bytes):
2101+
def __len__(self):
2102+
ba.clear()
2103+
return 1
2104+
2105+
self.assertRaises(BufferError, ba.hex, S(b':'))
2106+
2107+
20952108
class AssortedBytesTest(unittest.TestCase):
20962109
#
20972110
# Test various combinations of bytes and bytearray

Lib/test/test_memoryview.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,20 @@ def test_issue22668(self):
442442
self.assertEqual(c.format, "H")
443443
self.assertEqual(d.format, "H")
444444

445+
def test_hex_use_after_free(self):
446+
# Prevent UAF in memoryview.hex(sep) with re-entrant sep.__len__.
447+
# Regression test for https://github.com/python/cpython/issues/143195.
448+
ba = bytearray(b'A' * 1024)
449+
mv = memoryview(ba)
450+
451+
class S(bytes):
452+
def __len__(self):
453+
mv.release()
454+
ba.clear()
455+
return 1
456+
457+
self.assertRaises(BufferError, mv.hex, S(b':'))
458+
445459

446460
# Variations on source objects for the buffer: bytes-like objects, then arrays
447461
# with itemsize > 1.

Lib/test/test_mmap.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,6 +1166,15 @@ def test_flush_parameters(self):
11661166
m.flush(PAGESIZE)
11671167
m.flush(PAGESIZE, PAGESIZE)
11681168

1169+
if hasattr(mmap, 'MS_SYNC'):
1170+
m.flush(0, PAGESIZE, flags=mmap.MS_SYNC)
1171+
if hasattr(mmap, 'MS_ASYNC'):
1172+
m.flush(flags=mmap.MS_ASYNC)
1173+
if hasattr(mmap, 'MS_INVALIDATE'):
1174+
m.flush(PAGESIZE * 2, flags=mmap.MS_INVALIDATE)
1175+
if hasattr(mmap, 'MS_ASYNC') and hasattr(mmap, 'MS_INVALIDATE'):
1176+
m.flush(0, PAGESIZE, flags=mmap.MS_ASYNC | mmap.MS_INVALIDATE)
1177+
11691178
@unittest.skipUnless(sys.platform == 'linux', 'Linux only')
11701179
@support.requires_linux_version(5, 17, 0)
11711180
def test_set_name(self):
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add a ``flags`` parameter to :meth:`mmap.mmap.flush` to control synchronization behavior.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Fix use-after-free crashes in :meth:`bytearray.hex` and :meth:`memoryview.hex`
2+
when the separator's :meth:`~object.__len__` mutates the original object.
3+
Patch by Bénédikt Tran.

Modules/clinic/mmapmodule.c.h

Lines changed: 57 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/mmapmodule.c

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,12 +1034,15 @@ mmap.mmap.flush
10341034
offset: Py_ssize_t = 0
10351035
size: Py_ssize_t = -1
10361036
/
1037+
*
1038+
flags: int = 0
10371039
10381040
[clinic start generated code]*/
10391041

10401042
static PyObject *
1041-
mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size)
1042-
/*[clinic end generated code: output=956ced67466149cf input=c50b893bc69520ec]*/
1043+
mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size,
1044+
int flags)
1045+
/*[clinic end generated code: output=4225f4174dc75a53 input=42ba5fb716b6c294]*/
10431046
{
10441047
CHECK_VALID(NULL);
10451048
if (size == -1) {
@@ -1060,8 +1063,10 @@ mmap_mmap_flush_impl(mmap_object *self, Py_ssize_t offset, Py_ssize_t size)
10601063
}
10611064
Py_RETURN_NONE;
10621065
#elif defined(UNIX)
1063-
/* XXX flags for msync? */
1064-
if (-1 == msync(self->data + offset, size, MS_SYNC)) {
1066+
if (flags == 0) {
1067+
flags = MS_SYNC;
1068+
}
1069+
if (-1 == msync(self->data + offset, size, flags)) {
10651070
PyErr_SetFromErrno(PyExc_OSError);
10661071
return NULL;
10671072
}
@@ -2331,6 +2336,16 @@ mmap_exec(PyObject *module)
23312336
ADD_INT_MACRO(module, ACCESS_WRITE);
23322337
ADD_INT_MACRO(module, ACCESS_COPY);
23332338

2339+
#ifdef MS_INVALIDATE
2340+
ADD_INT_MACRO(module, MS_INVALIDATE);
2341+
#endif
2342+
#ifdef MS_ASYNC
2343+
ADD_INT_MACRO(module, MS_ASYNC);
2344+
#endif
2345+
#ifdef MS_SYNC
2346+
ADD_INT_MACRO(module, MS_SYNC);
2347+
#endif
2348+
23342349
#ifdef HAVE_MADVISE
23352350
// Conventional advice values
23362351
#ifdef MADV_NORMAL

Objects/bytearrayobject.c

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2664,7 +2664,13 @@ bytearray_hex_impl(PyByteArrayObject *self, PyObject *sep, int bytes_per_sep)
26642664
{
26652665
char* argbuf = PyByteArray_AS_STRING(self);
26662666
Py_ssize_t arglen = PyByteArray_GET_SIZE(self);
2667-
return _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
2667+
// Prevent 'self' from being freed if computing len(sep) mutates 'self'
2668+
// in _Py_strhex_with_sep().
2669+
// See: https://github.com/python/cpython/issues/143195.
2670+
self->ob_exports++;
2671+
PyObject *res = _Py_strhex_with_sep(argbuf, arglen, sep, bytes_per_sep);
2672+
self->ob_exports--;
2673+
return res;
26682674
}
26692675

26702676
static PyObject *

0 commit comments

Comments
 (0)