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
This commit is contained in:
Victor Stinner 2016-08-16 15:23:58 +02:00
parent c35a32fe85
commit 4bad3b622e
1 changed files with 76 additions and 57 deletions

View File

@ -39,10 +39,9 @@ win32_urandom_init(int raise)
return 0;
error:
if (raise)
if (raise) {
PyErr_SetFromWindowsErr(0);
else
Py_FatalError("Failed to initialize Windows random API (CryptoGen)");
}
return -1;
}
@ -55,8 +54,9 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
if (hCryptProv == 0)
{
if (win32_urandom_init(raise) == -1)
if (win32_urandom_init(raise) == -1) {
return -1;
}
}
while (size > 0)
@ -65,11 +65,9 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
if (!CryptGenRandom(hCryptProv, (DWORD)chunk, buffer))
{
/* CryptGenRandom() failed */
if (raise)
if (raise) {
PyErr_SetFromWindowsErr(0);
else
Py_FatalError("Failed to initialized the randomized hash "
"secret using CryptoGen)");
}
return -1;
}
buffer += chunk;
@ -86,29 +84,28 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
/* Fill buffer with size pseudo-random bytes generated by getentropy().
Return 0 on success, or raise an exception and return -1 on error.
If fatal is nonzero, call Py_FatalError() instead of raising an exception
on error. */
If raise is zero, don't raise an exception on error. */
static int
py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
py_getentropy(char *buffer, Py_ssize_t size, int raise)
{
while (size > 0) {
Py_ssize_t len = Py_MIN(size, 256);
int res;
if (!fatal) {
if (raise) {
Py_BEGIN_ALLOW_THREADS
res = getentropy(buffer, len);
Py_END_ALLOW_THREADS
if (res < 0) {
PyErr_SetFromErrno(PyExc_OSError);
return -1;
}
}
else {
res = getentropy(buffer, len);
if (res < 0)
Py_FatalError("getentropy() failed");
}
if (res < 0) {
if (raise) {
PyErr_SetFromErrno(PyExc_OSError);
}
return -1;
}
buffer += len;
@ -195,18 +192,15 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
if (errno == EINTR) {
if (PyErr_CheckSignals()) {
if (!raise)
Py_FatalError("getrandom() interrupted by a signal");
return -1;
}
/* retry getrandom() */
continue;
}
if (raise)
if (raise) {
PyErr_SetFromErrno(PyExc_OSError);
else
Py_FatalError("getrandom() failed");
}
return -1;
}
@ -225,9 +219,9 @@ static struct {
/* Read size bytes from /dev/urandom into buffer.
Call Py_FatalError() on error. */
static void
dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
Return 0 success, or return -1 on error. */
static int
dev_urandom_noraise(char *buffer, Py_ssize_t size)
{
int fd;
Py_ssize_t n;
@ -235,31 +229,35 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)
assert (0 < size);
#ifdef PY_GETRANDOM
if (py_getrandom(buffer, size, 0) == 1)
return;
if (py_getrandom(buffer, size, 0) == 1) {
return 0;
}
/* getrandom() is not supported by the running kernel, fall back
* on reading /dev/urandom */
#endif
fd = _Py_open_noraise("/dev/urandom", O_RDONLY);
if (fd < 0)
Py_FatalError("Failed to open /dev/urandom");
if (fd < 0) {
return -1;
}
while (0 < size)
{
do {
n = read(fd, buffer, (size_t)size);
} while (n < 0 && errno == EINTR);
if (n <= 0)
{
if (n <= 0) {
/* stop on error or if read(size) returned 0 */
Py_FatalError("Failed to read bytes from /dev/urandom");
break;
return -1;
}
buffer += n;
size -= n;
}
close(fd);
return 0;
}
/* Read size bytes from /dev/urandom into buffer.
@ -379,6 +377,40 @@ lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
}
}
/* If raise is zero:
* - Don't raise exceptions on error
* - Don't call PyErr_CheckSignals() on EINTR (retry directly the interrupted
* syscall)
* - Don't release the GIL to call syscalls. */
static int
pyurandom(void *buffer, Py_ssize_t size, int raise)
{
if (size < 0) {
if (raise) {
PyErr_Format(PyExc_ValueError,
"negative argument not allowed");
}
return -1;
}
if (size == 0) {
return 0;
}
#ifdef MS_WINDOWS
return win32_urandom((unsigned char *)buffer, size, raise);
#elif defined(PY_GETENTROPY)
return py_getentropy(buffer, size, raise);
#else
if (raise) {
return dev_urandom_python(buffer, size);
}
else {
return dev_urandom_noraise(buffer, size);
}
#endif
}
/* Fill buffer with size pseudo-random bytes from the operating system random
number generator (RNG). It is suitable for most cryptographic purposes
except long living private keys for asymmetric encryption.
@ -387,21 +419,7 @@ lcg_urandom(unsigned int x0, unsigned char *buffer, size_t size)
int
_PyOS_URandom(void *buffer, Py_ssize_t size)
{
if (size < 0) {
PyErr_Format(PyExc_ValueError,
"negative argument not allowed");
return -1;
}
if (size == 0)
return 0;
#ifdef MS_WINDOWS
return win32_urandom((unsigned char *)buffer, size, 1);
#elif defined(PY_GETENTROPY)
return py_getentropy(buffer, size, 0);
#else
return dev_urandom_python((char*)buffer, size);
#endif
return pyurandom(buffer, size, 1);
}
void
@ -442,13 +460,14 @@ _PyRandom_Init(void)
}
}
else {
#ifdef MS_WINDOWS
(void)win32_urandom(secret, secret_size, 0);
#elif defined(PY_GETENTROPY)
(void)py_getentropy(secret, secret_size, 1);
#else
dev_urandom_noraise(secret, secret_size);
#endif
int res;
/* _PyRandom_Init() is called very early in the Python initialization
* and so exceptions cannot be used. */
res = pyurandom(secret, secret_size, 0);
if (res < 0) {
Py_FatalError("failed to get random numbers to initialize Python");
}
}
}