# HG changeset patch # User Steve Dower # Date 1424930096 28800 # Wed Feb 25 21:54:56 2015 -0800 # Node ID d0b6b9c443161c75ee49dc3fc1517e3bc82b696f # Parent 344d57c521b9ec2b50a1ae7c84dcf66a1ee18008 Issue #23524: Replace _PyVerify_fd function with calls to _set_thread_local_invalid_parameter_handler. diff --git a/Include/fileobject.h b/Include/fileobject.h --- a/Include/fileobject.h +++ b/Include/fileobject.h @@ -32,17 +32,6 @@ #ifndef Py_LIMITED_API PyAPI_FUNC(PyObject *) PyFile_NewStdPrinter(int); PyAPI_DATA(PyTypeObject) PyStdPrinter_Type; - -#if defined _MSC_VER && _MSC_VER >= 1400 -/* A routine to check if a file descriptor is valid on Windows. Returns 0 - * and sets errno to EBADF if it isn't. This is to avoid Assertions - * from various functions in the Windows CRT beginning with - * Visual Studio 2005 - */ -int _PyVerify_fd(int fd); -#else -#define _PyVerify_fd(A) (1) /* dummy */ -#endif #endif /* Py_LIMITED_API */ /* A routine to check if a file descriptor can be select()-ed. */ diff --git a/Include/fileutils.h b/Include/fileutils.h --- a/Include/fileutils.h +++ b/Include/fileutils.h @@ -108,6 +108,30 @@ PyAPI_FUNC(int) _Py_set_blocking(int fd, int blocking); #endif /* !MS_WINDOWS */ +#if defined _MSC_VER && _MSC_VER >= 1400 +/* A routine to check if a file descriptor is valid on Windows. Returns 0 + * and sets errno to EBADF if it isn't. This is to avoid Assertions + * from various functions in the Windows CRT beginning with + * Visual Studio 2005 + */ +int _PyVerify_fd(int fd); + +/* Macro to call _PyVerify_fd from code blocks protected against termination + * due to invalid parameters. When protected, the upcoming CRT call will + * fail with EBADF itself, and so we do not need to call _PyVerify_fd. + * (issue23524) + */ +#if _MSC_VER >= 1900 +#define _Py_VERIFY_FD(fd) (1) +#else +#define _Py_VERIFY_FD(fd) (_PyVerify_fd(fd)) +#endif + +#else +#define _PyVerify_fd(A) (1) /* dummy */ +#define _Py_VERIFY_FD(A) (1) /* dummy */ +#endif + #endif /* Py_LIMITED_API */ #ifdef __cplusplus diff --git a/Include/pymacro.h b/Include/pymacro.h --- a/Include/pymacro.h +++ b/Include/pymacro.h @@ -88,4 +88,24 @@ #define Py_UNUSED(name) _unused_ ## name #endif +#ifdef Py_BUILD_CORE +/* + * Macros to protect CRT calls against instant termination when passed an + * invalid parameter (issue23524). + */ +#if defined _MSC_VER && _MSC_VER >= 1900 + +PyAPI_DATA(void*) _Py_silent_invalid_parameter_handler; +#define _Py_BEGIN_SUPPRESS_IPH { _invalid_parameter_handler _Py_old_handler = \ + _set_thread_local_invalid_parameter_handler((_invalid_parameter_handler)_Py_silent_invalid_parameter_handler); +#define _Py_END_SUPPRESS_IPH _set_thread_local_invalid_parameter_handler(_Py_old_handler); } + +#else + +#define _Py_BEGIN_SUPPRESS_IPH +#define _Py_END_SUPPRESS_IPH + +#endif /* _MSC_VER >= 1900 */ +#endif /* Py_BUILD_CORE */ + #endif /* Py_PYMACRO_H */ diff --git a/Modules/_io/fileio.c b/Modules/_io/fileio.c --- a/Modules/_io/fileio.c +++ b/Modules/_io/fileio.c @@ -105,11 +105,13 @@ int fd = self->fd; self->fd = -1; /* fd is accessible and someone else may have closed it */ - if (_PyVerify_fd(fd)) { + if (_Py_VERIFY_FD(fd)) { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH err = close(fd); if (err < 0) save_errno = errno; + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } else { save_errno = errno; @@ -182,7 +184,13 @@ { #if defined(HAVE_FSTAT) || defined(MS_WINDOWS) struct _Py_stat_struct buf; - if (!_PyVerify_fd(fd) || (_Py_fstat(fd, &buf) < 0 && errno == EBADF)) { + if (_Py_fstat(fd, &buf) < 0 && +#ifdef MS_WINDOWS + GetLastError() == ERROR_INVALID_HANDLE +#else + errno == EBADF +#endif + ) { PyObject *exc; char *msg = strerror(EBADF); exc = PyObject_CallFunction(PyExc_OSError, "(is)", @@ -574,10 +582,11 @@ if (!PyArg_ParseTuple(args, "w*", &pbuf)) return NULL; - if (_PyVerify_fd(self->fd)) { + if (_Py_VERIFY_FD(self->fd)) { len = pbuf.len; do { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH errno = 0; #ifdef MS_WINDOWS if (len > INT_MAX) @@ -586,6 +595,7 @@ #else n = read(self->fd, pbuf.buf, len); #endif + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); @@ -639,14 +649,17 @@ if (self->fd < 0) return err_closed(); - if (!_PyVerify_fd(self->fd)) + if (!_Py_VERIFY_FD(self->fd)) return PyErr_SetFromErrno(PyExc_IOError); + _Py_BEGIN_SUPPRESS_IPH #ifdef MS_WINDOWS pos = _lseeki64(self->fd, 0L, SEEK_CUR); #else pos = lseek(self->fd, 0L, SEEK_CUR); #endif + _Py_END_SUPPRESS_IPH + if (_Py_fstat(self->fd, &st) == 0) end = st.st_size; else @@ -684,6 +697,7 @@ } do { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH errno = 0; n = bufsize - bytes_read; #ifdef MS_WINDOWS @@ -693,6 +707,7 @@ #else n = read(self->fd, PyBytes_AS_STRING(result) + bytes_read, n); #endif + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); @@ -763,15 +778,17 @@ return NULL; ptr = PyBytes_AS_STRING(bytes); - if (_PyVerify_fd(self->fd)) { + if (_Py_VERIFY_FD(self->fd)) { do { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH errno = 0; #ifdef MS_WINDOWS n = read(self->fd, ptr, (int)size); #else n = read(self->fd, ptr, size); #endif + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); @@ -814,9 +831,10 @@ if (!PyArg_ParseTuple(args, "y*", &pbuf)) return NULL; - if (_PyVerify_fd(self->fd)) { + if (_Py_VERIFY_FD(self->fd)) { do { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH errno = 0; len = pbuf.len; #ifdef MS_WINDOWS @@ -832,6 +850,7 @@ #else n = write(self->fd, pbuf.buf, len); #endif + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); @@ -892,13 +911,15 @@ return NULL; } - if (_PyVerify_fd(fd)) { + if (_Py_VERIFY_FD(fd)) { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH #ifdef MS_WINDOWS res = _lseeki64(fd, pos, whence); #else res = lseek(fd, pos, whence); #endif + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } else res = -1; @@ -991,14 +1012,20 @@ /* Truncate. Note that this may grow the file! */ Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH errno = 0; - hFile = (HANDLE)_get_osfhandle(fd); - ret = hFile == (HANDLE)-1; /* testing for INVALID_HANDLE value */ + if (_Py_VERIFY_FD(fd)) { + hFile = (HANDLE)_get_osfhandle(fd); + ret = hFile == (HANDLE)INVALID_HANDLE_VALUE; + } + else + ret = 1; if (ret == 0) { ret = SetEndOfFile(hFile) == 0; if (ret) errno = EACCES; } + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS /* we restore the file pointer position in any case */ @@ -1023,8 +1050,10 @@ } Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH errno = 0; ret = ftruncate(fd, pos); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS #endif /* !MS_WINDOWS */ @@ -1099,7 +1128,12 @@ if (self->fd < 0) return err_closed(); Py_BEGIN_ALLOW_THREADS - res = isatty(self->fd); + _Py_BEGIN_SUPPRESS_IPH + if (_Py_VERIFY_FD(self->fd)) + res = isatty(self->fd); + else + res = 0; + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS return PyBool_FromLong(res); } diff --git a/Modules/mmapmodule.c b/Modules/mmapmodule.c --- a/Modules/mmapmodule.c +++ b/Modules/mmapmodule.c @@ -1325,11 +1325,13 @@ */ if (fileno != -1 && fileno != 0) { /* Ensure that fileno is within the CRT's valid range */ - if (_PyVerify_fd(fileno) == 0) { + if (!_Py_VERIFY_FD(fileno)) { PyErr_SetFromErrno(PyExc_OSError); return NULL; } + _Py_BEGIN_SUPPRESS_IPH fh = (HANDLE)_get_osfhandle(fileno); + _Py_END_SUPPRESS_IPH if (fh==(HANDLE)-1) { PyErr_SetFromErrno(PyExc_OSError); return NULL; diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -1051,99 +1051,16 @@ } -#if defined _MSC_VER && _MSC_VER >= 1400 -/* Microsoft CRT in VS2005 and higher will verify that a filehandle is - * valid and raise an assertion if it isn't. - * Normally, an invalid fd is likely to be a C program error and therefore - * an assertion can be useful, but it does contradict the POSIX standard - * which for write(2) states: - * "Otherwise, -1 shall be returned and errno set to indicate the error." - * "[EBADF] The fildes argument is not a valid file descriptor open for - * writing." - * Furthermore, python allows the user to enter any old integer - * as a fd and should merely raise a python exception on error. - * The Microsoft CRT doesn't provide an official way to check for the - * validity of a file descriptor, 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 defined _MSC_VER && _MSC_VER >= 1400 && _MSC_VER < 1900 +/* Legacy implementation of _PyVerify_fd_dup2 while transitioning to + * MSVC 14.0. This should eventually be removed. (issue23524) */ -/* The actual size of the structure is determined at runtime. - * Only the first items must be present. - */ - -#if _MSC_VER >= 1900 - -typedef struct { - CRITICAL_SECTION lock; - intptr_t osfhnd; - __int64 startpos; - char osfile; -} my_ioinfo; - -#define IOINFO_L2E 6 -#define IOINFO_ARRAYS 128 - -#else - -typedef struct { - intptr_t osfhnd; - char osfile; -} my_ioinfo; - #define IOINFO_L2E 5 #define IOINFO_ARRAYS 64 - -#endif - -extern __declspec(dllimport) char * __pioinfo[]; #define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E) #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); - - static size_t sizeof_ioinfo = 0; - - /* Determine the actual size of the ioinfo structure, - * as used by the CRT loaded in memory - */ - if (sizeof_ioinfo == 0 && __pioinfo[0] != NULL) { - sizeof_ioinfo = _msize(__pioinfo[0]) / IOINFO_ARRAY_ELTS; - } - if (sizeof_ioinfo == 0) { - /* This should not happen... */ - goto fail; - } - - /* 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 */ - my_ioinfo* info = (my_ioinfo*)(__pioinfo[i1] + i2 * sizeof_ioinfo); - if (info->osfile & FOPEN) { - return 1; - } - } - } - fail: - 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) @@ -1157,9 +1074,10 @@ else return 0; } -#else -/* dummy version. _PyVerify_fd() is already defined in fileobject.h */ -#define _PyVerify_fd_dup2(A, B) (1) + +#define _Py_VERIFY_FD_DUP2(fd1, fd2) (_PyVerify_fd_dup2((fd1), (fd2))) +#else +#define _Py_VERIFY_FD_DUP2(fd1, fd2) (_Py_VERIFY_FD(fd1) && (fd2) >= 0) #endif #ifdef MS_WINDOWS @@ -1371,7 +1289,9 @@ do { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH res = (*func)(fd); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } while (res != 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); if (res != 0) @@ -5751,6 +5671,7 @@ int result; Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH #ifdef MS_WINDOWS if (path->wide) result = Py_DeleteFileW(path->wide); @@ -5765,6 +5686,7 @@ #endif /* HAVE_UNLINKAT */ result = unlink(path->narrow); #endif + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS if (result) @@ -10834,14 +10756,16 @@ /*[clinic end generated code: output=927004e29ad55808 input=2bc42451ca5c3223]*/ { int res; - if (!_PyVerify_fd(fd)) + if (!_Py_VERIFY_FD(fd)) return posix_error(); /* We do not want to retry upon EINTR: see http://lwn.net/Articles/576478/ * and http://linux.derkeiler.com/Mailing-Lists/Kernel/2005-09/3000.html * for more details. */ Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH res = close(fd); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS if (res < 0) return posix_error(); @@ -10894,9 +10818,11 @@ { int i; Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH for (i = fd_low; i < fd_high; i++) - if (_PyVerify_fd(i)) + if (_Py_VERIFY_FD(i)) close(i); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS Py_RETURN_NONE; } @@ -11002,7 +10928,7 @@ int dup3_works = -1; #endif - if (!_PyVerify_fd_dup2(fd, fd2)) + if (!_Py_VERIFY_FD_DUP2(fd, fd2)) return posix_error(); /* dup2() can fail with EINTR if the target FD is already open, because it @@ -11011,7 +10937,9 @@ */ #ifdef MS_WINDOWS Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH res = dup2(fd, fd2); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS if (res < 0) return posix_error(); @@ -11198,7 +11126,7 @@ { Py_off_t result; - if (!_PyVerify_fd(fd)) { + if (!_Py_VERIFY_FD(fd)) { posix_error(); return -1; } @@ -11214,16 +11142,18 @@ if (PyErr_Occurred()) return -1; - if (!_PyVerify_fd(fd)) { + if (!_Py_VERIFY_FD(fd)) { posix_error(); return -1; } Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH #ifdef MS_WINDOWS result = _lseeki64(fd, position, how); #else result = lseek(fd, position, how); #endif + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS if (result < 0) posix_error(); @@ -11282,7 +11212,7 @@ errno = EINVAL; return posix_error(); } - if (!_PyVerify_fd(fd)) + if (!_Py_VERIFY_FD(fd)) return posix_error(); #ifdef MS_WINDOWS @@ -11299,7 +11229,9 @@ do { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH n = read(fd, PyBytes_AS_STRING(buffer), READ_CAST length); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); @@ -11536,14 +11468,16 @@ buffer = PyBytes_FromStringAndSize((char *)NULL, length); if (buffer == NULL) return NULL; - if (!_PyVerify_fd(fd)) { + if (!_Py_VERIFY_FD(fd)) { Py_DECREF(buffer); return posix_error(); } do { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH n = pread(fd, PyBytes_AS_STRING(buffer), length, offset); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); @@ -11612,14 +11546,14 @@ Py_ssize_t size; int async_err = 0; Py_ssize_t len = data->len; - - if (!_PyVerify_fd(fd)) { + if (!_Py_VERIFY_FD(fd)) { posix_error(); return -1; } do { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH #ifdef MS_WINDOWS if (len > INT_MAX) len = INT_MAX; @@ -11627,6 +11561,7 @@ #else size = write(fd, data->buf, len); #endif + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } while (size < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); @@ -11906,9 +11841,13 @@ os_isatty_impl(PyModuleDef *module, int fd) /*[clinic end generated code: output=4bfadbfe22715097 input=08ce94aa1eaf7b5e]*/ { - if (!_PyVerify_fd(fd)) + int return_value; + if (!_Py_VERIFY_FD(fd)) return 0; - return isatty(fd); + _Py_BEGIN_SUPPRESS_IPH + return_value = isatty(fd); + _Py_END_SUPPRESS_IPH + return return_value; } @@ -12229,14 +12168,16 @@ Py_ssize_t size; int async_err = 0; - if (!_PyVerify_fd(fd)) { + if (!_Py_VERIFY_FD(fd)) { posix_error(); return -1; } do { Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH size = pwrite(fd, buffer->buf, (size_t)buffer->len, offset); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS } while (size < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); @@ -16197,12 +16138,16 @@ os_get_inheritable_impl(PyModuleDef *module, int fd) /*[clinic end generated code: output=261d1dd2b0dbdc35 input=89ac008dc9ab6b95]*/ { - if (!_PyVerify_fd(fd)){ + int return_value; + if (!_Py_VERIFY_FD(fd)) { posix_error(); return -1; } - return _Py_get_inheritable(fd); + _Py_BEGIN_SUPPRESS_IPH + return_value = _Py_get_inheritable(fd); + _Py_END_SUPPRESS_IPH + return return_value; } @@ -16248,10 +16193,14 @@ os_set_inheritable_impl(PyModuleDef *module, int fd, int inheritable) /*[clinic end generated code: output=64dfe5e15c906539 input=9ceaead87a1e2402]*/ { - if (!_PyVerify_fd(fd)) - return posix_error(); - - if (_Py_set_inheritable(fd, inheritable, NULL) < 0) + int result; + if (!_Py_VERIFY_FD(fd)) + return posix_error(); + + _Py_BEGIN_SUPPRESS_IPH + result = _Py_set_inheritable(fd, inheritable, NULL); + _Py_END_SUPPRESS_IPH + if (result < 0) return NULL; Py_RETURN_NONE; } @@ -16380,10 +16329,12 @@ if (!PyArg_ParseTuple(args, "i:get_blocking", &fd)) return NULL; - if (!_PyVerify_fd(fd)) - return posix_error(); - + if (!_Py_VERIFY_FD(fd)) + return posix_error(); + + _Py_BEGIN_SUPPRESS_IPH blocking = _Py_get_blocking(fd); + _Py_END_SUPPRESS_IPH if (blocking < 0) return NULL; return PyBool_FromLong(blocking); @@ -16399,15 +16350,18 @@ static PyObject* posix_set_blocking(PyObject *self, PyObject *args) { - int fd, blocking; + int fd, blocking, result; if (!PyArg_ParseTuple(args, "ii:set_blocking", &fd, &blocking)) return NULL; - if (!_PyVerify_fd(fd)) - return posix_error(); - - if (_Py_set_blocking(fd, blocking) < 0) + if (!_Py_VERIFY_FD(fd)) + return posix_error(); + + _Py_BEGIN_SUPPRESS_IPH + result = _Py_set_blocking(fd, blocking); + _Py_END_SUPPRESS_IPH + if (result < 0) return NULL; Py_RETURN_NONE; } diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c --- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -560,7 +560,7 @@ } if (_Py_fstat(fd, &st) != 0) { - PyErr_SetFromErrno(PyExc_OSError); + PyErr_SetFromWindowsErr(GetLastError()); return NULL; } diff --git a/PC/invalid_parameter_handler.c b/PC/invalid_parameter_handler.c new file mode 100644 --- /dev/null +++ b/PC/invalid_parameter_handler.c @@ -0,0 +1,22 @@ +#ifdef _MSC_VER + +#include + +#if _MSC_VER >= 1900 +/* pyconfig.h uses this function in the _Py_BEGIN/END_SUPPRESS_IPH + * macros. It does not need to be defined when building using MSVC + * earlier than 14.0 (_MSC_VER == 1900). + */ + +static void __cdecl _silent_invalid_parameter_handler( + wchar_t const* expression, + wchar_t const* function, + wchar_t const* file, + unsigned int line, + uintptr_t pReserved) { } + +void *_Py_silent_invalid_parameter_handler = + (void*)_silent_invalid_parameter_handler; +#endif + +#endif diff --git a/PC/msvcrtmodule.c b/PC/msvcrtmodule.c --- a/PC/msvcrtmodule.c +++ b/PC/msvcrtmodule.c @@ -146,10 +146,12 @@ if (!PyArg_ParseTuple(args,"i:get_osfhandle", &fd)) return NULL; - if (!_PyVerify_fd(fd)) + if (!_Py_VERIFY_FD(fd)) return PyErr_SetFromErrno(PyExc_IOError); + _Py_BEGIN_SUPPRESS_IPH handle = _get_osfhandle(fd); + _Py_END_SUPPRESS_IPH if (handle == -1) return PyErr_SetFromErrno(PyExc_IOError); diff --git a/PC/pyconfig.h b/PC/pyconfig.h --- a/PC/pyconfig.h +++ b/PC/pyconfig.h @@ -111,7 +111,7 @@ * literally in the string. */ #define _Py_PASTE_VERSION(SUFFIX) \ - ("[MSC v." _Py_STRINGIZE(_MSC_VER) " " SUFFIX "]") + ("[MSC v." _Py_STRINGIZE(_MSC_VER) " " SUFFIX "]") /* e.g., this produces, after compile-time string catenation, * ("[MSC v.1200 32 bit (Intel)]") * @@ -132,7 +132,7 @@ *and* on Win64. For the same reasons, in Python, MS_WIN32 is defined on Win32 *and* Win64. Win32 only code must therefore be guarded as follows: - #if defined(MS_WIN32) && !defined(MS_WIN64) + #if defined(MS_WIN32) && !defined(MS_WIN64) Some modules are disabled on Itanium processors, therefore we have MS_WINI64 set for those targets, otherwise MS_WINX64 */ @@ -300,9 +300,9 @@ #ifdef MS_COREDLL # ifndef Py_BUILD_CORE /* not building the core - must be an ext */ # if defined(_MSC_VER) - /* So MSVC users need not specify the .lib file in - their Makefile (other compilers are generally - taken care of by distutils.) */ + /* So MSVC users need not specify the .lib file in + their Makefile (other compilers are generally + taken care of by distutils.) */ # if defined(_DEBUG) # pragma comment(lib,"python35_d.lib") # elif defined(Py_LIMITED_API) @@ -339,7 +339,7 @@ # define SIZEOF_FPOS_T 8 # define SIZEOF_HKEY 4 # define SIZEOF_SIZE_T 4 - /* MS VS2005 changes time_t to an 64-bit type on all platforms */ + /* MS VS2005 changes time_t to an 64-bit type on all platforms */ # if defined(_MSC_VER) && _MSC_VER >= 1400 # define SIZEOF_TIME_T 8 # else diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj --- a/PCbuild/pythoncore.vcxproj +++ b/PCbuild/pythoncore.vcxproj @@ -333,6 +333,7 @@ + @@ -394,25 +395,21 @@ - - - - - + + + $([System.IO.File]::ReadAllText('$(IntDir)hgbranch.txt').Trim()) $([System.IO.File]::ReadAllText('$(IntDir)hgversion.txt').Trim()) $([System.IO.File]::ReadAllText('$(IntDir)hgtag.txt').Trim()) - HGVERSION="$(HgVersion)";HGTAG="$(HgTag)";HGBRANCH="$(HgBranch)";%(PreprocessorDefinitions) - diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters --- a/PCbuild/pythoncore.vcxproj.filters +++ b/PCbuild/pythoncore.vcxproj.filters @@ -959,6 +959,9 @@ Modules + + PC + diff --git a/Python/fileutils.c b/Python/fileutils.c --- a/Python/fileutils.c +++ b/Python/fileutils.c @@ -3,6 +3,7 @@ #include #ifdef MS_WINDOWS +# include # include #endif @@ -39,9 +40,13 @@ #if defined(MS_WINDOWS) UINT cp; #endif - if (!_PyVerify_fd(fd) || !isatty(fd)) { + int valid; + _Py_BEGIN_SUPPRESS_IPH + valid = _Py_VERIFY_FD(fd) && isatty(fd); + _Py_END_SUPPRESS_IPH + if (!valid) Py_RETURN_NONE; - } + #if defined(MS_WINDOWS) if (fd == 0) cp = GetConsoleCP(); @@ -631,10 +636,13 @@ HANDLE h; int type; - if (!_PyVerify_fd(fd)) + if (!_Py_VERIFY_FD(fd)) h = INVALID_HANDLE_VALUE; - else + else { + _Py_BEGIN_SUPPRESS_IPH h = (HANDLE)_get_osfhandle(fd); + _Py_END_SUPPRESS_IPH + } /* Protocol violation: we explicitly clear errno, instead of setting it to a POSIX error. Callers should use GetLastError. */ @@ -723,13 +731,15 @@ HANDLE handle; DWORD flags; - if (!_PyVerify_fd(fd)) { + if (!_Py_VERIFY_FD(fd)) { if (raise) PyErr_SetFromErrno(PyExc_OSError); return -1; } + _Py_BEGIN_SUPPRESS_IPH handle = (HANDLE)_get_osfhandle(fd); + _Py_END_SUPPRESS_IPH if (handle == INVALID_HANDLE_VALUE) { if (raise) PyErr_SetFromWindowsErr(0); @@ -798,13 +808,15 @@ } #ifdef MS_WINDOWS - if (!_PyVerify_fd(fd)) { + if (!_Py_VERIFY_FD(fd)) { if (raise) PyErr_SetFromErrno(PyExc_OSError); return -1; } + _Py_BEGIN_SUPPRESS_IPH handle = (HANDLE)_get_osfhandle(fd); + _Py_END_SUPPRESS_IPH if (handle == INVALID_HANDLE_VALUE) { if (raise) PyErr_SetFromWindowsErr(0); @@ -1158,13 +1170,15 @@ DWORD ftype; #endif - if (!_PyVerify_fd(fd)) { + if (!_Py_VERIFY_FD(fd)) { PyErr_SetFromErrno(PyExc_OSError); return -1; } #ifdef MS_WINDOWS + _Py_BEGIN_SUPPRESS_IPH handle = (HANDLE)_get_osfhandle(fd); + _Py_END_SUPPRESS_IPH if (handle == INVALID_HANDLE_VALUE) { PyErr_SetFromWindowsErr(0); return -1; @@ -1174,7 +1188,9 @@ ftype = GetFileType(handle); Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH fd = dup(fd); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS if (fd < 0) { PyErr_SetFromErrno(PyExc_OSError); @@ -1184,13 +1200,17 @@ /* Character files like console cannot be make non-inheritable */ if (ftype != FILE_TYPE_CHAR) { if (_Py_set_inheritable(fd, 0, NULL) < 0) { + _Py_BEGIN_SUPPRESS_IPH close(fd); + _Py_END_SUPPRESS_IPH return -1; } } #elif defined(HAVE_FCNTL_H) && defined(F_DUPFD_CLOEXEC) Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH fd = fcntl(fd, F_DUPFD_CLOEXEC, 0); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS if (fd < 0) { PyErr_SetFromErrno(PyExc_OSError); @@ -1199,7 +1219,9 @@ #else Py_BEGIN_ALLOW_THREADS + _Py_BEGIN_SUPPRESS_IPH fd = dup(fd); + _Py_END_SUPPRESS_IPH Py_END_ALLOW_THREADS if (fd < 0) { PyErr_SetFromErrno(PyExc_OSError); @@ -1267,3 +1289,104 @@ } #endif +#ifdef _MSC_VER +#if _MSC_VER >= 1900 + +/* This function lets the Windows CRT validate the file handle without + terminating the process if it's invalid. */ +int +_PyVerify_fd(int fd) +{ + intptr_t osh; + /* Fast check for the only condition we know */ + if (fd < 0) { + _set_errno(EBADF); + return 0; + } + _Py_BEGIN_SUPPRESS_IPH + osh = _get_osfhandle(fd); + _Py_END_SUPPRESS_IPH + return osh != (intptr_t)-1; +} + +#elif _MSC_VER >= 1400 +/* Legacy implementation of _PyVerify_fd while transitioning to + * MSVC 14.0. This should eventually be removed. (issue23524) + */ + +/* Microsoft CRT in VS2005 and higher will verify that a filehandle is + * valid and raise an assertion if it isn't. + * Normally, an invalid fd is likely to be a C program error and therefore + * an assertion can be useful, but it does contradict the POSIX standard + * which for write(2) states: + * "Otherwise, -1 shall be returned and errno set to indicate the error." + * "[EBADF] The fildes argument is not a valid file descriptor open for + * writing." + * Furthermore, python allows the user to enter any old integer + * as a fd and should merely raise a python exception on error. + * The Microsoft CRT doesn't provide an official way to check for the + * validity of a file descriptor, 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()) + */ +/* The actual size of the structure is determined at runtime. + * Only the first items must be present. + */ +typedef struct { + intptr_t osfhnd; + char osfile; +} my_ioinfo; + +extern __declspec(dllimport) char * __pioinfo[]; +#define IOINFO_L2E 5 +#define IOINFO_ARRAYS 64 +#define IOINFO_ARRAY_ELTS (1 << IOINFO_L2E) +#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); + + static size_t sizeof_ioinfo = 0; + + /* Determine the actual size of the ioinfo structure, + * as used by the CRT loaded in memory + */ + if (sizeof_ioinfo == 0 && __pioinfo[0] != NULL) { + sizeof_ioinfo = _msize(__pioinfo[0]) / IOINFO_ARRAY_ELTS; + } + if (sizeof_ioinfo == 0) { + /* This should not happen... */ + goto fail; + } + + /* 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 */ + my_ioinfo* info = (my_ioinfo*)(__pioinfo[i1] + i2 * sizeof_ioinfo); + if (info->osfile & FOPEN) { + return 1; + } + } + } + fail: + errno = EBADF; + return 0; +} + +#endif /* _MSC_VER >= 1900 || _MSC_VER >= 1400 */ +#endif /* defined _MSC_VER */ diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -1066,13 +1066,14 @@ is_valid_fd(int fd) { int dummy_fd; - if (fd < 0 || !_PyVerify_fd(fd)) + if (fd < 0 || !_Py_VERIFY_FD(fd)) return 0; + _Py_BEGIN_SUPPRESS_IPH dummy_fd = dup(fd); - if (dummy_fd < 0) - return 0; - close(dummy_fd); - return 1; + if (dummy_fd >= 0) + close(dummy_fd); + _Py_END_SUPPRESS_IPH + return dummy_fd >= 0; } /* Initialize sys.stdin, stdout, stderr and builtins.open */