From e50cd5b3da9258cfd04383e5adebd6b855eaacbe Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Thu, 6 Jun 2019 22:23:47 -0400 Subject: [PATCH 1/5] MNT: protect from out-of-bounds data access at the c level As suggested by @cgohlke --- lib/matplotlib/tests/test_backend_tk.py | 30 +++++++++++++++++++++++++ src/_tkagg.cpp | 6 +++++ 2 files changed, 36 insertions(+) create mode 100644 lib/matplotlib/tests/test_backend_tk.py diff --git a/lib/matplotlib/tests/test_backend_tk.py b/lib/matplotlib/tests/test_backend_tk.py new file mode 100644 index 000000000000..3078d87bf1b2 --- /dev/null +++ b/lib/matplotlib/tests/test_backend_tk.py @@ -0,0 +1,30 @@ +import pytest +import numpy as np +from matplotlib import pyplot as plt +from matplotlib.backends import _tkagg + + +@pytest.mark.backend('TkAgg') +def test_blit(): + def evil_blit(photoimage, aggimage, offsets, bboxptr): + data = np.asarray(aggimage) + height, width = data.shape[:2] + dataptr = (height, width, data.ctypes.data) + _tkagg.blit( + photoimage.tk.interpaddr(), str(photoimage), dataptr, offsets, + bboxptr) + + fig, ax = plt.subplots() + for bad_boxes in ((-1, 2, 0, 2), + (2, 0, 0, 2), + (1, 6, 0, 2), + (0, 2, -1, 2), + (0, 2, 2, 0), + (0, 2, 1, 6), + ): + with pytest.raises(ValueError): + print(bad_boxes) + evil_blit(fig.canvas._tkphoto, + np.ones((4, 4, 4)), + (0, 1, 2, 3), + bad_boxes) diff --git a/src/_tkagg.cpp b/src/_tkagg.cpp index 760b4b0efd84..89fa629531c7 100644 --- a/src/_tkagg.cpp +++ b/src/_tkagg.cpp @@ -67,6 +67,12 @@ static PyObject *mpl_tk_blit(PyObject *self, PyObject *args) PyErr_SetString(PyExc_ValueError, "Failed to extract Tk_PhotoHandle"); goto exit; } + if (0 > y1 || y1 > y2 || y2 > height || + 0 > x1 || x1 > x2 || x2 > width ) { + PyErr_SetString(PyExc_ValueError, "Attempting to draw out of bounds"); + goto exit; + } + block.pixelPtr = data_ptr + 4 * ((height - y2) * width + x1); block.width = x2 - x1; block.height = y2 - y1; From 9d1b200c8b8d56d5e68b1bf05436e94a63dd3b83 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 10 Jun 2019 00:01:33 -0400 Subject: [PATCH 2/5] MNT: remove print + pep8 --- lib/matplotlib/tests/test_backend_tk.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/matplotlib/tests/test_backend_tk.py b/lib/matplotlib/tests/test_backend_tk.py index 3078d87bf1b2..7a6eaf2065d5 100644 --- a/lib/matplotlib/tests/test_backend_tk.py +++ b/lib/matplotlib/tests/test_backend_tk.py @@ -20,10 +20,8 @@ def evil_blit(photoimage, aggimage, offsets, bboxptr): (1, 6, 0, 2), (0, 2, -1, 2), (0, 2, 2, 0), - (0, 2, 1, 6), - ): + (0, 2, 1, 6)): with pytest.raises(ValueError): - print(bad_boxes) evil_blit(fig.canvas._tkphoto, np.ones((4, 4, 4)), (0, 1, 2, 3), From 44aa9236fbd39c6f70d201d6ea3a24afc16723e9 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 10 Jun 2019 00:02:11 -0400 Subject: [PATCH 3/5] STY: reformat c++ conditional --- src/_tkagg.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/_tkagg.cpp b/src/_tkagg.cpp index 89fa629531c7..2e5167ac8ff5 100644 --- a/src/_tkagg.cpp +++ b/src/_tkagg.cpp @@ -67,8 +67,7 @@ static PyObject *mpl_tk_blit(PyObject *self, PyObject *args) PyErr_SetString(PyExc_ValueError, "Failed to extract Tk_PhotoHandle"); goto exit; } - if (0 > y1 || y1 > y2 || y2 > height || - 0 > x1 || x1 > x2 || x2 > width ) { + if (0 > y1 || y1 > y2 || y2 > height || 0 > x1 || x1 > x2 || x2 > width) { PyErr_SetString(PyExc_ValueError, "Attempting to draw out of bounds"); goto exit; } From 774f5581bcc98aaaf0534e3f23c1a892ff742a1d Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 10 Jun 2019 00:10:42 -0400 Subject: [PATCH 4/5] TST: fail gracefully if backend can not be imported - due to missing tk libraries - due to qt already being imported --- lib/matplotlib/testing/conftest.py | 4 +++- lib/matplotlib/tests/test_backend_tk.py | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/matplotlib/testing/conftest.py b/lib/matplotlib/testing/conftest.py index 04f6e7048b26..a93f705af092 100644 --- a/lib/matplotlib/testing/conftest.py +++ b/lib/matplotlib/testing/conftest.py @@ -26,6 +26,8 @@ def mpl_test_settings(request): assert len(backend_marker.args) == 1, \ "Marker 'backend' must specify 1 backend." backend, = backend_marker.args + skip_on_importerror = backend_marker.kwargs.get( + 'skip_on_importerror', False) prev_backend = matplotlib.get_backend() style = '_classic_test' # Default of cleanup and image_comparison too. @@ -45,7 +47,7 @@ def mpl_test_settings(request): except ImportError as exc: # Should only occur for the cairo backend tests, if neither # pycairo nor cairocffi are installed. - if 'cairo' in backend.lower(): + if 'cairo' in backend.lower() or skip_on_importerror: pytest.skip("Failed to switch to backend {} ({})." .format(backend, exc)) else: diff --git a/lib/matplotlib/tests/test_backend_tk.py b/lib/matplotlib/tests/test_backend_tk.py index 7a6eaf2065d5..b7a0145a3668 100644 --- a/lib/matplotlib/tests/test_backend_tk.py +++ b/lib/matplotlib/tests/test_backend_tk.py @@ -4,7 +4,7 @@ from matplotlib.backends import _tkagg -@pytest.mark.backend('TkAgg') +@pytest.mark.backend('TkAgg', skip_on_importerror=True) def test_blit(): def evil_blit(photoimage, aggimage, offsets, bboxptr): data = np.asarray(aggimage) From 0f5fe5d3e437ccf7b0b3883d8bc8c037cd61a291 Mon Sep 17 00:00:00 2001 From: Thomas A Caswell Date: Mon, 10 Jun 2019 19:34:55 -0400 Subject: [PATCH 5/5] TST: hide possible unavailable import --- lib/matplotlib/tests/test_backend_tk.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matplotlib/tests/test_backend_tk.py b/lib/matplotlib/tests/test_backend_tk.py index b7a0145a3668..18ffcb40a0b1 100644 --- a/lib/matplotlib/tests/test_backend_tk.py +++ b/lib/matplotlib/tests/test_backend_tk.py @@ -1,11 +1,11 @@ import pytest import numpy as np from matplotlib import pyplot as plt -from matplotlib.backends import _tkagg @pytest.mark.backend('TkAgg', skip_on_importerror=True) def test_blit(): + from matplotlib.backends import _tkagg def evil_blit(photoimage, aggimage, offsets, bboxptr): data = np.asarray(aggimage) height, width = data.shape[:2]