Skip to content

Commit

Permalink
Issue python#25003: On Solaris 11.3 or newer, os.urandom() now uses t…
Browse files Browse the repository at this point in the history
…he getrandom()

function instead of the getentropy() function. The getentropy() function is
blocking to generate very good quality entropy, os.urandom() doesn't need such
high-quality entropy.
  • Loading branch information
vstinner committed Oct 1, 2015
1 parent 87bddba commit bae2d62
Show file tree
Hide file tree
Showing 6 changed files with 119 additions and 28 deletions.
16 changes: 9 additions & 7 deletions Lib/test/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -1226,13 +1226,15 @@ def test_urandom_subprocess(self):
self.assertNotEqual(data1, data2)


HAVE_GETENTROPY = (sysconfig.get_config_var('HAVE_GETENTROPY') == 1)
HAVE_GETRANDOM = (sysconfig.get_config_var('HAVE_GETRANDOM_SYSCALL') == 1)

@unittest.skipIf(HAVE_GETENTROPY,
"getentropy() does not use a file descriptor")
@unittest.skipIf(HAVE_GETRANDOM,
"getrandom() does not use a file descriptor")
# os.urandom() doesn't use a file descriptor when it is implemented with the
# getentropy() function, the getrandom() function or the getrandom() syscall
OS_URANDOM_DONT_USE_FD = (
sysconfig.get_config_var('HAVE_GETENTROPY') == 1
or sysconfig.get_config_var('HAVE_GETRANDOM') == 1
or sysconfig.get_config_var('HAVE_GETRANDOM_SYSCALL') == 1)

@unittest.skipIf(OS_URANDOM_DONT_USE_FD ,
"os.random() does not use a file descriptor")
class URandomFDTests(unittest.TestCase):
@unittest.skipUnless(resource, "test requires the resource module")
def test_urandom_failure(self):
Expand Down
5 changes: 5 additions & 0 deletions Misc/NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ Release date: TBA
Core and Builtins
-----------------

- Issue #25003: On Solaris 11.3 or newer, os.urandom() now uses the
getrandom() function instead of the getentropy() function. The getentropy()
function is blocking to generate very good quality entropy, os.urandom()
doesn't need such high-quality entropy.

- Issue #25182: The stdprinter (used as sys.stderr before the io module is
imported at startup) now uses the backslashreplace error handler.

Expand Down
49 changes: 34 additions & 15 deletions Python/random.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
# ifdef HAVE_SYS_STAT_H
# include <sys/stat.h>
# endif
# ifdef HAVE_GETRANDOM_SYSCALL
# ifdef HAVE_GETRANDOM
# include <sys/random.h>
# elif defined(HAVE_GETRANDOM_SYSCALL)
# include <sys/syscall.h>
# endif
#endif
Expand Down Expand Up @@ -70,7 +72,9 @@ win32_urandom(unsigned char *buffer, Py_ssize_t size, int raise)
return 0;
}

#elif HAVE_GETENTROPY
#elif defined(HAVE_GETENTROPY) && !defined(sun)
#define PY_GETENTROPY 1

/* Fill buffer with size pseudo-random bytes generated by getentropy().
Return 0 on success, or raise an exception and return -1 on error.
Expand Down Expand Up @@ -105,16 +109,19 @@ py_getentropy(unsigned char *buffer, Py_ssize_t size, int fatal)
return 0;
}

#else /* !HAVE_GETENTROPY */
#else

#if defined(HAVE_GETRANDOM) || defined(HAVE_GETRANDOM_SYSCALL)
#define PY_GETRANDOM 1

#ifdef HAVE_GETRANDOM_SYSCALL
static int
py_getrandom(void *buffer, Py_ssize_t size, int raise)
{
/* is getrandom() supported by the running kernel?
* need Linux kernel 3.17 or later */
/* Is getrandom() supported by the running kernel?
* Need Linux kernel 3.17 or newer, or Solaris 11.3 or newer */
static int getrandom_works = 1;
/* Use /dev/urandom, block if the kernel has no entropy */
/* Use non-blocking /dev/urandom device. On Linux at boot, the getrandom()
* syscall blocks until /dev/urandom is initialized with enough entropy. */
const int flags = 0;
int n;

Expand All @@ -124,7 +131,18 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
while (0 < size) {
errno = 0;

/* Use syscall() because the libc doesn't expose getrandom() yet, see:
#ifdef HAVE_GETRANDOM
if (raise) {
Py_BEGIN_ALLOW_THREADS
n = getrandom(buffer, size, flags);
Py_END_ALLOW_THREADS
}
else {
n = getrandom(buffer, size, flags);
}
#else
/* On Linux, use the syscall() function because the GNU libc doesn't
* expose the Linux getrandom() syscall yet. See:
* https://sourceware.org/bugzilla/show_bug.cgi?id=17252 */
if (raise) {
Py_BEGIN_ALLOW_THREADS
Expand All @@ -134,6 +152,7 @@ py_getrandom(void *buffer, Py_ssize_t size, int raise)
else {
n = syscall(SYS_getrandom, buffer, size, flags);
}
#endif

if (n < 0) {
if (errno == ENOSYS) {
Expand Down Expand Up @@ -182,7 +201,7 @@ dev_urandom_noraise(unsigned char *buffer, Py_ssize_t size)

assert (0 < size);

#ifdef HAVE_GETRANDOM_SYSCALL
#ifdef PY_GETRANDOM
if (py_getrandom(buffer, size, 0) == 1)
return;
/* getrandom() is not supported by the running kernel, fall back
Expand Down Expand Up @@ -218,14 +237,14 @@ dev_urandom_python(char *buffer, Py_ssize_t size)
int fd;
Py_ssize_t n;
struct _Py_stat_struct st;
#ifdef HAVE_GETRANDOM_SYSCALL
#ifdef PY_GETRANDOM
int res;
#endif

if (size <= 0)
return 0;

#ifdef HAVE_GETRANDOM_SYSCALL
#ifdef PY_GETRANDOM
res = py_getrandom(buffer, size, 1);
if (res < 0)
return -1;
Expand Down Expand Up @@ -304,7 +323,7 @@ dev_urandom_close(void)
}
}

#endif /* HAVE_GETENTROPY */
#endif

/* Fill buffer with pseudo-random bytes generated by a linear congruent
generator (LCG):
Expand Down Expand Up @@ -345,7 +364,7 @@ _PyOS_URandom(void *buffer, Py_ssize_t size)

#ifdef MS_WINDOWS
return win32_urandom((unsigned char *)buffer, size, 1);
#elif HAVE_GETENTROPY
#elif defined(PY_GETENTROPY)
return py_getentropy(buffer, size, 0);
#else
return dev_urandom_python((char*)buffer, size);
Expand Down Expand Up @@ -392,7 +411,7 @@ _PyRandom_Init(void)
else {
#ifdef MS_WINDOWS
(void)win32_urandom(secret, secret_size, 0);
#elif HAVE_GETENTROPY
#elif defined(PY_GETENTROPY)
(void)py_getentropy(secret, secret_size, 1);
#else
dev_urandom_noraise(secret, secret_size);
Expand All @@ -408,7 +427,7 @@ _PyRandom_Fini(void)
CryptReleaseContext(hCryptProv, 0);
hCryptProv = 0;
}
#elif HAVE_GETENTROPY
#elif defined(PY_GETENTROPY)
/* nothing to clean */
#else
dev_urandom_close();
Expand Down
43 changes: 40 additions & 3 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -16085,11 +16085,11 @@ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
#include <sys/syscall.h>
int main() {
const int flags = 0;
char buffer[1];
int n;
const size_t buflen = sizeof(buffer);
const int flags = 0;
/* ignore the result, Python checks for ENOSYS at runtime */
(void)syscall(SYS_getrandom, buffer, sizeof(buffer), flags);
(void)syscall(SYS_getrandom, buffer, buflen, flags);
return 0;
}
Expand All @@ -16111,6 +16111,43 @@ $as_echo "#define HAVE_GETRANDOM_SYSCALL 1" >>confdefs.h

fi

# check if the getrandom() function is available
# the test was written for the Solaris function of <sys/random.h>
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for the getrandom() function" >&5
$as_echo_n "checking for the getrandom() function... " >&6; }
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include <sys/random.h>
int main() {
char buffer[1];
const size_t buflen = sizeof(buffer);
const int flags = 0;
/* ignore the result, Python checks for ENOSYS at runtime */
(void)getrandom(buffer, buflen, flags);
return 0;
}
_ACEOF
if ac_fn_c_try_link "$LINENO"; then :
have_getrandom=yes
else
have_getrandom=no
fi
rm -f core conftest.err conftest.$ac_objext \
conftest$ac_exeext conftest.$ac_ext
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $have_getrandom" >&5
$as_echo "$have_getrandom" >&6; }

if test "$have_getrandom" = yes; then

$as_echo "#define HAVE_GETRANDOM 1" >>confdefs.h

fi

# generate output files
ac_config_files="$ac_config_files Makefile.pre Modules/Setup.config Misc/python.pc Misc/python-config.sh"

Expand Down
31 changes: 28 additions & 3 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -5154,11 +5154,11 @@ AC_LINK_IFELSE(
#include <sys/syscall.h>
int main() {
const int flags = 0;
char buffer[1];
int n;
const size_t buflen = sizeof(buffer);
const int flags = 0;
/* ignore the result, Python checks for ENOSYS at runtime */
(void)syscall(SYS_getrandom, buffer, sizeof(buffer), flags);
(void)syscall(SYS_getrandom, buffer, buflen, flags);
return 0;
}
]])
Expand All @@ -5170,6 +5170,31 @@ if test "$have_getrandom_syscall" = yes; then
[Define to 1 if the Linux getrandom() syscall is available])
fi

# check if the getrandom() function is available
# the test was written for the Solaris function of <sys/random.h>
AC_MSG_CHECKING(for the getrandom() function)
AC_LINK_IFELSE(
[
AC_LANG_SOURCE([[
#include <sys/random.h>
int main() {
char buffer[1];
const size_t buflen = sizeof(buffer);
const int flags = 0;
/* ignore the result, Python checks for ENOSYS at runtime */
(void)getrandom(buffer, buflen, flags);
return 0;
}
]])
],[have_getrandom=yes],[have_getrandom=no])
AC_MSG_RESULT($have_getrandom)

if test "$have_getrandom" = yes; then
AC_DEFINE(HAVE_GETRANDOM, 1,
[Define to 1 if the getrandom() function is available])
fi

# generate output files
AC_CONFIG_FILES(Makefile.pre Modules/Setup.config Misc/python.pc Misc/python-config.sh)
AC_CONFIG_FILES([Modules/ld_so_aix], [chmod +x Modules/ld_so_aix])
Expand Down
3 changes: 3 additions & 0 deletions pyconfig.h.in
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,9 @@
/* Define to 1 if you have the `getpwent' function. */
#undef HAVE_GETPWENT

/* Define to 1 if the getrandom() function is available */
#undef HAVE_GETRANDOM

/* Define to 1 if the Linux getrandom() syscall is available */
#undef HAVE_GETRANDOM_SYSCALL

Expand Down

0 comments on commit bae2d62

Please sign in to comment.