Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e472aea

Browse files
committed
Issue #21207: Detect when the os.urandom cached fd has been closed or replaced, and open it anew.
1 parent 86fe53e commit e472aea

3 files changed

Lines changed: 85 additions & 10 deletions

File tree

Lib/test/test_os.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1070,6 +1070,49 @@ def test_urandom_failure(self):
10701070
"""
10711071
assert_python_ok('-c', code)
10721072

1073+
def test_urandom_fd_closed(self):
1074+
# Issue #21207: urandom() should reopen its fd to /dev/urandom if
1075+
# closed.
1076+
code = """if 1:
1077+
import os
1078+
import sys
1079+
os.urandom(4)
1080+
os.closerange(3, 256)
1081+
sys.stdout.buffer.write(os.urandom(4))
1082+
"""
1083+
rc, out, err = assert_python_ok('-Sc', code)
1084+
1085+
def test_urandom_fd_reopened(self):
1086+
# Issue #21207: urandom() should detect its fd to /dev/urandom
1087+
# changed to something else, and reopen it.
1088+
with open(support.TESTFN, 'wb') as f:
1089+
f.write(b"x" * 256)
1090+
self.addCleanup(os.unlink, support.TESTFN)
1091+
code = """if 1:
1092+
import os
1093+
import sys
1094+
os.urandom(4)
1095+
for fd in range(3, 256):
1096+
try:
1097+
os.close(fd)
1098+
except OSError:
1099+
pass
1100+
else:
1101+
# Found the urandom fd (XXX hopefully)
1102+
break
1103+
os.closerange(3, 256)
1104+
with open({TESTFN!r}, 'rb') as f:
1105+
os.dup2(f.fileno(), fd)
1106+
sys.stdout.buffer.write(os.urandom(4))
1107+
sys.stdout.buffer.write(os.urandom(4))
1108+
""".format(TESTFN=support.TESTFN)
1109+
rc, out, err = assert_python_ok('-Sc', code)
1110+
self.assertEqual(len(out), 8)
1111+
self.assertNotEqual(out[0:4], out[4:8])
1112+
rc, out2, err2 = assert_python_ok('-Sc', code)
1113+
self.assertEqual(len(out2), 8)
1114+
self.assertNotEqual(out2, out)
1115+
10731116

10741117
@contextlib.contextmanager
10751118
def _execvpe_mockup(defpath=None):

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ Core and Builtins
3939
Library
4040
-------
4141

42+
- Issue #21207: Detect when the os.urandom cached fd has been closed or
43+
replaced, and open it anew.
44+
4245
- Issue #21291: subprocess's Popen.wait() is now thread safe so that
4346
multiple threads may be calling wait() or poll() on a Popen instance
4447
at the same time without losing the Popen.returncode value.

Python/random.c

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
#include <windows.h>
44
#else
55
#include <fcntl.h>
6+
#ifdef HAVE_SYS_STAT_H
7+
#include <sys/stat.h>
8+
#endif
69
#endif
710

811
#ifdef Py_DEBUG
@@ -69,7 +72,11 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
6972

7073

7174
#ifndef MS_WINDOWS
72-
static int urandom_fd = -1;
75+
static struct {
76+
int fd;
77+
dev_t st_dev;
78+
ino_t st_ino;
79+
} urandom_cache = { -1 };
7380

7481
/* Read size bytes from /dev/urandom into buffer.
7582
Call Py_FatalError() on error. */
@@ -109,12 +116,24 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
109116
{
110117
int fd;
111118
Py_ssize_t n;
119+
struct stat st;
112120

113121
if (size <= 0)
114122
return 0;
115123

116-
if (urandom_fd >= 0)
117-
fd = urandom_fd;
124+
if (urandom_cache.fd >= 0) {
125+
/* Does the fd point to the same thing as before? (issue #21207) */
126+
if (fstat(urandom_cache.fd, &st)
127+
|| st.st_dev != urandom_cache.st_dev
128+
|| st.st_ino != urandom_cache.st_ino) {
129+
/* Something changed: forget the cached fd (but don't close it,
130+
since it probably points to something important for some
131+
third-party code). */
132+
urandom_cache.fd = -1;
133+
}
134+
}
135+
if (urandom_cache.fd >= 0)
136+
fd = urandom_cache.fd;
118137
else {
119138
Py_BEGIN_ALLOW_THREADS
120139
fd = _Py_open("/dev/urandom", O_RDONLY);
@@ -129,14 +148,24 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
129148
PyErr_SetFromErrno(PyExc_OSError);
130149
return -1;
131150
}
132-
if (urandom_fd >= 0) {
151+
if (urandom_cache.fd >= 0) {
133152
/* urandom_fd was initialized by another thread while we were
134153
not holding the GIL, keep it. */
135154
close(fd);
136-
fd = urandom_fd;
155+
fd = urandom_cache.fd;
156+
}
157+
else {
158+
if (fstat(fd, &st)) {
159+
PyErr_SetFromErrno(PyExc_OSError);
160+
close(fd);
161+
return -1;
162+
}
163+
else {
164+
urandom_cache.fd = fd;
165+
urandom_cache.st_dev = st.st_dev;
166+
urandom_cache.st_ino = st.st_ino;
167+
}
137168
}
138-
else
139-
urandom_fd = fd;
140169
}
141170

142171
Py_BEGIN_ALLOW_THREADS
@@ -168,9 +197,9 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
168197
static void
169198
dev_urandom_close(void)
170199
{
171-
if (urandom_fd >= 0) {
172-
close(urandom_fd);
173-
urandom_fd = -1;
200+
if (urandom_cache.fd >= 0) {
201+
close(urandom_cache.fd);
202+
urandom_cache.fd = -1;
174203
}
175204
}
176205

0 commit comments

Comments
 (0)