Index: Modules/_fileio.c =================================================================== --- Modules/_fileio.c (revision 69363) +++ Modules/_fileio.c (working copy) @@ -122,12 +122,14 @@ return 0; } +int _PyVerify_fd(int fd); /* from posixmodule.c */ + static int check_fd(int fd) { #if defined(HAVE_FSTAT) struct stat buf; - if (fstat(fd, &buf) < 0 && errno == EBADF) { + if (!_PyVerify_fd(fd) || (fstat(fd, &buf) < 0 && errno == EBADF)) { PyObject *exc; char *msg = strerror(EBADF); exc = PyObject_CallFunction(PyExc_OSError, "(is)", Index: Modules/posixmodule.c =================================================================== --- Modules/posixmodule.c (revision 69363) +++ Modules/posixmodule.c (working copy) @@ -343,6 +343,123 @@ #endif #endif +#if defined _MSC_VER && _MSC_VER >= 1400 +/* Microsoft CRT in VS2005 and higher will verify that a filehandle is + * valid and throw an assertion if it isn't. + * Normally, an invalid fd would be a C program bug and thus rightfully + * assertable. However, python allows the user to enter any old integer + * as a fd and will merely raise a python exception on error. + *The CRT doesn't provide an + * official way to check for validity, but we can emulate its internal + * behaviour by using the exported __pinfo data member and knowledge of the + * internal structures involved. + * The structures below must be updated for each version of visual studio + * according to the file internal.h in the CRT source, until MS comes + * up with a less hacky way to do this. + * (all of this is to avoid globally modifying the CRT behaviour using + * _set_invalid_parameter_handler() and _CrtSetReportMode()) + */ +#if _MSC_VER >= 1500 /* VS 2008 */ +typedef struct { + intptr_t osfhnd; + char osfile; + char pipech; + int lockinitflag; + CRITICAL_SECTION lock; +#ifndef _SAFECRT_IMPL + char textmode : 7; + char unicode : 1; + char pipech2[2]; + __int64 startpos; + BOOL utf8translations; + char dbcsBuffer; + BOOL dbcsBufferUsed; +#endif /* _SAFECRT_IMPL */ + } ioinfo; +#elif _MSC_VER >= 1400 /* VS 2005 */ +typedef struct { + intptr_t osfhnd; + char osfile; + char pipech; + int lockinitflag; + CRITICAL_SECTION lock; +#ifndef _SAFECRT_IMPL + char textmode : 7; + char unicode : 1; + char pipech2[2]; + __int64 startpos; + BOOL utf8translations; +#endif /* _SAFECRT_IMPL */ + } ioinfo; +#else /* VS 2003, really not supported */ +typedef struct { + long osfhnd; + char osfile; + char pipech; +#ifdef _MT + int lockinitflag; + CRITICAL_SECTION lock; +#endif /* _MT */ + } ioinfo; +#endif + +extern __declspec(dllimport) ioinfo * __pioinfo[]; +#define IOINFO_L2E 5 +#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E) +#define IOINFO_ARRAYS 64 +#define _NHANDLE_ (IOINFO_ARRAYS * IOINFO_ARRAY_ELTS) +#define FOPEN 0x01 +#define _NO_CONSOLE_FILENO (intptr_t)-2 + +/* This function emulates what the windows CRT does to validate file handles */ +int +_PyVerify_fd(int fd) +{ + const int i1 = fd >> IOINFO_L2E; + const int i2 = fd & ((1 << IOINFO_L2E) - 1); + + /* See that it isn't a special CLEAR fileno */ + if (fd != _NO_CONSOLE_FILENO) { + /* Microsoft CRT would check that 0<=fd<_nhandle but we can't do that. Instead + * we check pointer validity and other info + */ + if (0 <= i1 && i1 < IOINFO_ARRAYS && __pioinfo[i1] != NULL) { + /* finally, check that the file is open */ + if (__pioinfo[i1][i2].osfile & FOPEN) + return 1; + } + } + errno = EBADF; + return 0; +} + +/* the special case of checking dup2. The target fd must be in a sensible range */ +static int +_PyVerify_fd_dup2(int fd1, int fd2) +{ + if (!_PyVerify_fd(fd1)) + return 0; + if (fd2 == _NO_CONSOLE_FILENO) + return 0; + if ((unsigned)fd2 < _NHANDLE_) + return 1; + else + return 0; +} +#else +/* dummy versions of the above, used when CRT doen't assert */ +int +_PyVerify_fd(int fd) +{ + return 1 +} +static int +_PyVerify_fd_dup2(int fd1, int fd2) +{ + return 1; +} +#endif + /* Return a dictionary corresponding to the POSIX environment table */ #ifdef WITH_NEXT_FRAMEWORK /* On Darwin/MacOSX a shared library or framework has no access to @@ -581,6 +698,8 @@ fd = PyObject_AsFileDescriptor(fdobj); if (fd < 0) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS res = (*func)(fd); Py_END_ALLOW_THREADS @@ -6188,6 +6307,8 @@ int fd, res; if (!PyArg_ParseTuple(args, "i:close", &fd)) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS res = close(fd); Py_END_ALLOW_THREADS @@ -6210,7 +6331,8 @@ return NULL; Py_BEGIN_ALLOW_THREADS for (i = fd_from; i < fd_to; i++) - close(i); + if (_PyVerify_fd(i)) + close(i); Py_END_ALLOW_THREADS Py_RETURN_NONE; } @@ -6226,6 +6348,8 @@ int fd; if (!PyArg_ParseTuple(args, "i:dup", &fd)) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS fd = dup(fd); Py_END_ALLOW_THREADS @@ -6245,6 +6369,8 @@ int fd, fd2, res; if (!PyArg_ParseTuple(args, "ii:dup2", &fd, &fd2)) return NULL; + if (!_PyVerify_fd_dup2(fd, fd2)) + return posix_error(); Py_BEGIN_ALLOW_THREADS res = dup2(fd, fd2); Py_END_ALLOW_THREADS @@ -6289,6 +6415,8 @@ if (PyErr_Occurred()) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS #if defined(MS_WIN64) || defined(MS_WINDOWS) res = _lseeki64(fd, pos, how); @@ -6325,6 +6453,8 @@ buffer = PyString_FromStringAndSize((char *)NULL, size); if (buffer == NULL) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS n = read(fd, PyString_AsString(buffer), size); Py_END_ALLOW_THREADS @@ -6351,6 +6481,8 @@ if (!PyArg_ParseTuple(args, "is*:write", &fd, &pbuf)) return NULL; + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS size = write(fd, pbuf.buf, (size_t)pbuf.len); Py_END_ALLOW_THREADS @@ -6377,6 +6509,8 @@ /* on OpenVMS we must ensure that all bytes are written to the file */ fsync(fd); #endif + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS res = FSTAT(fd, &st); Py_END_ALLOW_THREADS @@ -6419,6 +6553,8 @@ PyMem_FREE(mode); return NULL; } + if (!_PyVerify_fd(fd)) + return posix_error(); Py_BEGIN_ALLOW_THREADS #if !defined(MS_WINDOWS) && defined(HAVE_FCNTL_H) if (mode[0] == 'a') { @@ -6458,6 +6594,8 @@ int fd; if (!PyArg_ParseTuple(args, "i:isatty", &fd)) return NULL; + if (!_PyVerify_fd(fd)) + return PyBool_FromLong(0); return PyBool_FromLong(isatty(fd)); } Index: Objects/exceptions.c =================================================================== --- Objects/exceptions.c (revision 69363) +++ Objects/exceptions.c (working copy) @@ -1972,29 +1972,7 @@ if (PyDict_SetItemString(bdict, # TYPE, PyExc_ ## TYPE)) \ Py_FatalError("Module dictionary insertion problem."); -#if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__) -/* crt variable checking in VisualStudio .NET 2005 */ -#include -static int prevCrtReportMode; -static _invalid_parameter_handler prevCrtHandler; - -/* Invalid parameter handler. Sets a ValueError exception */ -static void -InvalidParameterHandler( - const wchar_t * expression, - const wchar_t * function, - const wchar_t * file, - unsigned int line, - uintptr_t pReserved) -{ - /* Do nothing, allow execution to continue. Usually this - * means that the CRT will set errno to EINVAL - */ -} -#endif - - PyMODINIT_FUNC _PyExc_Init(void) { @@ -2153,13 +2131,6 @@ } Py_DECREF(bltinmod); - -#if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__) - /* Set CRT argument error handler */ - prevCrtHandler = _set_invalid_parameter_handler(InvalidParameterHandler); - /* turn off assertions in debug mode */ - prevCrtReportMode = _CrtSetReportMode(_CRT_ASSERT, 0); -#endif } void @@ -2167,9 +2138,4 @@ { Py_XDECREF(PyExc_MemoryErrorInst); PyExc_MemoryErrorInst = NULL; -#if defined _MSC_VER && _MSC_VER >= 1400 && defined(__STDC_SECURE_LIB__) - /* reset CRT error handling */ - _set_invalid_parameter_handler(prevCrtHandler); - _CrtSetReportMode(_CRT_ASSERT, prevCrtReportMode); -#endif }