diff --git a/Lib/stat.py b/Lib/stat.py index 1b4ed1ebc940ef..81f694329bf4ff 100644 --- a/Lib/stat.py +++ b/Lib/stat.py @@ -166,9 +166,14 @@ def filemode(mode): perm = [] for index, table in enumerate(_filemode_table): for bit, char in table: - if mode & bit == bit: - perm.append(char) - break + if index == 0: + if S_IFMT(mode) == bit: + perm.append(char) + break + else: + if mode & bit == bit: + perm.append(char) + break else: if index == 0: # Unknown filetype diff --git a/Lib/test/test_stat.py b/Lib/test/test_stat.py index 49013a4bcd8af6..bde22338113237 100644 --- a/Lib/test/test_stat.py +++ b/Lib/test/test_stat.py @@ -163,6 +163,11 @@ def test_mode(self): self.statmod.S_IFREG) self.assertEqual(self.statmod.S_IMODE(st_mode), 0o666) + def test_filemode_does_not_misclassify_random_bits(self): + # gh-144050 regression test + self.assertEqual(self.statmod.filemode(0o77777)[0], "?") + self.assertEqual(self.statmod.filemode(0o177777)[0], "?") + @os_helper.skip_unless_working_chmod def test_directory(self): os.mkdir(TESTFN) diff --git a/Misc/NEWS.d/next/Library/2026-01-20-16-35-55.gh-issue-144050.0kKFbF.rst b/Misc/NEWS.d/next/Library/2026-01-20-16-35-55.gh-issue-144050.0kKFbF.rst new file mode 100644 index 00000000000000..dfc062d023c8f1 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-01-20-16-35-55.gh-issue-144050.0kKFbF.rst @@ -0,0 +1,2 @@ +Fix :func:`stat.filemode` in the pure-Python implementation to avoid misclassifying +invalid mode values as block devices.