Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix segfault when passing empty StringIO/File object to Font #2548

Merged
merged 10 commits into from
Nov 6, 2023
16 changes: 15 additions & 1 deletion src_c/_freetype.c
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ _ftfont_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
obj->bgcolor[1] = 0;
obj->bgcolor[2] = 0;
obj->bgcolor[3] = 0;
obj->init_generation = current_freetype_generation;
}
return (PyObject *)obj;
}
Expand Down Expand Up @@ -783,6 +784,13 @@ _ftfont_init(pgFontObject *self, PyObject *args, PyObject *kwds)
goto end;
}

if (source->size(source) <= 0) {
PyErr_Format(PyExc_ValueError,
"Font file object has an invalid file size: %lld",
source->size(source));
goto end;
}

path = PyObject_GetAttrString(original_file, "name");
if (!path) {
PyErr_Clear();
Expand Down Expand Up @@ -827,6 +835,13 @@ _ftfont_init(pgFontObject *self, PyObject *args, PyObject *kwds)
goto end;
}

if (source->size(source) <= 0) {
PyErr_Format(PyExc_ValueError,
"Font file object has an invalid file size: %lld",
source->size(source));
goto end;
}

PyObject *path = NULL;
if (pgRWops_IsFileObject(source)) {
path = PyObject_GetAttrString(file, "name");
Expand Down Expand Up @@ -890,7 +905,6 @@ _ftfont_init(pgFontObject *self, PyObject *args, PyObject *kwds)
*/
self->freetype = ft;
++ft->ref_count;
self->init_generation = current_freetype_generation;

rval = 0;

Expand Down
7 changes: 7 additions & 0 deletions src_c/font.c
Original file line number Diff line number Diff line change
Expand Up @@ -1184,6 +1184,13 @@ font_init(PyFontObject *self, PyObject *args, PyObject *kwds)
if (fontsize <= 1)
fontsize = 1;

if (rw->size(rw) <= 0) {
PyErr_Format(PyExc_ValueError,
"Font file object has an invalid file size: %lld",
rw->size(rw));
goto error;
}

Py_BEGIN_ALLOW_THREADS;
font = TTF_OpenFontRW(rw, 1, fontsize);
Py_END_ALLOW_THREADS;
Expand Down
7 changes: 7 additions & 0 deletions test/font_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from re import T
import sys
import os
import io
import unittest
import pathlib
import platform
Expand Down Expand Up @@ -708,6 +709,12 @@ def test_load_from_file_obj_default(self):
with open(font_path, "rb") as f:
font = pygame_font.Font(f)

def test_load_from_invalid_sized_file_obj(self):
pygame.font.init()
f = io.StringIO()
with self.assertRaises(ValueError):
font = pygame.font.Font(f)

def test_load_default_font_filename(self):
# In font_init, a special case is when the filename argument is
# identical to the default font file name.
Expand Down
16 changes: 13 additions & 3 deletions test/freetype_test.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os

import io
import unittest
import ctypes
import weakref
Expand Down Expand Up @@ -158,6 +158,11 @@ def test_freetype_Font_init(self):
f.__init__(self._bmp_8_75dpi_path, size=12)
self.assertEqual(f.size, 12.0)

def test_load_from_invalid_sized_file_obj(self):
f = io.StringIO()
with self.assertRaises(ValueError):
font = f = ft.Font(f, size=24)

@unittest.skipIf(IS_PYPY, "PyPy doesn't use refcounting")
def test_freetype_Font_dealloc(self):
import sys
Expand Down Expand Up @@ -544,7 +549,9 @@ def test_freetype_Font_name(self):
f = self._TEST_FONTS["fixed"]
self.assertEqual(f.name, "Inconsolata")

self.assertRaises(RuntimeError, lambda: nullfont().name)
with self.assertRaises(AttributeError):
null_font = ft.Font.__new__(ft.Font)
null_font.name

def test_freetype_Font_size(self):
f = ft.Font(None, size=12)
Expand Down Expand Up @@ -1349,7 +1356,10 @@ def test_freetype_Font_resolution(self):

def test_freetype_Font_path(self):
self.assertEqual(self._TEST_FONTS["sans"].path, self._sans_path)
self.assertRaises(pygame.error, lambda: nullfont().path)

with self.assertRaises(AttributeError):
nullfont = ft.Font.__new__(ft.Font)
nullfont.path
ankith26 marked this conversation as resolved.
Show resolved Hide resolved

# This Font cache test is conditional on freetype being built by a debug
# version of Python or with the C macro PGFT_DEBUG_CACHE defined.
Expand Down
Loading