summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Python/random.c42
1 files changed, 39 insertions, 3 deletions
diff --git a/Python/random.c b/Python/random.c
index ad2c389..bc96226 100644
--- a/Python/random.c
+++ b/Python/random.c
@@ -183,14 +183,31 @@ py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
#elif defined(HAVE_GETENTROPY)
#define PY_GETENTROPY 1
-/* Fill buffer with size pseudo-random bytes generated by getentropy().
- Return 1 on success, or raise an exception and return -1 on error.
+/* Fill buffer with size pseudo-random bytes generated by getentropy():
- If raise is zero, don't raise an exception on error. */
+ - Return 1 on success
+ - Return 0 if getentropy() syscall is not available (failed with ENOSYS or
+ EPERM).
+ - Raise an exception (if raise is non-zero) and return -1 on error:
+ if getentropy() failed with EINTR, raise is non-zero and the Python signal
+ handler raised an exception, or if getentropy() failed with a different
+ error.
+
+ getentropy() is retried if it failed with EINTR: interrupted by a signal. */
static int
py_getentropy(char *buffer, Py_ssize_t size, int raise)
{
+ /* Is getentropy() supported by the running kernel? Set to 0 if
+ getentropy() failed with ENOSYS or EPERM. */
+ static int getentropy_works = 1;
+
+ if (!getentropy_works) {
+ return 0;
+ }
+
while (size > 0) {
+ /* getentropy() is limited to returning up to 256 bytes. Call it
+ multiple times if more bytes are requested. */
Py_ssize_t len = Py_MIN(size, 256);
int res;
@@ -204,6 +221,25 @@ py_getentropy(char *buffer, Py_ssize_t size, int raise)
}
if (res < 0) {
+ /* ENOSYS: the syscall is not supported by the running kernel.
+ EPERM: the syscall is blocked by a security policy (ex: SECCOMP)
+ or something else. */
+ if (errno == ENOSYS || errno == EPERM) {
+ getentropy_works = 0;
+ return 0;
+ }
+
+ if (errno == EINTR) {
+ if (raise) {
+ if (PyErr_CheckSignals()) {
+ return -1;
+ }
+ }
+
+ /* retry getentropy() if it was interrupted by a signal */
+ continue;
+ }
+
if (raise) {
PyErr_SetFromErrno(PyExc_OSError);
}