Skip to content

Commit 006857a

Browse files
committed
py_getentropy() now supports ENOSYS, EPERM & EINTR
Issue #29157.
1 parent a7057d0 commit 006857a

1 file changed

Lines changed: 39 additions & 3 deletions

File tree

Python/random.c

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,14 +183,31 @@ py_getrandom(void *buffer, Py_ssize_t size, int blocking, int raise)
183183
#elif defined(HAVE_GETENTROPY)
184184
#define PY_GETENTROPY 1
185185

186-
/* Fill buffer with size pseudo-random bytes generated by getentropy().
187-
Return 1 on success, or raise an exception and return -1 on error.
186+
/* Fill buffer with size pseudo-random bytes generated by getentropy():
188187
189-
If raise is zero, don't raise an exception on error. */
188+
- Return 1 on success
189+
- Return 0 if getentropy() syscall is not available (failed with ENOSYS or
190+
EPERM).
191+
- Raise an exception (if raise is non-zero) and return -1 on error:
192+
if getentropy() failed with EINTR, raise is non-zero and the Python signal
193+
handler raised an exception, or if getentropy() failed with a different
194+
error.
195+
196+
getentropy() is retried if it failed with EINTR: interrupted by a signal. */
190197
static int
191198
py_getentropy(char *buffer, Py_ssize_t size, int raise)
192199
{
200+
/* Is getentropy() supported by the running kernel? Set to 0 if
201+
getentropy() failed with ENOSYS or EPERM. */
202+
static int getentropy_works = 1;
203+
204+
if (!getentropy_works) {
205+
return 0;
206+
}
207+
193208
while (size > 0) {
209+
/* getentropy() is limited to returning up to 256 bytes. Call it
210+
multiple times if more bytes are requested. */
194211
Py_ssize_t len = Py_MIN(size, 256);
195212
int res;
196213

@@ -204,6 +221,25 @@ py_getentropy(char *buffer, Py_ssize_t size, int raise)
204221
}
205222

206223
if (res < 0) {
224+
/* ENOSYS: the syscall is not supported by the running kernel.
225+
EPERM: the syscall is blocked by a security policy (ex: SECCOMP)
226+
or something else. */
227+
if (errno == ENOSYS || errno == EPERM) {
228+
getentropy_works = 0;
229+
return 0;
230+
}
231+
232+
if (errno == EINTR) {
233+
if (raise) {
234+
if (PyErr_CheckSignals()) {
235+
return -1;
236+
}
237+
}
238+
239+
/* retry getentropy() if it was interrupted by a signal */
240+
continue;
241+
}
242+
207243
if (raise) {
208244
PyErr_SetFromErrno(PyExc_OSError);
209245
}

0 commit comments

Comments
 (0)