From f0684d22b0d682165160188ae30811679bc17b6e Mon Sep 17 00:00:00 2001 From: Darren Carreras <283775510+carrerasdarren-cell@users.noreply.github.com> Date: Tue, 1 Sep 2026 14:33:03 -0400 Subject: [PATCH 1/2] GH-51044: Reject read-only readinto destinations Immutable destinations expose a null mutable pointer and can crash the native read path. Validate destination mutability and cover bytes and read-only memoryviews with regression tests. Assisted-by: OpenAI Codex --- python/pyarrow/io.pxi | 2 ++ python/pyarrow/tests/test_io.py | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/python/pyarrow/io.pxi b/python/pyarrow/io.pxi index b648fbf66980..32f7bc26b1ff 100644 --- a/python/pyarrow/io.pxi +++ b/python/pyarrow/io.pxi @@ -548,6 +548,8 @@ cdef class NativeFile(_Weakrefable): handle = self.get_input_stream() py_buf = py_buffer(b) + if not py_buf.buffer.get().is_mutable(): + raise TypeError("readinto() argument must be a writable buffer") buf_len = py_buf.size buf = py_buf.buffer.get().mutable_data() diff --git a/python/pyarrow/tests/test_io.py b/python/pyarrow/tests/test_io.py index 8494a0ee66b4..0dfe58258e25 100644 --- a/python/pyarrow/tests/test_io.py +++ b/python/pyarrow/tests/test_io.py @@ -217,6 +217,13 @@ def test_python_file_readinto(): assert len(dst_buf) == length +@pytest.mark.parametrize("dst_buf", [b"a", memoryview(b"a")]) +def test_native_file_readinto_rejects_readonly_buffer(dst_buf): + with pa.BufferReader(b"x") as f: + with pytest.raises(TypeError, match="writable buffer"): + f.readinto(dst_buf) + + def test_python_file_read_buffer(): length = 10 data = b'0123456798' From 6fbd4a0a83c3aff0474702bcdc45d2de3dd85cf5 Mon Sep 17 00:00:00 2001 From: Darren Carreras <283775510+carrerasdarren-cell@users.noreply.github.com> Date: Wed, 9 Sep 2026 05:13:15 -0400 Subject: [PATCH 2/2] GH-51044: [Python] Group readinto buffer test with buffer tests Move the existing decorated test immediately after test_nativefile_write_memoryview, as requested in review. AI-assisted mechanical relocation only; test logic and decorators are unchanged. Checked Python syntax, statement and byte equivalence, and whitespace. Runtime tests were not rerun for this ordering change. --- python/pyarrow/tests/test_io.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/python/pyarrow/tests/test_io.py b/python/pyarrow/tests/test_io.py index 0dfe58258e25..2cebaabbcbdd 100644 --- a/python/pyarrow/tests/test_io.py +++ b/python/pyarrow/tests/test_io.py @@ -217,13 +217,6 @@ def test_python_file_readinto(): assert len(dst_buf) == length -@pytest.mark.parametrize("dst_buf", [b"a", memoryview(b"a")]) -def test_native_file_readinto_rejects_readonly_buffer(dst_buf): - with pa.BufferReader(b"x") as f: - with pytest.raises(TypeError, match="writable buffer"): - f.readinto(dst_buf) - - def test_python_file_read_buffer(): length = 10 data = b'0123456798' @@ -1031,6 +1024,13 @@ def test_nativefile_write_memoryview(): assert buf.to_pybytes() == data * 3 +@pytest.mark.parametrize("dst_buf", [b"a", memoryview(b"a")]) +def test_native_file_readinto_rejects_readonly_buffer(dst_buf): + with pa.BufferReader(b"x") as f: + with pytest.raises(TypeError, match="writable buffer"): + f.readinto(dst_buf) + + # ---------------------------------------------------------------------- # Mock output stream