diff --git a/Lib/ctypes/__init__.py b/Lib/ctypes/__init__.py index 95353bab26cc71..a9a1d81830b583 100644 --- a/Lib/ctypes/__init__.py +++ b/Lib/ctypes/__init__.py @@ -481,7 +481,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/test/test_ctypes/test_win32.py b/Lib/test/test_ctypes/test_win32.py index e51bdc8ad6b071..abbac815a06326 100644 --- a/Lib/test/test_ctypes/test_win32.py +++ b/Lib/test/test_ctypes/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 4ef7decfbc263e..afe9b59f543e00 100644 --- a/Lib/test/test_exceptions.py +++ b/Lib/test/test_exceptions.py @@ -402,6 +402,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/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..43aeb44102665b --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2021-08-26-00-58-35.bpo-27484.7MThNi.rst @@ -0,0 +1,2 @@ +Improve handling and reporting of unsigned win32 error codes. +Patch by Adam Meily. diff --git a/Modules/_ctypes/callproc.c b/Modules/_ctypes/callproc.c index 93bc784df5386f..131813cb2c0958 100644 --- a/Modules/_ctypes/callproc.c +++ b/Modules/_ctypes/callproc.c @@ -1334,12 +1334,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((DWORD)code != code) { + PyErr_Format(PyExc_OverflowError, "error code %lld too big for int", code); 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 6c9dfbd9b415cf..b0286f715421d0 100644 --- a/Objects/exceptions.c +++ b/Objects/exceptions.c @@ -1700,15 +1700,21 @@ 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; - errcode = winerror_to_errno(winerrcode); - *myerrno = PyLong_FromLong(errcode); + + if(winerrcode < LONG_MIN || winerrcode > ULONG_MAX) { + PyErr_Format(PyExc_OverflowError, "error code %lld too big for int", winerrcode); + return -1; + } + + errcode = (long long)winerror_to_errno((int)winerrcode); + *myerrno = PyLong_FromLongLong(errcode); if (!*myerrno) return -1; newargs = PyTuple_New(nargs);