Skip to content

Commit 1a6ba10

Browse files
authored
Merge pull request matplotlib#31950 from meeseeksmachine/auto-backport-of-pr-31857-on-v3.11.x
Backport PR matplotlib#31857 on branch v3.11.x (bound ft2font stream read to the requested count)
2 parents 969c331 + 70e08de commit 1a6ba10

2 files changed

Lines changed: 34 additions & 0 deletions

File tree

lib/matplotlib/tests/test_ft2font.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,31 @@ def test_ft2font_invalid_args(tmp_path):
203203
ft2font.FT2Font(file, _kerning_factor=123)
204204

205205

206+
def test_ft2font_oversized_read():
207+
# A file object whose read() returns *more* bytes than requested is
208+
# misbehaving: FreeType only sizes its buffer for the requested count, so
209+
# an over-long read must be rejected rather than overflow the buffer or be
210+
# silently truncated. Verify that such an object causes construction to
211+
# fail loudly instead.
212+
data = Path(fm.findfont('DejaVu Sans')).read_bytes()
213+
214+
class OversizedReader:
215+
def __init__(self, data):
216+
self._data = data
217+
218+
def seek(self, offset):
219+
pass
220+
221+
def read(self, size):
222+
# Ignore the requested size and hand back the whole file, which is
223+
# far more than FreeType ever asks for. The initial read(0) probe
224+
# in the constructor still gets an empty bytes object.
225+
return self._data if size else b''
226+
227+
with pytest.raises(RuntimeError):
228+
ft2font.FT2Font(OversizedReader(data))
229+
230+
206231
@pytest.mark.parametrize('name, size, skippable',
207232
[('DejaVu Sans', 1, False), ('WenQuanYi Zen Hei', 3, True)])
208233
def test_ft2font_face_index(name, size, skippable):

src/ft2font_wrapper.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,15 @@ read_from_file_callback(FT_Stream stream, unsigned long offset, unsigned char *b
453453
if (PyBytes_AsStringAndSize(read_result.ptr(), &tmpbuf, &n_read) == -1) {
454454
throw py::error_already_set();
455455
}
456+
if ((unsigned long)n_read > count) {
457+
// A well-behaved read() never returns more than the requested
458+
// number of bytes. FreeType only ever sized `buffer` for `count`
459+
// bytes, so honoring an over-long read would overflow it. Rather
460+
// than silently truncate (which would feed FreeType corrupt data),
461+
// signal a failed read so that FT_Open_Face -- and in turn the
462+
// FT2Font constructor -- raises.
463+
n_read = 0;
464+
}
456465
memcpy(buffer, tmpbuf, n_read);
457466
} catch (py::error_already_set &eas) {
458467
eas.discard_as_unraisable(__func__);

0 commit comments

Comments
 (0)