Skip to content

gh-100107: Make Py launcher ignore app aliases that launch Microsoft Store #114358

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 25, 2024

Conversation

flagrama
Copy link
Contributor

@flagrama flagrama commented Jan 20, 2024

Currently when you use the #!/usr/bin/env python3 shebang on Windows 10 or Windows 11 the Py launcher will search the PATH environment variable and find the default NTFS reparse points called App Execution Aliases Microsoft added to the OSes. These are effectively stub exes that, when Python is not installed from the Microsoft Store, will launch the Microsoft Store to install Python, or if launched with arguments will report an error.

This makes Windows 10/11 the only OS where the standard shebang does not work. It complicated packaging python scripts for ordinary users because they have an extra step, either disabling the app execution aliases, installing from the Microsoft Store, or launching the script manually. Windows ends up being the only OS that doesn't "just work" and changing the shebang to something else will break or degrade the experience in other OSes.

This PR solves this issue by checking if the exe found in the PATH is a reparse point. If it is, it will read the file and identify the alias that will launch the store and will drop out of the shebang handling as if nothing was found in the PATH. If it is an alias that does not launch the store, or an ordinary file it will just continue and use the found file as there shouldn't be any issues.

In my testing this now makes opening a script with the standard #!/usr/bin/env python3 shebang work without additional work by the user.

Note that I am not a Windows user or a Windows developer so it's possible that I've missed some workflow and don't know if it works, or my code may be inefficient. I've only really tested the workflow of the normal users that use a Python application I help develop and know that I have solved the issue there.

@flagrama flagrama requested a review from a team as a code owner January 20, 2024 21:42
@ghost
Copy link

ghost commented Jan 20, 2024

All commit authors signed the Contributor License Agreement.
CLA signed

@bedevere-app
Copy link

bedevere-app bot commented Jan 20, 2024

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

Copy link
Member

@zooba zooba left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's got all the pieces we need, though it can be simplified quite a bit. Would also be ideal to move it to its own function, so that someone reading this function can see from the call what it's doing but they don't have to actually read the details unless they want to.

PC/launcher2.c Outdated
winerror(0, L"Failed to find %s on PATH\n", filename);
return RC_BAD_VIRTUAL_PATH;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be able to FindClose here and not worry about cleaning it up later.

PC/launcher2.c Outdated
@@ -826,6 +841,72 @@ 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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's start by refactoring all this out into another function ensure_no_redirector_stub that either returns 0 (okay) or the RC constant that should be returned from here.

PC/launcher2.c Outdated
}

if (findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT &&
findData.dwReserved0 & IO_REPARSE_TAG_APPEXECLINK) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After refactoring, use this check to exit early from the new function. That will keep the overall indent level lower for the rest of it.

PC/launcher2.c Outdated
Comment on lines 867 to 871
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;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this error adds anything - we could lose this race at any time, so probably best to just return RC_NO_SHEBANG for them all and it can fail if/when we try to launch it.

PC/launcher2.c Outdated
Comment on lines 879 to 883
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;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto - no benefit to calling this one out separately. Normal handling for all errors should be okay

PC/launcher2.c Outdated

appExecLinkFilePtr = (AppExecLinkFile*)&appExecLinkBuf;

wchar_t redirectorPackageId[] = L"Microsoft.DesktopAppInstaller";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
wchar_t redirectorPackageId[] = L"Microsoft.DesktopAppInstaller";
const wchar_t *redirectorPackageId = L"Microsoft.DesktopAppInstaller_8wekyb3d8bbwe";

PC/launcher2.c Outdated
Comment on lines 862 to 863
const size_t bufSize = sizeof(AppExecLinkFile);
wchar_t appExecLinkBuf[sizeof(AppExecLinkFile)];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aren't necessary - just allocate the struct:

AppExecLinkFile appExecLink;

And then use sizeof(appExecLink) anywhere we need it, which ought to only be the DeviceIOControl call.

PC/launcher2.c Outdated
wchar_t partialId[30];
wcsncpy_s(partialId, redirectorStrLen + 1, appExecLinkFilePtr->stringList, redirectorStrLen);

if (0 == wcsncmp(partialId, &redirectorPackageId, redirectorStrLen)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A simple wcscmp ought to be fine here if you use the full package name. As far as I'm aware, the publisher ID has never changed and never will (it's always some kind of hash of "Microsoft").

@bedevere-app
Copy link

bedevere-app bot commented Jan 22, 2024

A Python core developer has requested some changes be made to your pull request before we can consider merging it. If you could please address their requests along with any other requests in other reviews from core developers that would be appreciated.

Once you have made the requested changes, please leave a comment on this pull request containing the phrase I have made the requested changes; please review again. I will then notify any core developers who have left a review that you're ready for them to take another look at this pull request.

@zooba
Copy link
Member

zooba commented Jan 22, 2024

Adding a NEWS entry would be great, too. Just something like The ``py.exe`` launcher will no longer attempt to run the Microsoft Store redirector when launching a script containing a ``/usr/bin/env`` shebang.

move new code to own function
simplify error handling
simplify struct handling
simplify string comparison
@bedevere-app
Copy link

bedevere-app bot commented Jan 22, 2024

Most changes to Python require a NEWS entry. Add one using the blurb_it web app or the blurb command-line tool.

If this change has little impact on Python users, wait for a maintainer to apply the skip news label instead.

@flagrama
Copy link
Contributor Author

I have made the requested changes; please review again.

Everything looks better to me except the actual call to the function and checking it's return value.

Also, is there any chance of this getting backported into the next release of older supported versions?

@zooba
Copy link
Member

zooba commented Jan 23, 2024

One leaked handle and we're good. And yeah, we can backport this to earlier versions, though it's worth noting that once you've installed 3.12 you'll need a later 3.12 to upgrade the installer - it won't downgrade to a 3.11 version, even if it's "newer".

@zooba zooba added needs backport to 3.11 only security fixes needs backport to 3.12 only security fixes labels Jan 23, 2024
Co-authored-by: Steve Dower <steve.dower@microsoft.com>
@zooba zooba merged commit d5c21c1 into python:main Jan 25, 2024
@miss-islington-app
Copy link

Thanks @flagrama for the PR, and @zooba for merging it 🌮🎉.. I'm working now to backport this PR to: 3.11, 3.12.
🐍🍒⛏🤖

miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Jan 25, 2024
…Microsoft Store (pythonGH-114358)

(cherry picked from commit d5c21c1)

Co-authored-by: Vincent Cunningham <flagrama@users.noreply.github.com>
miss-islington pushed a commit to miss-islington/cpython that referenced this pull request Jan 25, 2024
…Microsoft Store (pythonGH-114358)

(cherry picked from commit d5c21c1)

Co-authored-by: Vincent Cunningham <flagrama@users.noreply.github.com>
@zooba
Copy link
Member

zooba commented Jan 25, 2024

Thanks! And congratulations on your first PR!

@bedevere-app
Copy link

bedevere-app bot commented Jan 25, 2024

GH-114541 is a backport of this pull request to the 3.12 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.12 only security fixes label Jan 25, 2024
@bedevere-app
Copy link

bedevere-app bot commented Jan 25, 2024

GH-114542 is a backport of this pull request to the 3.11 branch.

@bedevere-app bedevere-app bot removed the needs backport to 3.11 only security fixes label Jan 25, 2024
zooba pushed a commit that referenced this pull request Jan 25, 2024
…oft Store (GH-114358)

(cherry picked from commit d5c21c1)

Co-authored-by: Vincent Cunningham <flagrama@users.noreply.github.com>
zooba pushed a commit that referenced this pull request Jan 25, 2024
…oft Store (GH-114358)

(cherry picked from commit d5c21c1)

Co-authored-by: Vincent Cunningham <flagrama@users.noreply.github.com>
aisk pushed a commit to aisk/cpython that referenced this pull request Feb 11, 2024
Glyphack pushed a commit to Glyphack/cpython that referenced this pull request Sep 2, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants