Skip to content

Commit e0a0bd6

Browse files
committed
Issue #23458: On POSIX, the file descriptor kept open by os.urandom() is now
set to non inheritable
1 parent 0e4da40 commit e0a0bd6

4 files changed

Lines changed: 72 additions & 0 deletions

File tree

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
"""When called as a script, print a comma-separated list of the open
2+
file descriptors on stdout.
3+
4+
Usage:
5+
fd_stats.py: check all file descriptors
6+
fd_status.py fd1 fd2 ...: check only specified file descriptors
7+
"""
8+
9+
import errno
10+
import os
11+
import stat
12+
import sys
13+
14+
if __name__ == "__main__":
15+
fds = []
16+
if len(sys.argv) == 1:
17+
try:
18+
_MAXFD = os.sysconf("SC_OPEN_MAX")
19+
except:
20+
_MAXFD = 256
21+
test_fds = range(0, _MAXFD)
22+
else:
23+
test_fds = map(int, sys.argv[1:])
24+
for fd in test_fds:
25+
try:
26+
st = os.fstat(fd)
27+
except OSError as e:
28+
if e.errno == errno.EBADF:
29+
continue
30+
raise
31+
fds.append(fd)
32+
print(','.join(map(str, fds)))

Lib/test/test_os.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import signal
1111
import subprocess
1212
import sysconfig
13+
import textwrap
1314
import time
1415
try:
1516
import resource
@@ -568,6 +569,33 @@ def test_urandom_subprocess(self):
568569
data2 = self.get_urandom_subprocess(16)
569570
self.assertNotEqual(data1, data2)
570571

572+
def test_urandom_fd_non_inheritable(self):
573+
# Issue #23458: os.urandom() keeps a file descriptor open, but it
574+
# must be non inheritable
575+
fd_status = test_support.findfile("fd_status.py", subdir="subprocessdata")
576+
577+
# Need a two subprocesses because the Python test suite opens other
578+
# inheritable file descriptors, whereas the test is specific to
579+
# os.urandom() file descriptor
580+
code = textwrap.dedent("""
581+
import os
582+
import subprocess
583+
import sys
584+
585+
# Ensure that the /dev/urandom file descriptor is open
586+
os.urandom(1)
587+
588+
exitcode = subprocess.call([sys.executable, %r],
589+
close_fds=False)
590+
sys.exit(exitcode)
591+
""" % fd_status)
592+
593+
proc = subprocess.Popen([sys.executable, "-c", code],
594+
stdout=subprocess.PIPE, close_fds=True)
595+
output, error = proc.communicate()
596+
open_fds = set(map(int, output.rstrip().split(',')))
597+
self.assertEqual(open_fds - set(range(3)), set())
598+
571599

572600
HAVE_GETENTROPY = (sysconfig.get_config_var('HAVE_GETENTROPY') == 1)
573601

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ Core and Builtins
1818
Library
1919
-------
2020

21+
- Issue #23458: On POSIX, the file descriptor kept open by os.urandom() is now
22+
set to non inheritable
23+
2124
- Issue #22113: struct.pack_into() now supports new buffer protocol (in
2225
particular accepts writable memoryview).
2326

Python/random.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
188188
int fd;
189189
Py_ssize_t n;
190190
struct stat st;
191+
int attr;
191192

192193
if (size <= 0)
193194
return 0;
@@ -219,6 +220,14 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
219220
PyErr_SetFromErrno(PyExc_OSError);
220221
return -1;
221222
}
223+
224+
/* try to make the file descriptor non-inheritable, ignore errors */
225+
attr = fcntl(fd, F_GETFD);
226+
if (attr >= 0) {
227+
attr |= FD_CLOEXEC;
228+
(void)fcntl(fd, F_SETFD, attr);
229+
}
230+
222231
if (urandom_cache.fd >= 0) {
223232
/* urandom_fd was initialized by another thread while we were
224233
not holding the GIL, keep it. */

0 commit comments

Comments
 (0)