An interpreted, interactive, object-oriented programming language
CentOS Sources
2017-08-01 71084d584ff953f5463757ec6536406320560b4d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
 
# HG changeset patch
# User Benjamin Peterson <benjamin@python.org>
# Date 1409243400 14400
# Node ID 3e7f8855078855a9409bc2c1372de89cb021d6c8
# Parent  3f73c44b1fd1d442d6841493328e9756fb5e7ef5
PEP 466: backport persistent urandom fd (closes #21305)
 
Patch from Alex Gaynor.
 
diff --git a/Python/pythonrun.c b/Python/pythonrun.c
--- a/Python/pythonrun.c
+++ b/Python/pythonrun.c
@@ -536,6 +536,7 @@ Py_Finalize(void)
     PyInt_Fini();
     PyFloat_Fini();
     PyDict_Fini();
+    _PyRandom_Fini();
 
 #ifdef Py_USING_UNICODE
     /* Cleanup Unicode implementation */
diff -up Python-2.7.5/Include/pythonrun.h.urandom Python-2.7.5/Include/pythonrun.h
--- Python-2.7.5/Include/pythonrun.h.urandom    2015-03-06 08:16:47.638584015 +0100
+++ Python-2.7.5/Include/pythonrun.h    2015-03-06 08:21:48.009485462 +0100
@@ -145,6 +145,7 @@ PyAPI_FUNC(void) PyInt_Fini(void);
 PyAPI_FUNC(void) PyFloat_Fini(void);
 PyAPI_FUNC(void) PyOS_FiniInterrupts(void);
 PyAPI_FUNC(void) PyByteArray_Fini(void);
+PyAPI_FUNC(void) _PyRandom_Fini(void);
 
 /* Stuff with no proper home (yet) */
 PyAPI_FUNC(char *) PyOS_Readline(FILE *, FILE *, char *);
diff -up Python-2.7.5/Python/random.c.urandom Python-2.7.5/Python/random.c
--- Python-2.7.5/Python/random.c.urandom    2015-03-06 08:22:10.244699950 +0100
+++ Python-2.7.5/Python/random.c    2015-03-06 08:24:57.907317272 +0100
@@ -118,10 +118,16 @@ vms_urandom(unsigned char *buffer, Py_ss
 
 #if !defined(MS_WINDOWS) && !defined(__VMS)
 
+static struct {
+    int fd;
+    dev_t st_dev;
+    ino_t st_ino;
+} urandom_cache = { -1 };
+
 /* Read size bytes from /dev/urandom into buffer.
    Call Py_FatalError() on error. */
 static void
-dev_urandom_noraise(char *buffer, Py_ssize_t size)
+dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
 {
     int fd;
     Py_ssize_t n;
@@ -156,18 +162,56 @@ dev_urandom_python(char *buffer, Py_ssiz
 {
     int fd;
     Py_ssize_t n;
+    struct stat st;
 
     if (size <= 0)
         return 0;
 
-    Py_BEGIN_ALLOW_THREADS
-    fd = open("/dev/urandom", O_RDONLY);
-    Py_END_ALLOW_THREADS
-    if (fd < 0)
-    {
-        PyErr_SetString(PyExc_NotImplementedError,
-                        "/dev/urandom (or equivalent) not found");
-        return -1;
+    if (urandom_cache.fd >= 0) {
+        /* Does the fd point to the same thing as before? (issue #21207) */
+        if (fstat(urandom_cache.fd, &st)
+            || st.st_dev != urandom_cache.st_dev
+            || st.st_ino != urandom_cache.st_ino) {
+            /* Something changed: forget the cached fd (but don't close it,
+               since it probably points to something important for some
+               third-party code). */
+            urandom_cache.fd = -1;
+        }
+    }
+    if (urandom_cache.fd >= 0)
+        fd = urandom_cache.fd;
+    else {
+        Py_BEGIN_ALLOW_THREADS
+        fd = open("/dev/urandom", O_RDONLY);
+        Py_END_ALLOW_THREADS
+        if (fd < 0)
+        {
+            if (errno == ENOENT || errno == ENXIO ||
+                errno == ENODEV || errno == EACCES)
+                PyErr_SetString(PyExc_NotImplementedError,
+                                "/dev/urandom (or equivalent) not found");
+            else
+                PyErr_SetFromErrno(PyExc_OSError);
+            return -1;
+        }
+        if (urandom_cache.fd >= 0) {
+            /* urandom_fd was initialized by another thread while we were
+               not holding the GIL, keep it. */
+            close(fd);
+            fd = urandom_cache.fd;
+        }
+        else {
+            if (fstat(fd, &st)) {
+                PyErr_SetFromErrno(PyExc_OSError);
+                close(fd);
+                return -1;
+            }
+            else {
+                urandom_cache.fd = fd;
+                urandom_cache.st_dev = st.st_dev;
+                urandom_cache.st_ino = st.st_ino;
+            }
+        }
     }
 
     Py_BEGIN_ALLOW_THREADS
@@ -191,12 +235,21 @@ dev_urandom_python(char *buffer, Py_ssiz
             PyErr_Format(PyExc_RuntimeError,
                          "Failed to read %zi bytes from /dev/urandom",
                          size);
-        close(fd);
         return -1;
     }
-    close(fd);
     return 0;
 }
+
+static void
+dev_urandom_close(void)
+{
+    if (urandom_cache.fd >= 0) {
+        close(urandom_cache.fd);
+        urandom_cache.fd = -1;
+    }
+}
+
+
 #endif /* !defined(MS_WINDOWS) && !defined(__VMS) */
 
 /* Fill buffer with pseudo-random bytes generated by a linear congruent
@@ -300,8 +353,21 @@ _PyRandom_Init(void)
 # ifdef __VMS
         vms_urandom((unsigned char *)secret, secret_size, 0);
 # else
-        dev_urandom_noraise((char*)secret, secret_size);
+        dev_urandom_noraise((unsigned char*)secret, secret_size);
 # endif
 #endif
     }
 }
+
+void
+_PyRandom_Fini(void)
+{
+#ifdef MS_WINDOWS
+    if (hCryptProv) {
+        CryptReleaseContext(hCryptProv, 0);
+        hCryptProv = 0;
+    }
+#else
+    dev_urandom_close();
+#endif
+}