gh-131020: py.exe launcher does not correctly detect a BOM when searching for the shebang (GH-131021)

This commit is contained in:
Chris Eibl 2025-03-10 18:59:23 +01:00 committed by GitHub
parent 5a484714c3
commit 36ef3bfe39
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 29 additions and 5 deletions

View file

@ -271,7 +271,10 @@ class RunPyMixin:
@contextlib.contextmanager @contextlib.contextmanager
def script(self, content, encoding="utf-8"): def script(self, content, encoding="utf-8"):
file = Path(tempfile.mktemp(dir=os.getcwd()) + ".py") file = Path(tempfile.mktemp(dir=os.getcwd()) + ".py")
file.write_text(content, encoding=encoding) if isinstance(content, bytes):
file.write_bytes(content)
else:
file.write_text(content, encoding=encoding)
try: try:
yield file yield file
finally: finally:
@ -624,6 +627,25 @@ class TestLauncher(unittest.TestCase, RunPyMixin):
self.assertEqual("3.100", data["SearchInfo.tag"]) self.assertEqual("3.100", data["SearchInfo.tag"])
self.assertEqual(f'X.Y.exe -prearg "{script}" -postarg', data["stdout"].strip()) self.assertEqual(f'X.Y.exe -prearg "{script}" -postarg', data["stdout"].strip())
def test_py_shebang_valid_bom(self):
with self.py_ini(TEST_PY_DEFAULTS):
content = "#! /usr/bin/python -prearg".encode("utf-8")
with self.script(b"\xEF\xBB\xBF" + content) as script:
data = self.run_py([script, "-postarg"])
self.assertEqual("PythonTestSuite", data["SearchInfo.company"])
self.assertEqual("3.100", data["SearchInfo.tag"])
self.assertEqual(f"X.Y.exe -prearg {quote(script)} -postarg", data["stdout"].strip())
def test_py_shebang_invalid_bom(self):
with self.py_ini(TEST_PY_DEFAULTS):
content = "#! /usr/bin/python3 -prearg".encode("utf-8")
with self.script(b"\xEF\xAA\xBF" + content) as script:
data = self.run_py([script, "-postarg"])
self.assertIn("Invalid BOM", data["stderr"])
self.assertEqual("PythonTestSuite", data["SearchInfo.company"])
self.assertEqual("3.100", data["SearchInfo.tag"])
self.assertEqual(f"X.Y.exe {quote(script)} -postarg", data["stdout"].strip())
def test_py_handle_64_in_ini(self): def test_py_handle_64_in_ini(self):
with self.py_ini("\n".join(["[defaults]", "python=3.999-64"])): with self.py_ini("\n".join(["[defaults]", "python=3.999-64"])):
# Expect this to fail, but should get oldStyleTag flipped on # Expect this to fail, but should get oldStyleTag flipped on

View file

@ -0,0 +1,2 @@
:source:`pylauncher <PC/launcher2.c>` correctly detects a BOM when searching for the
shebang. Fix by Chris Eibl.

View file

@ -1062,7 +1062,7 @@ checkShebang(SearchInfo *search)
} }
DWORD bytesRead = 0; DWORD bytesRead = 0;
char buffer[4096]; unsigned char buffer[4096];
if (!ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL)) { if (!ReadFile(hFile, buffer, sizeof(buffer), &bytesRead, NULL)) {
debug(L"# Failed to read %s for shebang parsing (0x%08X)\n", debug(L"# Failed to read %s for shebang parsing (0x%08X)\n",
scriptFile, GetLastError()); scriptFile, GetLastError());
@ -1075,7 +1075,7 @@ checkShebang(SearchInfo *search)
free(scriptFile); free(scriptFile);
char *b = buffer; unsigned char *b = buffer;
bool onlyUtf8 = false; bool onlyUtf8 = false;
if (bytesRead > 3 && *b == 0xEF) { if (bytesRead > 3 && *b == 0xEF) {
if (*++b == 0xBB && *++b == 0xBF) { if (*++b == 0xBB && *++b == 0xBF) {
@ -1096,13 +1096,13 @@ checkShebang(SearchInfo *search)
++b; ++b;
--bytesRead; --bytesRead;
while (--bytesRead > 0 && isspace(*++b)) { } while (--bytesRead > 0 && isspace(*++b)) { }
char *start = b; const unsigned char *start = b;
while (--bytesRead > 0 && *++b != '\r' && *b != '\n') { } while (--bytesRead > 0 && *++b != '\r' && *b != '\n') { }
wchar_t *shebang; wchar_t *shebang;
int shebangLength; int shebangLength;
// We add 1 when bytesRead==0, as in that case we hit EOF and b points // We add 1 when bytesRead==0, as in that case we hit EOF and b points
// to the last character in the file, not the newline // to the last character in the file, not the newline
int exitCode = _decodeShebang(search, start, (int)(b - start + (bytesRead == 0)), onlyUtf8, &shebang, &shebangLength); int exitCode = _decodeShebang(search, (const char*)start, (int)(b - start + (bytesRead == 0)), onlyUtf8, &shebang, &shebangLength);
if (exitCode) { if (exitCode) {
return exitCode; return exitCode;
} }