From 00164b4a9821e82f513183035587bea9243a7d5e Mon Sep 17 00:00:00 2001
From: Jean-Christophe Fillion-Robin <jchris.fillionr@kitware.com>
Date: Tue, 1 Aug 2017 14:13:24 -0400
Subject: [PATCH 3/3] VS2015 Support: Backport of "Issue #23524: Replace
 _PyVerify_fd function with calling
 _set_thread_local_invalid_parameter_handler on every thread."

This commit is a partial backport of python/cpython@d81431f. It was
originally designed to work with python-cmake-buildsystem.

Implementation of "_PyVerify_fd" in "Python/fileutils.c" found only in
Python 3.x has been copied into "Modules/posixmodule.c"

The following modules have NOT been backported:

* PCbuild
---
 Modules/posixmodule.c              | 54 +++++++++++++++++++++++-------------------
 PC/invalid_parameter_handler.c     | 22 +++++++++++++++++
 Python/pystate.c                   | 12 ++++++++++
 PCbuild/pythoncore.vcxproj         | 1  +
 PCbuild/pythoncore.vcxproj.filters | 1  +
 5 files changed, 65 insertions(+), 26 deletions(-)
 create mode 100644 PC/invalid_parameter_handler.c

diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c
index 90d5318..6a180a0 100644
--- a/Modules/posixmodule.c
+++ b/Modules/posixmodule.c
@@ -277,6 +277,7 @@ extern int lstat(const char *, struct stat *);
 #include "osdefs.h"
 #include <malloc.h>
 #include <windows.h>
+#include <malloc.h>
 #include <shellapi.h>   /* for ShellExecute() */
 #define popen   _popen
 #define pclose  _pclose
@@ -535,8 +534,28 @@ _PyInt_FromDev(PY_LONG_LONG v)
 #  define _PyInt_FromDev PyInt_FromLong
 #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;
+    }
+    osh = _get_osfhandle(fd);
+    return osh != (intptr_t)-1;
+}
+
+#define _PyVerify_fd_dup2(fd1, fd2) (_PyVerify_fd(fd1) && (fd2) >= 0)
+
+#elif _MSC_VER >= 1400
 
-#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
@@ -601,35 +580,18 @@ _PyInt_FromDev(PY_LONG_LONG v)
  * 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
+#define IOINFO_L2E 5
+#define IOINFO_ARRAYS 64
 
 /* This function emulates what the windows CRT does to validate file handles */
 int
@@ -653,6 +649,8 @@ _PyVerify_fd_dup2(int fd1, int fd2)
 #define _PyVerify_fd_dup2(A, B) (1)
 #endif
 
+#endif /* defined _MSC_VER */
+
 /* Return a dictionary corresponding to the POSIX environment table */
 #if defined(WITH_NEXT_FRAMEWORK) || (defined(__APPLE__) && defined(Py_ENABLE_SHARED))
 /* On Darwin/MacOSX a shared library or framework has no access to
@@ -1260,14 +1254,10 @@ win32_fstat(int file_number, struct win32_stat *result)
 
     h = (HANDLE)_get_osfhandle(file_number);
 
-    /* Protocol violation: we explicitly clear errno, instead of
-       setting it to a POSIX error. Callers should use GetLastError. */
     errno = 0;
 
     if (h == INVALID_HANDLE_VALUE) {
-        /* This is really a C library error (invalid file handle).
-           We set the Win32 error to the closes one matching. */
-        SetLastError(ERROR_INVALID_HANDLE);
+        errno = EBADF;
         return -1;
     }
     memset(result, 0, sizeof(*result));
@@ -1268,6 +1266,7 @@ win32_fstat(int file_number, struct win32_stat *result)
     if (type == FILE_TYPE_UNKNOWN) {
         DWORD error = GetLastError();
         if (error != 0) {
+        errno = EINVAL;
         return -1;
         }
         /* else: valid but unknown file */
@@ -1284,6 +1281,7 @@ win32_fstat(int file_number, struct win32_stat *result)
     }
 
     if (!GetFileInformationByHandle(h, &info)) {
+        errno = EINVAL;
         return -1;
     }
 
diff --git a/PC/invalid_parameter_handler.c b/PC/invalid_parameter_handler.c
new file mode 100644
index 0000000..3bc0104
--- /dev/null
+++ b/PC/invalid_parameter_handler.c
@@ -0,0 +1,22 @@
+#ifdef _MSC_VER
+
+#include <stdlib.h>
+
+#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/Python/pystate.c b/Python/pystate.c
index eb992c1..1c0f970 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -22,6 +22,12 @@ the expense of doing their own locking).
 #endif
 #endif
 
+#if defined _MSC_VER && _MSC_VER >= 1900
+/* Issue #23524: Temporary fix to disable termination due to invalid parameters */
+PyAPI_DATA(void*) _Py_silent_invalid_parameter_handler;
+#include <stdlib.h>
+#endif
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -202,6 +208,12 @@ new_threadstate(PyInterpreterState *interp, int init)
         tstate->next = interp->tstate_head;
         interp->tstate_head = tstate;
         HEAD_UNLOCK();
+
+#if defined _MSC_VER && _MSC_VER >= 1900
+        /* Issue #23524: Temporary fix to disable termination due to invalid parameters */
+        _set_thread_local_invalid_parameter_handler((_invalid_parameter_handler)_Py_silent_invalid_parameter_handler);
+#endif
+
     }
 
     return tstate;
-- 
2.5.0

diff --git a/PCbuild/pythoncore.vcxproj b/PCbuild/pythoncore.vcxproj
index 99291ea..af17762 100644
--- a/PCbuild/pythoncore.vcxproj
+++ b/PCbuild/pythoncore.vcxproj
@@ -333,6 +333,7 @@
     <ClCompile Include="..\Parser\parser.c" />
     <ClCompile Include="..\Parser\parsetok.c" />
     <ClCompile Include="..\Parser\tokenizer.c" />
+    <ClCompile Include="..\PC\invalid_parameter_handler.c" />
     <ClCompile Include="..\PC\_subprocess.c" />
     <ClCompile Include="..\PC\_winreg.c" />
     <ClCompile Include="..\PC\config.c" />

diff --git a/PCbuild/pythoncore.vcxproj.filters b/PCbuild/pythoncore.vcxproj.filters
index 99291ea..af17762 100644
--- a/PCbuild/pythoncore.vcxproj.filters
+++ b/PCbuild/pythoncore.vcxproj.filters
@@ -794,6 +794,9 @@
     <ClCompile Include="..\Parser\tokenizer.c">
       <Filter>Parser</Filter>
     </ClCompile>
+    <ClCompile Include="..\PC\invalid_parameter_handler.c">
+      <Filter>PC</Filter>
+    </ClCompile>
     <ClCompile Include="..\PC\_subprocess.c">
       <Filter>PC</Filter>
     </ClCompile>
