From 887f87637cbebaa124ab0431b0df6a8ab45d1de9 Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Wed, 25 Aug 2021 20:37:44 -0400 Subject: [PATCH 1/6] properly handle unsigned long win32 error codes fixes #28474 --- Lib/ctypes/__init__.py | 4 +++- Lib/ctypes/test/test_win32.py | 20 ++++++++++++++++++++ Lib/test/test_exceptions.py | 8 ++++++++ Modules/_ctypes/callproc.c | 13 ++++++++++--- Objects/exceptions.c | 14 ++++++++++---- 5 files changed, 51 insertions(+), 8 deletions(-) diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index b08629e8df4dfd..1f8d4d0ee2d354 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -469,7 +469,9 @@ def LoadLibrary(self, name): def WinError(code=None, descr=None): if code is None: - code = GetLastError() + # GetLastError returns a DWORD, cast large error codes + # to unsigned + code = GetLastError() & 0xffffffff if descr is None: descr = FormatError(code).strip() return OSError(None, descr, None, code) diff --git a/Lib/ctypes/test/test_win32.py b/Lib/ctypes/test/test_win32.py index e51bdc8ad6b071..abbac815a06326 100644 --- a/Lib/ctypes/test/test_win32.py +++ b/Lib/ctypes/test/test_win32.py @@ -90,6 +90,26 @@ def test_winerror(self): self.assertEqual(e.errno, errno.EINVAL) self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER) + def test_winerror_dword(self): + # see Issue 28474 + E_POINTER = 0x80000005 + msg = FormatError(E_POINTER).strip() + args = (E_POINTER, msg, None, E_POINTER) + + e = WinError(E_POINTER) + self.assertEqual(e.args, args) + self.assertEqual(e.errno, E_POINTER) + self.assertEqual(e.winerror, E_POINTER) + + windll.kernel32.SetLastError(E_POINTER) + try: + raise WinError() + except OSError as exc: + e = exc + self.assertEqual(e.args, args) + self.assertEqual(e.errno, E_POINTER) + self.assertEqual(e.winerror, E_POINTER) + class Structures(unittest.TestCase): def test_struct_by_value(self): class POINT(Structure): diff --git a/Lib/test/test_exceptions.py b/Lib/test/test_exceptions.py index 70d10ebc66e98b..29189b0110a7f3 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -368,6 +368,14 @@ def test_WindowsError(self): self.assertEqual(w.strerror, 'foo') self.assertEqual(w.filename, None) self.assertEqual(w.filename2, None) + # DWORD error code (issue #28474) + E_POINTER = 0x80000005 + w = OSError(E_POINTER, 'foo', 'bar', E_POINTER) + self.assertEqual(w.errno, E_POINTER) + self.assertEqual(w.winerror, E_POINTER) + self.assertEqual(w.strerror, 'foo') + self.assertEqual(w.filename, 'bar') + self.assertEqual(w.filename2, None) @unittest.skipUnless(sys.platform == 'win32', 'test specific to Windows') diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 17e82f90cf4740..35aecbd977f5d5 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1321,12 +1321,19 @@ static PyObject *format_error(PyObject *self, PyObject *args) { PyObject *result; wchar_t *lpMsgBuf; - DWORD code = 0; - if (!PyArg_ParseTuple(args, "|i:FormatError", &code)) + long long code = 0; + + if (!PyArg_ParseTuple(args, "|L:FormatError", &code)) + return NULL; + + if(code < LONG_MIN || code > ULONG_MAX) { + PyErr_SetString(PyExc_OverflowError, "int doesn't fit in long"); return NULL; + } + if (code == 0) code = GetLastError(); - lpMsgBuf = FormatError(code); + lpMsgBuf = FormatError((DWORD)code); if (lpMsgBuf) { result = PyUnicode_FromWideChar(lpMsgBuf, wcslen(lpMsgBuf)); LocalFree(lpMsgBuf); diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 714039ee1abca1..ef15d5a3a453a8 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -845,22 +845,28 @@ oserror_parse_args(PyObject **p_args, return -1; #ifdef MS_WINDOWS if (*winerror && PyLong_Check(*winerror)) { - long errcode, winerrcode; + long long errcode, winerrcode; PyObject *newargs; Py_ssize_t i; - winerrcode = PyLong_AsLong(*winerror); + winerrcode = PyLong_AsLongLong(*winerror); if (winerrcode == -1 && PyErr_Occurred()) return -1; + + if(winerrcode < LONG_MIN || winerrcode > ULONG_MAX) { + PyErr_SetString(PyExc_OverflowError, "int doesn't fit in long"); + return -1; + } + /* Set errno to the corresponding POSIX errno (overriding first argument). Windows Socket error codes (>= 10000) have the same value as their POSIX counterparts. */ if (winerrcode < 10000) - errcode = winerror_to_errno(winerrcode); + errcode = (long long)winerror_to_errno((int)winerrcode); else errcode = winerrcode; - *myerrno = PyLong_FromLong(errcode); + *myerrno = PyLong_FromLongLong(errcode); if (!*myerrno) return -1; newargs = PyTuple_New(nargs); From 4a1be05de96fa3e195c85d86c07ba10e062fa597 Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Thu, 26 Aug 2021 00:58:36 +0000 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../NEWS.d/next/Windows/2021-08-26-00-58-35.bpo-27484.7MThNi.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Windows/2021-08-26-00-58-35.bpo-27484.7MThNi.rst diff --git a/Misc/NEWS.d/next/Windows/2021-08-26-00-58-35.bpo-27484.7MThNi.rst b/Misc/NEWS.d/next/Windows/2021-08-26-00-58-35.bpo-27484.7MThNi.rst new file mode 100644 index 00000000000000..edc5af630a1ae3 --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2021-08-26-00-58-35.bpo-27484.7MThNi.rst @@ -0,0 +1 @@ +improve handling and reporting of unsigned win32 error codes \ No newline at end of file From f42270d1230fe6c98c39f6312bf6e05088ca68ff Mon Sep 17 00:00:00 2001 From: Adam Meily Date: Thu, 26 Aug 2021 21:23:53 -0400 Subject: [PATCH 3/6] Apply suggestions from code review Co-authored-by: Steve Dower --- Modules/_ctypes/callproc.c | 4 ++-- Objects/exceptions.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 35aecbd977f5d5..2704be769526fd 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1326,8 +1326,8 @@ static PyObject *format_error(PyObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "|L:FormatError", &code)) return NULL; - if(code < LONG_MIN || code > ULONG_MAX) { - PyErr_SetString(PyExc_OverflowError, "int doesn't fit in long"); + if((DWORD)code != code) { + PyErr_Format(PyExc_OverflowError, "error code %lld too big for int", code); return NULL; } diff --git a/Objects/exceptions.c b/Objects/exceptions.c index ef15d5a3a453a8..a98ca396c6feb9 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -854,7 +854,7 @@ oserror_parse_args(PyObject **p_args, return -1; if(winerrcode < LONG_MIN || winerrcode > ULONG_MAX) { - PyErr_SetString(PyExc_OverflowError, "int doesn't fit in long"); + PyErr_Format(PyExc_OverflowError, "error code %lld too big for int", winerrcode); return -1; } From 7e5a446235b0baa9bc25cf70a9b70cb4461e4824 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Sun, 30 Apr 2023 18:50:11 +0400 Subject: [PATCH 4/6] Fix a typo in errmap.h --- PC/errmap.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PC/errmap.h b/PC/errmap.h index 0b93dc00ba377c..f8aee589cdc91e 100644 --- a/PC/errmap.h +++ b/PC/errmap.h @@ -1,5 +1,5 @@ // Window error codes are unsigned long (like E_POINTER 0x80000005). -// We are limited with signed types, so use signed long long. +// We are limited to signed types so use signed long long. long long winerror_to_errno(long long winerror) { From 1f769bcdcbabd6a0349348aa16e3c0fbd4f3bbb1 Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Sun, 30 Apr 2023 19:03:42 +0400 Subject: [PATCH 5/6] Revert my conversion of `winerror_to_errno()` --- Objects/exceptions.c | 2 +- PC/errmap.h | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Objects/exceptions.c b/Objects/exceptions.c index 626eb25f7003da..b0286f715421d0 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1713,7 +1713,7 @@ oserror_parse_args(PyObject **p_args, return -1; } - errcode = winerror_to_errno(winerrcode); + errcode = (long long)winerror_to_errno((int)winerrcode); *myerrno = PyLong_FromLongLong(errcode); if (!*myerrno) return -1; diff --git a/PC/errmap.h b/PC/errmap.h index f8aee589cdc91e..a7489ab75c6561 100644 --- a/PC/errmap.h +++ b/PC/errmap.h @@ -1,7 +1,5 @@ -// Window error codes are unsigned long (like E_POINTER 0x80000005). -// We are limited to signed types so use signed long long. -long long -winerror_to_errno(long long winerror) +int +winerror_to_errno(int winerror) { // Unwrap FACILITY_WIN32 HRESULT errors. if ((winerror & 0xFFFF0000) == 0x80070000) { From 96816da656df35ca984a629026584073cb4dcc4b Mon Sep 17 00:00:00 2001 From: Oleg Iarygin Date: Sun, 30 Apr 2023 19:05:13 +0400 Subject: [PATCH 6/6] Fix a news file --- .../next/Windows/2021-08-26-00-58-35.bpo-27484.7MThNi.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Misc/NEWS.d/next/Windows/2021-08-26-00-58-35.bpo-27484.7MThNi.rst b/Misc/NEWS.d/next/Windows/2021-08-26-00-58-35.bpo-27484.7MThNi.rst index edc5af630a1ae3..43aeb44102665b 100644 --- a/Misc/NEWS.d/next/Windows/2021-08-26-00-58-35.bpo-27484.7MThNi.rst +++ b/Misc/NEWS.d/next/Windows/2021-08-26-00-58-35.bpo-27484.7MThNi.rst @@ -1 +1,2 @@ -improve handling and reporting of unsigned win32 error codes \ No newline at end of file +Improve handling and reporting of unsigned win32 error codes. +Patch by Adam Meily.