From 9757e20694cf9d9de75bd8447ba59dd88dff8a45 Mon Sep 17 00:00:00 2001 From: Jesse Yurkovich Date: Sat, 29 Jul 2023 09:50:16 -0700 Subject: [PATCH] fix(python): Fix arithmetic overflow in oiio_bufinfo (Python interop) (#3931) When an incoming 1-dimensional, buffer-like, object was used in Python, the corresponding check inside OIIO would overflow if its length exceeded a signed 32bit value. An error similar to `Pixel data array error: Python array shape is [2304000000] but expecting h=24000, w=24000, ch=4` would be raised. I modified a few similar calculations and tried to follow the predominant pattern already used. Tested using the following snippet which would previously fail: ``` import OpenImageIO as oiio import numpy as np filename = "t:/foo.png" xres = 24000 yres = 24000 channels = 4 # RGB pixels = np.zeros(xres * yres * channels, dtype=np.uint8) spec = oiio.ImageSpec(xres, yres, channels, "uint8") image = oiio.ImageOutput.create(filename) image.open(filename, spec) if not image.write_image(pixels): print("error = ", image.geterror()) image.close() ``` --------- Signed-off-by: Jesse Yurkovich --- src/python/py_oiio.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/python/py_oiio.cpp b/src/python/py_oiio.cpp index d821490dcb..2c00b590b9 100644 --- a/src/python/py_oiio.cpp +++ b/src/python/py_oiio.cpp @@ -114,7 +114,7 @@ oiio_bufinfo::oiio_bufinfo(const py::buffer_info& pybuf, int nchans, int width, zstride = pybuf.strides[0]; } else if (pybuf.ndim == 3 && pybuf.shape[0] == depth && pybuf.shape[1] == height - && pybuf.shape[2] == width * nchans) { + && pybuf.shape[2] == int64_t(width) * int64_t(nchans)) { // passed from python as [z][y][xpixel] -- chans mushed together xstride = pybuf.strides[2]; ystride = pybuf.strides[1]; @@ -133,10 +133,11 @@ oiio_bufinfo::oiio_bufinfo(const py::buffer_info& pybuf, int nchans, int width, } else if (pybuf.ndim == 2) { // Somebody collapsed a dimension. Is it [pixel][c] with x&y // combined, or is it [y][xpixel] with channels mushed together? - if (pybuf.shape[0] == width * height && pybuf.shape[1] == nchans) + if (pybuf.shape[0] == int64_t(width) * int64_t(height) + && pybuf.shape[1] == nchans) xstride = pybuf.strides[0]; else if (pybuf.shape[0] == height - && pybuf.shape[1] == width * nchans) { + && pybuf.shape[1] == int64_t(width) * int64_t(nchans)) { ystride = pybuf.strides[0]; xstride = pybuf.strides[0] * nchans; } else { @@ -146,7 +147,9 @@ oiio_bufinfo::oiio_bufinfo(const py::buffer_info& pybuf, int nchans, int width, pixeldims, pybuf.ndim); } } else if (pybuf.ndim == 1 - && pybuf.shape[0] == height * width * nchans) { + && pybuf.shape[0] + == int64_t(width) * int64_t(height) + * int64_t(nchans)) { // all pixels & channels smushed together // just rely on autostride } else { @@ -161,7 +164,8 @@ oiio_bufinfo::oiio_bufinfo(const py::buffer_info& pybuf, int nchans, int width, && pybuf.shape[1] == nchans) { // passed from python as [x][c] xstride = pybuf.strides[0]; - } else if (pybuf.ndim == 1 && pybuf.shape[0] == width * nchans) { + } else if (pybuf.ndim == 1 + && pybuf.shape[0] == int64_t(width) * int64_t(nchans)) { // all pixels & channels smushed together xstride = pybuf.strides[0] * nchans; } else {