]> granicus.if.org Git - python/commitdiff
Issue 4804. Add a function to test the validity of file descriptors on Windows,...
authorKristján Valur Jónsson <kristjan@ccpgames.com>
Tue, 10 Feb 2009 13:32:24 +0000 (13:32 +0000)
committerKristján Valur Jónsson <kristjan@ccpgames.com>
Tue, 10 Feb 2009 13:32:24 +0000 (13:32 +0000)
Include/fileobject.h
Modules/_fileio.c
Modules/posixmodule.c
Objects/exceptions.c

index 56cf40a364725107f80f5745e9f128bba6db73c9..1fed47b895730086292365bc524ada14f8d295a2 100644 (file)
@@ -68,6 +68,17 @@ size_t Py_UniversalNewlineFread(char *, size_t, FILE *, PyObject *);
 */
 int _PyFile_SanitizeMode(char *mode);
 
+#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
+
 #ifdef __cplusplus
 }
 #endif
index 2fc6de887a8b2d347b9d63a7c7f5f36db85647ff..409abf6e0faa3f7090956acc45af61ffbc938d2d 100644 (file)
@@ -127,7 +127,7 @@ 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 cda8135c486858f48d0aa32a785672d20e1b4572..4f27cd1c6006e2e2385fbc5414e4b6875a60259e 100644 (file)
@@ -343,6 +343,109 @@ extern int lstat(const char *, struct stat *);
 #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 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 _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;
+#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 version. _PyVerify_fd() is already defined in fileobject.h */
+#define _PyVerify_fd_dup2(A, B) (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 +684,8 @@ posix_fildes(PyObject *fdobj, int (*func)(int))
        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 +6293,8 @@ posix_close(PyObject *self, PyObject *args)
        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 +6317,8 @@ posix_closerange(PyObject *self, PyObject *args)
                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 +6334,8 @@ posix_dup(PyObject *self, PyObject *args)
        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 +6355,8 @@ posix_dup2(PyObject *self, PyObject *args)
        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 +6401,8 @@ posix_lseek(PyObject *self, PyObject *args)
        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 +6439,8 @@ posix_read(PyObject *self, PyObject *args)
        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 +6467,8 @@ posix_write(PyObject *self, PyObject *args)
 
        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 +6495,8 @@ posix_fstat(PyObject *self, PyObject *args)
         /* 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 +6539,8 @@ posix_fdopen(PyObject *self, PyObject *args)
                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 +6580,8 @@ posix_isatty(PyObject *self, PyObject *args)
        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 99cfb2b15d00188300b4b514183566523bbc7c26..2f2a330f923edab17a95a8c9401ae3b536b77549 100644 (file)
@@ -1972,28 +1972,6 @@ static PyMethodDef functions[] = {
     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 <crtdbg.h>
-
-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 @@ _PyExc_Init(void)
     }
 
     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 @@ _PyExc_Fini(void)
 {
     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
 }