Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[3.12] gh-95782: Fix io.BufferedReader.tell() etc. being able to return offsets < 0 (GH-99709)#115599

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletionLib/_pyio.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1209,7 +1209,8 @@ def _readinto(self, buf, read1):
return written

def tell(self):
return _BufferedIOMixin.tell(self) - len(self._read_buf) + self._read_pos
# GH-95782: Keep return value non-negative
return max(_BufferedIOMixin.tell(self) - len(self._read_buf) + self._read_pos, 0)

def seek(self, pos, whence=0):
if whence not in valid_seek_flags:
Expand Down
47 changes: 46 additions & 1 deletionLib/test/test_io.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -263,6 +263,27 @@ class PyMockUnseekableIO(MockUnseekableIO, pyio.BytesIO):
UnsupportedOperation = pyio.UnsupportedOperation


class MockCharPseudoDevFileIO(MockFileIO):
# GH-95782
# ftruncate() does not work on these special files (and CPython then raises
# appropriate exceptions), so truncate() does not have to be accounted for
# here.
def __init__(self, data):
super().__init__(data)

def seek(self, *args):
return 0

def tell(self, *args):
return 0

class CMockCharPseudoDevFileIO(MockCharPseudoDevFileIO, io.BytesIO):
pass

class PyMockCharPseudoDevFileIO(MockCharPseudoDevFileIO, pyio.BytesIO):
pass


class MockNonBlockWriterIO:

def __init__(self):
Expand DownExpand Up@@ -1659,6 +1680,30 @@ def test_truncate_on_read_only(self):
self.assertRaises(self.UnsupportedOperation, bufio.truncate)
self.assertRaises(self.UnsupportedOperation, bufio.truncate, 0)

def test_tell_character_device_file(self):
# GH-95782
# For the (former) bug in BufferedIO to manifest, the wrapped IO obj
# must be able to produce at least 2 bytes.
raw = self.MockCharPseudoDevFileIO(b"12")
buf = self.tp(raw)
self.assertEqual(buf.tell(), 0)
self.assertEqual(buf.read(1), b"1")
self.assertEqual(buf.tell(), 0)

def test_seek_character_device_file(self):
raw = self.MockCharPseudoDevFileIO(b"12")
buf = self.tp(raw)
self.assertEqual(buf.seek(0, io.SEEK_CUR), 0)
self.assertEqual(buf.seek(1, io.SEEK_SET), 0)
self.assertEqual(buf.seek(0, io.SEEK_CUR), 0)
self.assertEqual(buf.read(1), b"1")

# In the C implementation, tell() sets the BufferedIO's abs_pos to 0,
# which means that the next seek() could return a negative offset if it
# does not sanity-check:
self.assertEqual(buf.tell(), 0)
self.assertEqual(buf.seek(0, io.SEEK_CUR), 0)


class CBufferedReaderTest(BufferedReaderTest, SizeofTest):
tp = io.BufferedReader
Expand DownExpand Up@@ -4888,7 +4933,7 @@ def load_tests(loader, tests, pattern):
# classes in the __dict__ of each test.
mocks = (MockRawIO, MisbehavedRawIO, MockFileIO, CloseFailureIO,
MockNonBlockWriterIO, MockUnseekableIO, MockRawIOWithoutRead,
SlowFlushRawIO)
SlowFlushRawIO, MockCharPseudoDevFileIO)
all_members = io.__all__
c_io_ns = {name : getattr(io, name) for name in all_members}
py_io_ns = {name : getattr(pyio, name) for name in all_members}
Expand Down
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
Fix :func:`io.BufferedReader.tell`, :func:`io.BufferedReader.seek`,
:func:`_pyio.BufferedReader.tell`, :func:`io.BufferedRandom.tell`,
:func:`io.BufferedRandom.seek` and :func:`_pyio.BufferedRandom.tell`
being able to return negative offsets.
11 changes: 10 additions & 1 deletionModules/_io/bufferedio.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1286,7 +1286,11 @@ _io__Buffered_tell_impl(buffered *self)
if (pos == -1)
return NULL;
pos -= RAW_OFFSET(self);
/* TODO: sanity check (pos >= 0) */

// GH-95782
if (pos < 0)
pos = 0;

return PyLong_FromOff_t(pos);
}

Expand DownExpand Up@@ -1355,6 +1359,11 @@ _io__Buffered_seek_impl(buffered *self, PyObject *targetobj, int whence)
offset = target;
if (offset >= -self->pos && offset <= avail) {
self->pos += offset;

// GH-95782
if (current - avail + offset < 0)
return PyLong_FromOff_t(0);

return PyLong_FromOff_t(current - avail + offset);
}
}
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp