swift-foundation icon indicating copy to clipboard operation
swift-foundation copied to clipboard

FileManager.isExecutableFile fails for wow64 processes

Open jakepetroules opened this issue 1 year ago • 2 comments

The following code, if compiled as an x86_64 process and run on an arm64 system, will claim that the command shell binary is not executable. This is because GetBinaryTypeW returns ERROR_BAD_EXE_FORMAT for wow64 processes.

This is not necessarily a bug in Foundation, but a quirk in the Win32 APIs. It seems worth documenting this caveat, at least.

import Foundation
import WinSDK

let fm = FileManager()
let path = "C:\\Windows\\system32\\cmd.exe"
print("\(path) isExecutable = \(fm.isExecutableFile(atPath: path))") // false

var dwBinaryType: DWORD = .max
let ret = path.withCString(encodedAs: UTF16.self) { ptr in
    GetBinaryTypeW(ptr, &dwBinaryType)
}
let err = GetLastError()
print("\(ret) \(dwBinaryType) \(err)")

// err == ERROR_BAD_EXE_FORMAT

jakepetroules avatar Aug 16 '24 00:08 jakepetroules

Thanks for catching this, I wonder if there's an alternative API that we can be using here that would allow us to determine if the file is executable even if the architectures don't match (or perhaps if we call GetBinaryTypeW and the error is ERROR_BAD_EXE_FORMAT, does that indicate that it is executable and we should add a special case to return true in that scenario?

jmschonfeld avatar Aug 19 '24 21:08 jmschonfeld

Apparently you'd have to parse the PE file manually: https://stackoverflow.com/a/44338280

jakepetroules avatar Aug 22 '24 03:08 jakepetroules

Actually, this may work:

func GetExecutableType(_ path: String) -> Bool {
    (path.withCString(encodedAs: UTF16.self) { SHGetFileInfoW($0, 0, nil, 0, numericCast(SHGFI_EXETYPE)) } & 0xFFFF) != 0
}

I've tested this and it seems to work for the relevant cases.

jakepetroules avatar Apr 10 '25 21:04 jakepetroules