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

Skip to content

Commit 4bad3b6

Browse files
committed
Issue #27776: Cleanup random.c
* Add pyurandom() helper function to factorize the code * don't call Py_FatalError() in helper functions, but only in _PyRandom_Init() if pyurandom() failed, to uniformize the code
1 parent c35a32f commit 4bad3b6

1 file changed

Lines changed: 74 additions & 55 deletions

File tree

Python/random.c

Lines changed: 74 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,9 @@ win32_urandom_init(int raise)
3939
return 0;
4040

4141
error:
42-
if (raise)
42+
if (raise) {
4343
PyErr_SetFromWindowsErr(0);
44-
else
45-
Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
44+
}
4645
return -1;
4746
}
4847

@@ -55,8 +54,9 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
5554

5655
if (hCryptProv == 0)
5756
{
58-
if (win32_urandom_init(raise) == -1)
57+
if (win32_urandom_init(raise) == -1) {
5958
return -1;
59+
}
6060
}
6161

6262
while (size > 0)
@@ -65,11 +65,9 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
6565
if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer))
6666
{
6767
/* CryptGenRandom() failed */
68-
if (raise)
68+
if (raise) {
6969
PyErr_SetFromWindowsErr(0);
70-
else
71-
Py_FatalError("Failed to initialized the randomized hash "
72-
"secret using CryptoGen)");
70+
}
7371
return -1;
7472
}
7573
buffer += chunk;
@@ -86,29 +84,28 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
8684
/* Fill buffer with size pseudo-random bytes generated by getentropy().
8785
Return 0 on success, or raise an exception and return -1 on error.
8886
89-
If fatal is nonzero, call Py_FatalError() instead of raising an exception
90-
on error. */
87+
If raise is zero, don't raise an exception on error. */
9188
static int
92-
py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
89+
py_getentropy(char *buffer, Py_ssize_t size, int raise)
9390
{
9491
while (size > 0) {
9592
Py_ssize_t len = Py_MIN(size, 256);
9693
int res;
9794

98-
if (!fatal) {
95+
if (raise) {
9996
Py_BEGIN_ALLOW_THREADS
10097
res = getentropy(buffer, len);
10198
Py_END_ALLOW_THREADS
102-
103-
if (res < 0) {
104-
PyErr_SetFromErrno(PyExc_OSError);
105-
return -1;
106-
}
10799
}
108100
else {
109101
res = getentropy(buffer, len);
110-
if (res < 0)
111-
Py_FatalError("getentropy() failed");
102+
}
103+
104+
if (res < 0) {
105+
if (raise) {
106+
PyErr_SetFromErrno(PyExc_OSError);
107+
}
108+
return -1;
112109
}
113110

114111
buffer += len;
@@ -195,18 +192,15 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
195192

196193
if (errno == EINTR) {
197194
if (PyErr_CheckSignals()) {
198-
if (!raise)
199-
Py_FatalError("getrandom() interrupted by a signal");
200195
return -1;
201196
}
202197
/* retry getrandom() */
203198
continue;
204199
}
205200

206-
if (raise)
201+
if (raise) {
207202
PyErr_SetFromErrno(PyExc_OSError);
208-
else
209-
Py_FatalError("getrandom() failed");
203+
}
210204
return -1;
211205
}
212206

@@ -225,41 +219,45 @@ static struct {
225219

226220

227221
/* Read size bytes from /dev/urandom into buffer.
228-
Call Py_FatalError() on error. */
229-
static void
230-
dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
222+
Return 0 success, or return -1 on error. */
223+
static int
224+
dev_urandom_noraise(char *buffer, Py_ssize_t size)
231225
{
232226
int fd;
233227
Py_ssize_t n;
234228

235229
assert (0 < size);
236230

237231
#ifdef PY_GETRANDOM
238-
if (py_getrandom(buffer, size, 0) == 1)
239-
return;
232+
if (py_getrandom(buffer, size, 0) == 1) {
233+
return 0;
234+
}
240235
/* getrandom() is not supported by the running kernel, fall back
241236
* on reading /dev/urandom */
242237
#endif
243238

244239
fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
245-
if (fd < 0)
246-
Py_FatalError("Failed to open /dev/urandom");
240+
if (fd < 0) {
241+
return -1;
242+
}
247243

248244
while (0 < size)
249245
{
250246
do {
251247
n = read(fd, buffer, (size_t)size);
252248
} while (n < 0 && errno == EINTR);
253-
if (n <= 0)
254-
{
249+
250+
if (n <= 0) {
255251
/* stop on error or if read(size) returned 0 */
256-
Py_FatalError("Failed to read bytes from /dev/urandom");
257-
break;
252+
return -1;
258253
}
254+
259255
buffer += n;
260256
size -= n;
261257
}
262258
close(fd);
259+
260+
return 0;
263261
}
264262

265263
/* Read size bytes from /dev/urandom into buffer.
@@ -379,31 +377,51 @@ lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
379377
}
380378
}
381379

382-
/* Fill buffer with size pseudo-random bytes from the operating system random
383-
number generator (RNG). It is suitable for most cryptographic purposes
384-
except long living private keys for asymmetric encryption.
385-
386-
Return 0 on success, raise an exception and return -1 on error. */
387-
int
388-
_PyOS_URandom(void *buffer, Py_ssize_t size)
380+
/* If raise is zero:
381+
* - Don't raise exceptions on error
382+
* - Don't call PyErr_CheckSignals() on EINTR (retry directly the interrupted
383+
* syscall)
384+
* - Don't release the GIL to call syscalls. */
385+
static int
386+
pyurandom(void *buffer, Py_ssize_t size, int raise)
389387
{
390388
if (size < 0) {
391-
PyErr_Format(PyExc_ValueError,
392-
"negative argument not allowed");
389+
if (raise) {
390+
PyErr_Format(PyExc_ValueError,
391+
"negative argument not allowed");
392+
}
393393
return -1;
394394
}
395-
if (size == 0)
395+
396+
if (size == 0) {
396397
return 0;
398+
}
397399

398400
#ifdef MS_WINDOWS
399-
return win32_urandom((unsigned char *)buffer, size, 1);
401+
return win32_urandom((unsigned char *)buffer, size, raise);
400402
#elif defined(PY_GETENTROPY)
401-
return py_getentropy(buffer, size, 0);
403+
return py_getentropy(buffer, size, raise);
402404
#else
403-
return dev_urandom_python((char*)buffer, size);
405+
if (raise) {
406+
return dev_urandom_python(buffer, size);
407+
}
408+
else {
409+
return dev_urandom_noraise(buffer, size);
410+
}
404411
#endif
405412
}
406413

414+
/* Fill buffer with size pseudo-random bytes from the operating system random
415+
number generator (RNG). It is suitable for most cryptographic purposes
416+
except long living private keys for asymmetric encryption.
417+
418+
Return 0 on success, raise an exception and return -1 on error. */
419+
int
420+
_PyOS_URandom(void *buffer, Py_ssize_t size)
421+
{
422+
return pyurandom(buffer, size, 1);
423+
}
424+
407425
void
408426
_PyRandom_Init(void)
409427
{
@@ -442,13 +460,14 @@ _PyRandom_Init(void)
442460
}
443461
}
444462
else {
445-
#ifdef MS_WINDOWS
446-
(void)win32_urandom(secret, secret_size, 0);
447-
#elif defined(PY_GETENTROPY)
448-
(void)py_getentropy(secret, secret_size, 1);
449-
#else
450-
dev_urandom_noraise(secret, secret_size);
451-
#endif
463+
int res;
464+
465+
/* _PyRandom_Init() is called very early in the Python initialization
466+
* and so exceptions cannot be used. */
467+
res = pyurandom(secret, secret_size, 0);
468+
if (res < 0) {
469+
Py_FatalError("failed to get random numbers to initialize Python");
470+
}
452471
}
453472
}
454473

0 commit comments

Comments
 (0)