Skip to content

Judge executables with API on Windows #2956

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 5 commits into from
Jan 6, 2021

Conversation

stevapple
Copy link
Contributor

@stevapple stevapple commented Dec 21, 2020

Use GetBinaryTypeW function to judge whether a file is executable on Windows.

@stevapple stevapple changed the title Judge executables with API on Windows [DNM] Judge executables with API on Windows Dec 21, 2020
@stevapple stevapple changed the base branch from main to release/5.4 December 21, 2020 14:02
@stevapple stevapple changed the title [DNM] Judge executables with API on Windows [DNM][5.4] Judge executables with API on Windows Dec 21, 2020
@spevans
Copy link
Contributor

spevans commented Dec 21, 2020

This PR would need to go into the main branch first before being backported to 5.4 (assuming the swiftlang/swift#35176 PR is also backported)

@stevapple stevapple changed the base branch from release/5.4 to main December 21, 2020 14:42
@stevapple
Copy link
Contributor Author

I see, thanks for pointing out @spevans

@stevapple stevapple changed the title [DNM][5.4] Judge executables with API on Windows [DNM] Judge executables with API on Windows Dec 21, 2020
Copy link
Member

@compnerd compnerd left a comment

Choose a reason for hiding this comment

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

This would break testing at the very least - .xctest is not an executable extension but is used to execute test bundles. I'm also concerned about how it treats something as being trustworthy. Do you have additional details on that?

@stevapple
Copy link
Contributor Author

@compnerd You may check out: https://docs.microsoft.com/en-us/windows/win32/api/winsafer/nf-winsafer-saferiisexecutablefiletype

This API comes from winsafer.h, which provides a set of APIs regarding Windows Safety Policies. These policies can be managed through Secpol.msc.

@stevapple
Copy link
Contributor Author

stevapple commented Dec 21, 2020

I think we should treat cases like xctest as exceptions here. Of course, it would be better if we can register it system-wide though some API (in the installer).

Update: Okay, I’ve found that. The list of registered executable file extensions lies in HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Windows\safer\codeidentifiers\ExecutableTypes. However, this item will only appear if we manually enable Software Restriction Policy, so the exception way may be better.

@stevapple
Copy link
Contributor Author

stevapple commented Dec 22, 2020

Latest update: I’ve searched through GitHub for all the related keywords, but none is used for discovering a xctest file... I think it’s totally safe to use this API, since it’s strictly aligned with system shell behavior and it works well in my environment.

The ultimate update: Managed to get identifying xctest(and many more) fixed. Now COFF executables with custom extensions will also be identified.

@stevapple
Copy link
Contributor Author

@compnerd I've got a question now: What is this API intended for? To identify a general-meaning "executable file", or to determine whether the file can be executed through CreateProcess? If is the latter one, I think GetBinaryType is enough here.

@stevapple stevapple changed the title [DNM] Judge executables with API on Windows Judge executables with API on Windows Dec 22, 2020
@compnerd
Copy link
Member

On Windows, it's largely meant for checking if the file can be executed (that is, can we just give it to CreateProcessW) as there is no executable bit on Windows. I think that using GetBinaryTypeW is a really good idea.

@stevapple
Copy link
Contributor Author

Then I think it’s ready for merge :)

@lxbndr
Copy link
Contributor

lxbndr commented Dec 22, 2020

Could it be tested somehow? Maybe against TestFoundation executable itself.

@compnerd
Copy link
Member

@lxbndr - yes, TestFileManager.test_isExecutableFile should cover that case

@compnerd
Copy link
Member

@stevapple
Copy link
Contributor Author

stevapple commented Dec 23, 2020

@lxbndr It’s hard to test against itself since the full path of the test suite seems hard to get (CommandLine.arguments[0] is not reliable, as it can be a relative path). If is necessary, I think we should test against system executables like cmd.exe because their paths are quite the same across machines.

@spevans
Copy link
Contributor

spevans commented Dec 23, 2020

@stevapple You could try the following, it works ok on Linux so it hopefully works on Windows as well

let testFoundationBinary = try XCTUnwrap(testBundle().url(forAuxiliaryExecutable: "TestFoundation")?.path)
XCTAssertTrue(fm.isExecutableFile(atPath: testFoundationBinary)) 

It would be good if the test can be made the same on all platforms avoiding #if os(..) tests if possible. It might also be worth creating a file with a .exe extension (or modify the file that is created in the text to have that extension) and check that its not executable - assuming windows actually looks at the PE header of the file.

What does windows do for .bat files? Does it just check for the files existence?

@stevapple
Copy link
Contributor Author

stevapple commented Dec 23, 2020

What does windows do for .bat files? Does it just check for the files existence?

For CreateProcessW, script and batch files will need to be executed through their interpreters. That is, cmd.exe for .bat, wscript.exe for .vbs, etc. To execute them, the interpreter path needs to be explicitly passed to the first parameter of CreateProcessW, where Swift will always pass NULL, so we just don’t need to consider script files now.

// test unExecutable even if file has an `exe` extension
try fm.copyItem(atPath: path, toPath: exePath)
XCTAssertFalse(fm.isExecutableFile(atPath: exePath))
#else
Copy link
Contributor

Choose a reason for hiding this comment

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

This test could still be run on Linux where it would also pass so I would suggest removing the #if os(Windows) and change the #else to #if !os(Windows) so this test is run on all platforms.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This could run, but it’s meaningless. The two different tests actually reflect different behavior on POSIX and Windows systems, which I think should be parallel.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ideally we want to run as many of the tests across all platforms and reduce the number of platform specific tests. In this case I would suggest adding the .exe extension to the original filename, eliminate the copy and then the 0 byte .exe file should then pass as non-executable on all platforms.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I still think merging these tests would lead to confusion, and the current tests can reflect actual cases... It's not about "running as many tests across platforms". Testing with ".exe" extension makes no sense on Linux and macOS, nor does testing POSIX permissions on Windows.

Copy link
Contributor

Choose a reason for hiding this comment

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

I would simply add a comment above the file saying that it is intended to be seen as an not executable by windows even though it has a .exe extension since it lacks the appropriate header, and not executable by unix since it lacks the appropriate execute bit.

@stevapple
Copy link
Contributor Author

Anyone to push forward this pull request?

@compnerd
Copy link
Member

compnerd commented Jan 5, 2021

@compnerd
Copy link
Member

compnerd commented Jan 5, 2021

@swift-ci please test

@spevans
Copy link
Contributor

spevans commented Jan 5, 2021

@stevapple Please squash the commits down into one commit, thanks.

@compnerd compnerd merged commit 8a87b13 into swiftlang:main Jan 6, 2021
stevapple added a commit to stevapple/swift-corelibs-foundation that referenced this pull request Feb 20, 2021
Use `GetBinaryTypeW` to identify if the file is an executable file.
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.

4 participants