From 028712f43d373dc15c67706197b45dad019df7ab Mon Sep 17 00:00:00 2001 From: Vincent Cunningham Date: Wed, 17 Jan 2024 18:46:54 -0800 Subject: [PATCH 1/8] pylauncher: Ignore shebang if APPEXECLINK found in PATH --- PC/launcher2.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/PC/launcher2.c b/PC/launcher2.c index 2a8f8a101fc8a6..d6a0ae58f97c8f 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -834,6 +834,16 @@ searchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength) return RC_RECURSIVE_SHEBANG; } + // Make sure we didn't find a reparse point that might open the Microsoft Store + // If we are, pretend there was no shebang and let normal handling take over + WIN32_FIND_DATAW findData; + wchar_t hFind = FindFirstFileW(buffer, &findData); + if (findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + if (findData.dwReserved0 & IO_REPARSE_TAG_APPEXECLINK) { + return RC_NO_SHEBANG; + } + } + wchar_t *buf = allocSearchInfoBuffer(search, n + 1); if (!buf || wcscpy_s(buf, n + 1, buffer)) { return RC_NO_MEMORY; From 5fa3ca9517463b37699057648a7baa150ce80305 Mon Sep 17 00:00:00 2001 From: Vincent Cunningham Date: Wed, 17 Jan 2024 22:34:14 -0800 Subject: [PATCH 2/8] pylauncher: Only ignore alias that opens app installer --- PC/launcher2.c | 41 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/PC/launcher2.c b/PC/launcher2.c index d6a0ae58f97c8f..d82e16c25829ad 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -571,6 +571,21 @@ findArgv0End(const wchar_t *buffer, int bufferLength) /******************************************************************************\ *** COMMAND-LINE PARSING *** \******************************************************************************/ +// Adapted from https://stackoverflow.com/a/65583702 +typedef struct AppExecLinkFile { // For tag IO_REPARSE_TAG_APPEXECLINK + DWORD reparseTag; + WORD reparseDataLength; + WORD reserved; + ULONG version; + wchar_t stringList[MAX_PATH * 4]; // Multistring (Consecutive UTF-16 strings each ending with a NUL) + /* There are normally 4 strings here. Ex: + Package ID: L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe" + Entry Point: L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe!PythonRedirector" + Executable: L"C:\Program Files\WindowsApps\Microsoft.DesktopAppInstaller_1.17.106910_x64__8wekyb3d8bbwe\AppInstallerPythonRedirector.exe" + Applic. Type: L"0" // Integer as ASCII. "0" = Desktop bridge application; Else sandboxed UWP application + */ +} AppExecLinkFile; + int @@ -834,12 +849,30 @@ searchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength) return RC_RECURSIVE_SHEBANG; } - // Make sure we didn't find a reparse point that might open the Microsoft Store - // If we are, pretend there was no shebang and let normal handling take over + // Make sure we didn't find a reparse point that will open the Microsoft Store + // If we did, pretend there was no shebang and let normal handling take over WIN32_FIND_DATAW findData; wchar_t hFind = FindFirstFileW(buffer, &findData); - if (findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { - if (findData.dwReserved0 & IO_REPARSE_TAG_APPEXECLINK) { + if (findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && + findData.dwReserved0 & IO_REPARSE_TAG_APPEXECLINK) { + const size_t bufSize = sizeof(AppExecLinkFile); + wchar_t buf[sizeof(AppExecLinkFile)]; + AppExecLinkFile *appExecLinkFilePtr; + + HANDLE hReparsePoint = CreateFileW(buffer, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL); + DeviceIoControl(hReparsePoint, FSCTL_GET_REPARSE_POINT, NULL, 0, buf, bufSize, NULL, NULL); + CloseHandle(hReparsePoint); + + appExecLinkFilePtr = (AppExecLinkFile*)&buf; + + wchar_t redirectorPackageId[] = L"Microsoft.DesktopAppInstaller"; + int redirectorStrLen = wcsnlen_s(redirectorPackageId, 29); + + wchar_t partialId[30]; + wcsncpy_s(partialId, redirectorStrLen + 1, appExecLinkFilePtr->stringList, redirectorStrLen); + + if (0 == wcsncmp(partialId, &redirectorPackageId, redirectorStrLen)) { + debug(L"# ignoring redirector that would launch store"); return RC_NO_SHEBANG; } } From 8f652d11c84ee29b84b3b1cf246d8395ae1d920f Mon Sep 17 00:00:00 2001 From: Vincent Cunningham Date: Sat, 20 Jan 2024 16:05:39 -0800 Subject: [PATCH 3/8] pylauncher: add error handling --- PC/launcher2.c | 66 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 52 insertions(+), 14 deletions(-) diff --git a/PC/launcher2.c b/PC/launcher2.c index d82e16c25829ad..a94ec2bf0829b5 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -841,29 +841,57 @@ searchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength) return RC_BAD_VIRTUAL_PATH; } - // Check that we aren't going to call ourselves again - // If we are, pretend there was no shebang and let normal handling take over - if (GetModuleFileNameW(NULL, filename, MAXLEN) && - 0 == _comparePath(filename, -1, buffer, -1)) { - debug(L"# ignoring recursive shebang command\n"); - return RC_RECURSIVE_SHEBANG; - } - // Make sure we didn't find a reparse point that will open the Microsoft Store // If we did, pretend there was no shebang and let normal handling take over WIN32_FIND_DATAW findData; - wchar_t hFind = FindFirstFileW(buffer, &findData); + HANDLE hFind = FindFirstFileW(buffer, &findData); + if (!hFind) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + debug(L"# %s on disappeared\n", buffer); + // The file found on PATH disappeared. Alias probably disabled by user while trying to run, let normal handling take over + RC_NO_SHEBANG; + } + + // Other errors should cause us to break + winerror(0, L"Failed to find %s on PATH\n", filename); + return RC_BAD_VIRTUAL_PATH; + } + if (findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && findData.dwReserved0 & IO_REPARSE_TAG_APPEXECLINK) { const size_t bufSize = sizeof(AppExecLinkFile); - wchar_t buf[sizeof(AppExecLinkFile)]; - AppExecLinkFile *appExecLinkFilePtr; + wchar_t appExecLinkBuf[sizeof(AppExecLinkFile)]; HANDLE hReparsePoint = CreateFileW(buffer, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL); - DeviceIoControl(hReparsePoint, FSCTL_GET_REPARSE_POINT, NULL, 0, buf, bufSize, NULL, NULL); + if (!hReparsePoint) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + debug(L"# %s on disappeared\n", buffer); + // The file found on PATH disappeared. Alias probably disabled by user while trying to run, let normal handling take over + RC_NO_SHEBANG; + } + + // Other errors should cause us to break + winerror(0, L"Failed to find %s on PATH\n", filename); + return RC_BAD_VIRTUAL_PATH; + } + + if (!DeviceIoControl(hReparsePoint, FSCTL_GET_REPARSE_POINT, NULL, 0, appExecLinkBuf, bufSize, NULL, NULL)) { + if (GetLastError() == ERROR_FILE_NOT_FOUND) { + debug(L"# %s on disappeared\n", buffer); + // The file found on PATH disappeared. Alias probably disabled by user while trying to run, let normal handling take over + RC_NO_SHEBANG; + } + + // Other errors should cause us to break + winerror(0, L"Failed to find %s on PATH\n", filename); + return RC_BAD_VIRTUAL_PATH; + } + CloseHandle(hReparsePoint); - appExecLinkFilePtr = (AppExecLinkFile*)&buf; + AppExecLinkFile* appExecLinkFilePtr; + + appExecLinkFilePtr = (AppExecLinkFile*)&appExecLinkBuf; wchar_t redirectorPackageId[] = L"Microsoft.DesktopAppInstaller"; int redirectorStrLen = wcsnlen_s(redirectorPackageId, 29); @@ -872,11 +900,21 @@ searchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength) wcsncpy_s(partialId, redirectorStrLen + 1, appExecLinkFilePtr->stringList, redirectorStrLen); if (0 == wcsncmp(partialId, &redirectorPackageId, redirectorStrLen)) { - debug(L"# ignoring redirector that would launch store"); + debug(L"# ignoring redirector that would launch store\n"); return RC_NO_SHEBANG; } } + FindClose(hFind); + + // Check that we aren't going to call ourselves again + // If we are, pretend there was no shebang and let normal handling take over + if (GetModuleFileNameW(NULL, filename, MAXLEN) && + 0 == _comparePath(filename, -1, buffer, -1)) { + debug(L"# ignoring recursive shebang command\n"); + return RC_RECURSIVE_SHEBANG; + } + wchar_t *buf = allocSearchInfoBuffer(search, n + 1); if (!buf || wcscpy_s(buf, n + 1, buffer)) { return RC_NO_MEMORY; From adca3a9cd0a295779fbc8918e6c54ed61ca672eb Mon Sep 17 00:00:00 2001 From: Vincent Cunningham Date: Mon, 22 Jan 2024 15:53:03 -0800 Subject: [PATCH 4/8] pylauncher: Address code review move new code to own function simplify error handling simplify struct handling simplify string comparison --- PC/launcher2.c | 115 ++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 64 deletions(-) diff --git a/PC/launcher2.c b/PC/launcher2.c index a94ec2bf0829b5..bd4d95e22cd1f2 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -778,6 +778,54 @@ _shebangStartsWith(const wchar_t *buffer, int bufferLength, const wchar_t *prefi } +int +ensure_no_redirector_stub(wchar_t* filename, wchar_t* buffer) +{ + // Make sure we didn't find a reparse point that will open the Microsoft Store + // If we did, pretend there was no shebang and let normal handling take over + WIN32_FIND_DATAW findData; + HANDLE hFind = FindFirstFileW(buffer, &findData); + if (!hFind) { + // Let normal handling take over + debug(L"# Did not find %s on PATH\n", filename); + return RC_NO_SHEBANG; + } + + FindClose(hFind); + + if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && + findData.dwReserved0 & IO_REPARSE_TAG_APPEXECLINK)) { + return 0; + } + + AppExecLinkFile appExecLink; + + HANDLE hReparsePoint = CreateFileW(buffer, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL); + if (!hReparsePoint) { + // Let normal handling take over + debug(L"# Did not find %s on PATH\n", filename); + return RC_NO_SHEBANG; + } + + if (!DeviceIoControl(hReparsePoint, FSCTL_GET_REPARSE_POINT, NULL, 0, &appExecLink, sizeof(appExecLink), NULL, NULL)) { + // Let normal handling take over + debug(L"# Did not find %s on PATH\n", filename); + return RC_NO_SHEBANG; + } + + CloseHandle(hReparsePoint); + + const wchar_t* redirectorPackageId = L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe"; + + if (0 == wcscmp(appExecLink.stringList, redirectorPackageId)) { + debug(L"# ignoring redirector that would launch store\n"); + return RC_NO_SHEBANG; + } + + return 0; +} + + int searchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength) { @@ -841,72 +889,11 @@ searchPath(SearchInfo *search, const wchar_t *shebang, int shebangLength) return RC_BAD_VIRTUAL_PATH; } - // Make sure we didn't find a reparse point that will open the Microsoft Store - // If we did, pretend there was no shebang and let normal handling take over - WIN32_FIND_DATAW findData; - HANDLE hFind = FindFirstFileW(buffer, &findData); - if (!hFind) { - if (GetLastError() == ERROR_FILE_NOT_FOUND) { - debug(L"# %s on disappeared\n", buffer); - // The file found on PATH disappeared. Alias probably disabled by user while trying to run, let normal handling take over - RC_NO_SHEBANG; - } - - // Other errors should cause us to break - winerror(0, L"Failed to find %s on PATH\n", filename); - return RC_BAD_VIRTUAL_PATH; - } - - if (findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT && - findData.dwReserved0 & IO_REPARSE_TAG_APPEXECLINK) { - const size_t bufSize = sizeof(AppExecLinkFile); - wchar_t appExecLinkBuf[sizeof(AppExecLinkFile)]; - - HANDLE hReparsePoint = CreateFileW(buffer, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL); - if (!hReparsePoint) { - if (GetLastError() == ERROR_FILE_NOT_FOUND) { - debug(L"# %s on disappeared\n", buffer); - // The file found on PATH disappeared. Alias probably disabled by user while trying to run, let normal handling take over - RC_NO_SHEBANG; - } - - // Other errors should cause us to break - winerror(0, L"Failed to find %s on PATH\n", filename); - return RC_BAD_VIRTUAL_PATH; - } - - if (!DeviceIoControl(hReparsePoint, FSCTL_GET_REPARSE_POINT, NULL, 0, appExecLinkBuf, bufSize, NULL, NULL)) { - if (GetLastError() == ERROR_FILE_NOT_FOUND) { - debug(L"# %s on disappeared\n", buffer); - // The file found on PATH disappeared. Alias probably disabled by user while trying to run, let normal handling take over - RC_NO_SHEBANG; - } - - // Other errors should cause us to break - winerror(0, L"Failed to find %s on PATH\n", filename); - return RC_BAD_VIRTUAL_PATH; - } - - CloseHandle(hReparsePoint); - - AppExecLinkFile* appExecLinkFilePtr; - - appExecLinkFilePtr = (AppExecLinkFile*)&appExecLinkBuf; - - wchar_t redirectorPackageId[] = L"Microsoft.DesktopAppInstaller"; - int redirectorStrLen = wcsnlen_s(redirectorPackageId, 29); - - wchar_t partialId[30]; - wcsncpy_s(partialId, redirectorStrLen + 1, appExecLinkFilePtr->stringList, redirectorStrLen); - - if (0 == wcsncmp(partialId, &redirectorPackageId, redirectorStrLen)) { - debug(L"# ignoring redirector that would launch store\n"); - return RC_NO_SHEBANG; - } + int result = ensure_no_redirector_stub(filename, buffer); + if (result) { + return result; } - FindClose(hFind); - // Check that we aren't going to call ourselves again // If we are, pretend there was no shebang and let normal handling take over if (GetModuleFileNameW(NULL, filename, MAXLEN) && From 9783c954c3231d7cfd81e28db537b608158397fb Mon Sep 17 00:00:00 2001 From: "blurb-it[bot]" <43283697+blurb-it[bot]@users.noreply.github.com> Date: Tue, 23 Jan 2024 00:05:05 +0000 Subject: [PATCH 5/8] =?UTF-8?q?=F0=9F=93=9C=F0=9F=A4=96=20Added=20by=20blu?= =?UTF-8?q?rb=5Fit.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../next/Windows/2024-01-23-00-05-05.gh-issue-100107.lkbP_Q.rst | 1 + 1 file changed, 1 insertion(+) create mode 100644 Misc/NEWS.d/next/Windows/2024-01-23-00-05-05.gh-issue-100107.lkbP_Q.rst diff --git a/Misc/NEWS.d/next/Windows/2024-01-23-00-05-05.gh-issue-100107.lkbP_Q.rst b/Misc/NEWS.d/next/Windows/2024-01-23-00-05-05.gh-issue-100107.lkbP_Q.rst new file mode 100644 index 00000000000000..388d61a2b3bd6d --- /dev/null +++ b/Misc/NEWS.d/next/Windows/2024-01-23-00-05-05.gh-issue-100107.lkbP_Q.rst @@ -0,0 +1 @@ +The ``py.exe`` launcher will no longer attempt to run the Microsoft Store redirector when launching a script containing a ``/usr/bin/env`` shebang From 1cf0235dbf1a8971272d9c91c84309db79815b1d Mon Sep 17 00:00:00 2001 From: Vincent Cunningham Date: Mon, 22 Jan 2024 16:10:13 -0800 Subject: [PATCH 6/8] pylauncher: Move struct allocation closer to usage --- PC/launcher2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/PC/launcher2.c b/PC/launcher2.c index bd4d95e22cd1f2..c31e4de72925fa 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -798,8 +798,6 @@ ensure_no_redirector_stub(wchar_t* filename, wchar_t* buffer) return 0; } - AppExecLinkFile appExecLink; - HANDLE hReparsePoint = CreateFileW(buffer, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL); if (!hReparsePoint) { // Let normal handling take over @@ -807,6 +805,8 @@ ensure_no_redirector_stub(wchar_t* filename, wchar_t* buffer) return RC_NO_SHEBANG; } + AppExecLinkFile appExecLink; + if (!DeviceIoControl(hReparsePoint, FSCTL_GET_REPARSE_POINT, NULL, 0, &appExecLink, sizeof(appExecLink), NULL, NULL)) { // Let normal handling take over debug(L"# Did not find %s on PATH\n", filename); From ba7fe3e7fa58455d8e49aaff7117935b21a1845b Mon Sep 17 00:00:00 2001 From: Vincent Cunningham Date: Mon, 22 Jan 2024 16:53:10 -0800 Subject: [PATCH 7/8] pylauncher: adjust newlines --- PC/launcher2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PC/launcher2.c b/PC/launcher2.c index c31e4de72925fa..9ae7aa4bb4c3c2 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -571,6 +571,7 @@ findArgv0End(const wchar_t *buffer, int bufferLength) /******************************************************************************\ *** COMMAND-LINE PARSING *** \******************************************************************************/ + // Adapted from https://stackoverflow.com/a/65583702 typedef struct AppExecLinkFile { // For tag IO_REPARSE_TAG_APPEXECLINK DWORD reparseTag; @@ -587,7 +588,6 @@ typedef struct AppExecLinkFile { // For tag IO_REPARSE_TAG_APPEXECLINK } AppExecLinkFile; - int parseCommandLine(SearchInfo *search) { From 6c819f05e5e31735135f7b7b4e26eb219d8cead3 Mon Sep 17 00:00:00 2001 From: Vincent Cunningham Date: Wed, 24 Jan 2024 18:27:21 -0500 Subject: [PATCH 8/8] pylauncher: cleanup leaked handle Co-authored-by: Steve Dower --- PC/launcher2.c | 1 + 1 file changed, 1 insertion(+) diff --git a/PC/launcher2.c b/PC/launcher2.c index 9ae7aa4bb4c3c2..e426eccd700044 100644 --- a/PC/launcher2.c +++ b/PC/launcher2.c @@ -810,6 +810,7 @@ ensure_no_redirector_stub(wchar_t* filename, wchar_t* buffer) if (!DeviceIoControl(hReparsePoint, FSCTL_GET_REPARSE_POINT, NULL, 0, &appExecLink, sizeof(appExecLink), NULL, NULL)) { // Let normal handling take over debug(L"# Did not find %s on PATH\n", filename); + CloseHandle(hReparsePoint); return RC_NO_SHEBANG; }