From 2b9c948db41c4600725051fab211b88e844c507c Mon Sep 17 00:00:00 2001 From: Jeong YunWon Date: Mon, 12 Oct 2026 02:18:17 +0900 Subject: [PATCH] gh-159182: Preserve empty ranges in os.closerange --- Lib/test/test_os/test_os.py | 28 +++++++++++++++++++ ...-10-11-17-15-00.gh-issue-159182.aQ3tX2.rst | 2 ++ Modules/posixmodule.c | 5 +++- 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 Misc/NEWS.d/next/Library/2026-10-11-17-15-00.gh-issue-159182.aQ3tX2.rst diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index 81b3043eb7e75bc..ee901c97522117d 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -225,6 +225,34 @@ def test_closerange(self): os.closerange(first, first + 2) self.assertRaises(OSError, os.write, first, b"a") + @support.requires_subprocess() + def test_closerange_empty(self): + # Run in a subprocess: a regression could close the standard streams + # and the test runner's own file descriptors. + code = textwrap.dedent("""\ + import os + import sys + + fd = os.open(os.devnull, os.O_RDONLY) + try: + result = os.closerange(int(sys.argv[1]), int(sys.argv[2])) + os.fstat(fd) + except BaseException: + os._exit(1) + os._exit(0 if result is None else 2) + """) + int_min = -(1 << (struct.calcsize('i') * 8 - 1)) + int_max = -int_min - 1 + for low, high in ((0, 0), (1, 0), (10, 5), (-1, 0), (-2, -1), + (-1, -1), (int_min, int_min), (0, int_min), + (int_max, int_max)): + with self.subTest(low=low, high=high): + proc = subprocess.run( + [sys.executable, '-I', '-c', code, str(low), str(high)], + stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, timeout=support.SHORT_TIMEOUT) + self.assertEqual(proc.returncode, 0) + @support.cpython_only def test_rename(self): path = os_helper.TESTFN diff --git a/Misc/NEWS.d/next/Library/2026-10-11-17-15-00.gh-issue-159182.aQ3tX2.rst b/Misc/NEWS.d/next/Library/2026-10-11-17-15-00.gh-issue-159182.aQ3tX2.rst new file mode 100644 index 000000000000000..45d3a09fc1ccd60 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2026-10-11-17-15-00.gh-issue-159182.aQ3tX2.rst @@ -0,0 +1,2 @@ +Fix :func:`os.closerange` closing unrelated file descriptors for empty or +negative-only ranges on platforms using ``close_range()``. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index 028270e7b841fda..e86def1ba7159cd 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -11895,7 +11895,10 @@ os_closerange_impl(PyObject *module, int fd_low, int fd_high) /*[clinic end generated code: output=0ce5c20fcda681c2 input=5855a3d053ebd4ec]*/ { Py_BEGIN_ALLOW_THREADS - _Py_closerange(fd_low, fd_high - 1); + /* Check the exclusive upper bound before converting it to inclusive. */ + if (fd_high > 0 && fd_low < fd_high) { + _Py_closerange(fd_low, fd_high - 1); + } Py_END_ALLOW_THREADS Py_RETURN_NONE; }