Skip to content

Commit 887f876

Browse files
committed
properly handle unsigned long win32 error codes
fixes python#28474
1 parent 0742abd commit 887f876

5 files changed

Lines changed: 51 additions & 8 deletions

File tree

‎Lib/ctypes/__init__.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -469,7 +469,9 @@ def LoadLibrary(self, name):
469469

470470
def WinError(code=None, descr=None):
471471
if code is None:
472-
code = GetLastError()
472+
# GetLastError returns a DWORD, cast large error codes
473+
# to unsigned
474+
code = GetLastError() & 0xffffffff
473475
if descr is None:
474476
descr = FormatError(code).strip()
475477
return OSError(None, descr, None, code)

‎Lib/ctypes/test/test_win32.py‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,26 @@ def test_winerror(self):
9090
self.assertEqual(e.errno, errno.EINVAL)
9191
self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER)
9292

93+
def test_winerror_dword(self):
94+
# see Issue 28474
95+
E_POINTER = 0x80000005
96+
msg = FormatError(E_POINTER).strip()
97+
args = (E_POINTER, msg, None, E_POINTER)
98+
99+
e = WinError(E_POINTER)
100+
self.assertEqual(e.args, args)
101+
self.assertEqual(e.errno, E_POINTER)
102+
self.assertEqual(e.winerror, E_POINTER)
103+
104+
windll.kernel32.SetLastError(E_POINTER)
105+
try:
106+
raise WinError()
107+
except OSError as exc:
108+
e = exc
109+
self.assertEqual(e.args, args)
110+
self.assertEqual(e.errno, E_POINTER)
111+
self.assertEqual(e.winerror, E_POINTER)
112+
93113
class Structures(unittest.TestCase):
94114
def test_struct_by_value(self):
95115
class POINT(Structure):

‎Lib/test/test_exceptions.py‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,14 @@ def test_WindowsError(self):
368368
self.assertEqual(w.strerror, 'foo')
369369
self.assertEqual(w.filename, None)
370370
self.assertEqual(w.filename2, None)
371+
# DWORD error code (issue #28474)
372+
E_POINTER = 0x80000005
373+
w = OSError(E_POINTER, 'foo', 'bar', E_POINTER)
374+
self.assertEqual(w.errno, E_POINTER)
375+
self.assertEqual(w.winerror, E_POINTER)
376+
self.assertEqual(w.strerror, 'foo')
377+
self.assertEqual(w.filename, 'bar')
378+
self.assertEqual(w.filename2, None)
371379

372380
@unittest.skipUnless(sys.platform == 'win32',
373381
'test specific to Windows')

‎Modules/_ctypes/callproc.c‎

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1321,12 +1321,19 @@ static PyObject *format_error(PyObject *self, PyObject *args)
13211321
{
13221322
PyObject *result;
13231323
wchar_t *lpMsgBuf;
1324-
DWORD code = 0;
1325-
if (!PyArg_ParseTuple(args, "|i:FormatError", &code))
1324+
long long code = 0;
1325+
1326+
if (!PyArg_ParseTuple(args, "|L:FormatError", &code))
1327+
return NULL;
1328+
1329+
if(code < LONG_MIN || code > ULONG_MAX) {
1330+
PyErr_SetString(PyExc_OverflowError, "int doesn't fit in long");
13261331
return NULL;
1332+
}
1333+
13271334
if (code == 0)
13281335
code = GetLastError();
1329-
lpMsgBuf = FormatError(code);
1336+
lpMsgBuf = FormatError((DWORD)code);
13301337
if (lpMsgBuf) {
13311338
result = PyUnicode_FromWideChar(lpMsgBuf, wcslen(lpMsgBuf));
13321339
LocalFree(lpMsgBuf);

‎Objects/exceptions.c‎

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -845,22 +845,28 @@ oserror_parse_args(PyObject **p_args,
845845
return -1;
846846
#ifdef MS_WINDOWS
847847
if (*winerror && PyLong_Check(*winerror)) {
848-
long errcode, winerrcode;
848+
long long errcode, winerrcode;
849849
PyObject *newargs;
850850
Py_ssize_t i;
851851

852-
winerrcode = PyLong_AsLong(*winerror);
852+
winerrcode = PyLong_AsLongLong(*winerror);
853853
if (winerrcode == -1 && PyErr_Occurred())
854854
return -1;
855+
856+
if(winerrcode < LONG_MIN || winerrcode > ULONG_MAX) {
857+
PyErr_SetString(PyExc_OverflowError, "int doesn't fit in long");
858+
return -1;
859+
}
860+
855861
/* Set errno to the corresponding POSIX errno (overriding
856862
first argument). Windows Socket error codes (>= 10000)
857863
have the same value as their POSIX counterparts.
858864
*/
859865
if (winerrcode < 10000)
860-
errcode = winerror_to_errno(winerrcode);
866+
errcode = (long long)winerror_to_errno((int)winerrcode);
861867
else
862868
errcode = winerrcode;
863-
*myerrno = PyLong_FromLong(errcode);
869+
*myerrno = PyLong_FromLongLong(errcode);
864870
if (!*myerrno)
865871
return -1;
866872
newargs = PyTuple_New(nargs);

0 commit comments

Comments
 (0)