Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
25 changes: 25 additions & 0 deletions lib/matplotlib/tests/test_ft2font.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,31 @@ def test_ft2font_invalid_args(tmp_path):
ft2font.FT2Font(file, _kerning_factor=123)


def test_ft2font_oversized_read():
# A file object whose read() returns *more* bytes than requested is
# misbehaving: FreeType only sizes its buffer for the requested count, so
# an over-long read must be rejected rather than overflow the buffer or be
# silently truncated. Verify that such an object causes construction to
# fail loudly instead.
data = Path(fm.findfont('DejaVu Sans')).read_bytes()

class OversizedReader:
def __init__(self, data):
self._data = data

def seek(self, offset):
pass

def read(self, size):
# Ignore the requested size and hand back the whole file, which is
# far more than FreeType ever asks for. The initial read(0) probe
# in the constructor still gets an empty bytes object.
return self._data if size else b''

with pytest.raises(RuntimeError):
ft2font.FT2Font(OversizedReader(data))


@pytest.mark.parametrize('name, size, skippable',
[('DejaVu Sans', 1, False), ('WenQuanYi Zen Hei', 3, True)])
def test_ft2font_face_index(name, size, skippable):
Expand Down
9 changes: 9 additions & 0 deletions src/ft2font_wrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,15 @@ read_from_file_callback(FT_Stream stream, unsigned long offset, unsigned char *b
if (PyBytes_AsStringAndSize(read_result.ptr(), &tmpbuf, &n_read) == -1) {
throw py::error_already_set();
}
if ((unsigned long)n_read > count) {
Comment thread
uwezkhan marked this conversation as resolved.
// A well-behaved read() never returns more than the requested
// number of bytes. FreeType only ever sized `buffer` for `count`
// bytes, so honoring an over-long read would overflow it. Rather
// than silently truncate (which would feed FreeType corrupt data),
// signal a failed read so that FT_Open_Face -- and in turn the
// FT2Font constructor -- raises.
n_read = 0;
}
memcpy(buffer, tmpbuf, n_read);
} catch (py::error_already_set &eas) {
eas.discard_as_unraisable(__func__);
Expand Down
Loading