Skip to content

Commit

Permalink
win32,python3: Fix crash when stdin is redirected
Browse files Browse the repository at this point in the history
This causes crash on Windows when Vim is linked with Python 3.5 or later:
```
echo 1 | vim -c "py3 print(1)" -
```

Python 3.5 or later calls `io.py` in `Py_Initialize()`, and it calls
`abort()` when stdin is redirected. This can be avoided by reconnecting
stdin to CONIN$.

However, we should consider that the python DLL and the Vim executable
might be linked to different C runtime libraries. This tries to get
stdin and freopen which are actually used in the python DLL, then calls
`freopen("CONIN$", "r", stdin)` to reconnect stdin.
  • Loading branch information
k-takata committed Aug 6, 2020
1 parent bbd3e3c commit d12b1fe
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/Make_cyg_ming.mak
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,8 @@ ifdef PYTHON3
CFLAGS += -DFEAT_PYTHON3
ifeq (yes, $(DYNAMIC_PYTHON3))
CFLAGS += -DDYNAMIC_PYTHON3 -DDYNAMIC_PYTHON3_DLL=\"$(DYNAMIC_PYTHON3_DLL)\"
else
CFLAGS += -DPYTHON3_DLL=\"$(DYNAMIC_PYTHON3_DLL)\"
endif
endif

Expand Down
6 changes: 5 additions & 1 deletion src/Make_mvc.mak
Original file line number Diff line number Diff line change
Expand Up @@ -1026,6 +1026,9 @@ PYTHON_LIB = $(PYTHON)\libs\python$(PYTHON_VER).lib
! ifndef PYTHON3_VER
PYTHON3_VER = 36
! endif
! ifndef DYNAMIC_PYTHON3_DLL
DYNAMIC_PYTHON3_DLL = python$(PYTHON3_VER).dll
! endif
! message Python3 requested (version $(PYTHON3_VER)) - root dir is "$(PYTHON3)"
! if "$(DYNAMIC_PYTHON3)" == "yes"
! message Python3 DLL will be loaded dynamically
Expand All @@ -1035,9 +1038,10 @@ PYTHON3_OBJ = $(OUTDIR)\if_python3.obj
PYTHON3_INC = /I "$(PYTHON3)\Include" /I "$(PYTHON3)\PC"
! if "$(DYNAMIC_PYTHON3)" == "yes"
CFLAGS = $(CFLAGS) -DDYNAMIC_PYTHON3 \
-DDYNAMIC_PYTHON3_DLL=\"python$(PYTHON3_VER).dll\"
-DDYNAMIC_PYTHON3_DLL=\"$(DYNAMIC_PYTHON3_DLL)\"
PYTHON3_LIB = /nodefaultlib:python$(PYTHON3_VER).lib
! else
CFLAGS = $(CFLAGS) -DPYTHON3_DLL=\"$(DYNAMIC_PYTHON3_DLL)\"
PYTHON3_LIB = $(PYTHON3)\libs\python$(PYTHON3_VER).lib
! endif
!endif
Expand Down
42 changes: 42 additions & 0 deletions src/if_python3.c
Original file line number Diff line number Diff line change
Expand Up @@ -907,6 +907,47 @@ python3_loaded(void)

static wchar_t *py_home_buf = NULL;

#if defined(MSWIN) && (PY_VERSION_HEX >= 0x030500f0)
// Python 3.5 or later will abort inside Py_Initialize() when stdin is
// redirected. Reconnect stdin to CONIN$.
// Note that the python DLL is linked to its own stdio DLL which can be
// differ from Vim's stdio.
static void
reset_stdin(void)
{
FILE *(*py__acrt_iob_func)(unsigned) = NULL;
FILE *(*pyfreopen)(const char *, const char *, FILE *) = NULL;
HINSTANCE hinst;

# ifdef DYNAMIC_PYTHON3
hinst = hinstPy3;
# else
hinst = GetModuleHandle(PYTHON3_DLL);
# endif
if (hinst == NULL)
return;

// Get "freopen" and "stdin" which are used in the python DLL.
// "stdin" is defined as "__acrt_iob_func(0)" in VC++ 2015 or later.
py__acrt_iob_func = get_dll_import_func(hinst, "__acrt_iob_func");
if (py__acrt_iob_func)
{
HINSTANCE hpystdiodll = find_imported_module_by_funcname(hinst,
"__acrt_iob_func");
if (hpystdiodll)
pyfreopen = (void*)GetProcAddress(hpystdiodll, "freopen");
}

// Reconnect stdin to CONIN$.
if (pyfreopen)
pyfreopen("CONIN$", "r", py__acrt_iob_func(0));
else
freopen("CONIN$", "r", stdin);
}
#else
# define reset_stdin()
#endif

static int
Python3_Init(void)
{
Expand Down Expand Up @@ -939,6 +980,7 @@ Python3_Init(void)

PyImport_AppendInittab("vim", Py3Init_vim);

reset_stdin();
Py_Initialize();

// Initialise threads, and below save the state using
Expand Down

0 comments on commit d12b1fe

Please sign in to comment.