From 20361e92389144552545f6db69bfaf8a5782b0c6 Mon Sep 17 00:00:00 2001 From: Lincoln Puzey Date: Sun, 31 Jan 2021 15:58:19 +0800 Subject: [PATCH] Fixed win32print.DeviceCapabilities() returning strings with junk in them. Previously this code would ZeroMemory() the output buffer before passing it to DeviceCapabilities(), and then assume that only characters in each string would be changed, i.e. the characters following the null-terminator would remain null. So it would only check if the last character in each string was null, to check if the string was null-terminated. This assumption is wrong - sometimes DeviceCapabilities() sets the characters after null to junk. Now every character in the returned buffer is checked for being null before falling back to the case where the string takes up all available space. https://github.com/mhammond/pywin32/issues/1654 --- CHANGES.txt | 4 ++++ win32/src/win32print/win32print.cpp | 22 ++++++++++++++++++---- 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index c8116259e..0fbad591b 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -12,6 +12,10 @@ Since build 300: * Shifted work in win32.lib.pywin32_bootstrap to Python's import system from manual path manipulations (@wkschwartz in #1651) +* Fixed a bug where win32print.DeviceCapabilities would return strings + containing the null character followed by junk characters. + (#1654, #1660, Lincoln Puzey) + Since build 228: ---------------- * Fixed a bug where win32com.client.VARIANT params were returned in the reverse diff --git a/win32/src/win32print/win32print.cpp b/win32/src/win32print/win32print.cpp index f6e730563..301eca940 100644 --- a/win32/src/win32print/win32print.cpp +++ b/win32/src/win32print/win32print.cpp @@ -1887,7 +1887,7 @@ static PyObject *PyDeviceCapabilities(PyObject *self, PyObject *args) static DWORD papernamesize = 64; // same for DC_PAPERNAMES, DC_MEDIATYPENAMES, DC_MEDIAREADY, DC_FILEDEPENDENCIES static DWORD binnamesize = 24; // DC_BINNAMES static DWORD personalitysize = 32; // DC_PERSONALITY - DWORD retsize; + DWORD retsize, retsize_index; if (!PyArg_ParseTuple(args, "OOh|O:DeviceCapabilities", &obdevice, &obport, &capability, &obdevmode)) return NULL; @@ -2048,19 +2048,33 @@ static PyObject *PyDeviceCapabilities(PyObject *self, PyObject *args) PyErr_Format(PyExc_MemoryError, "DeviceCapabilites: Unable to allocate %d bytes", bufsize); break; } - ZeroMemory(buf, bufsize); result = DeviceCapabilities(device, port, capability, buf, pdevmode); + // each string in buf can be null-terminated or take up all space and have length retsize. + // bytes after null character can be set to junk by DeviceCapabilities(). if (result == -1) break; ret = PyTuple_New(result); if (ret == NULL) break; retname = (TCHAR *)buf; + // process each returned string for (bufindex = 0; bufindex < result; bufindex++) { - if (*(retname + retsize - 1) == 0) + // check if returned string contains null character + bool contains_null = false; + for (retsize_index = 0; retsize_index < retsize; retsize_index++) { + if (*(retname + retsize_index) == 0) { + contains_null = true; + break; + } + } + if (contains_null) { + // string will be automatically terminated at null character. tuple_item = PyWinObject_FromTCHAR(retname); - else // won't be null-terminated if string occupies entire space + } + else { + // specify string length. tuple_item = PyWinObject_FromTCHAR(retname, retsize); + } if (tuple_item == NULL) { Py_DECREF(ret); ret = NULL;