Re: Support getrandom() for pg_strong_random() source - Mailing list pgsql-hackers
From | Dagfinn Ilmari Mannsåker |
---|---|
Subject | Re: Support getrandom() for pg_strong_random() source |
Date | |
Msg-id | 87y0s5hktp.fsf@wibble.ilmari.org Whole thread Raw |
In response to | Re: Support getrandom() for pg_strong_random() source (Dagfinn Ilmari Mannsåker <ilmari@ilmari.org>) |
List | pgsql-hackers |
Dagfinn Ilmari Mannsåker <ilmari@ilmari.org> writes: > Masahiko Sawada <sawada.mshk@gmail.com> writes: > >> On Tue, Jul 29, 2025 at 8:55 AM Jacob Champion >> <jacob.champion@enterprisedb.com> wrote: >>> >>> On Mon, Jul 28, 2025 at 6:30 PM Michael Paquier <michael@paquier.xyz> wrote: >>> > My understanding of the problem is that it is a choice of efficiency >>> > vs entropy, and that it's not really possible to have both parts of >>> > the cake. >> >> Agreed. I think the optimal choice would depend on the specific use >> case. For instance, since UUIDs are not intended for security >> purposes, they don't require particularly high entropy. In UUID >> generation, the efficiency of random data generation tends to be >> prioritized over the quality of randomness. >> >>> >>> > Could getentropy() be more efficient at the end on most platforms, >>> > meaning that this could limit the meaning of having a GUC switch? >>> >>> I don't know. [2] implies that the performance comparison depends on >>> several factors, and falls in favor of OpenSSL when the number of >>> bytes per call is large -- but our use of pg_strong_random() is >>> generally on small buffers. We would need to do a _lot_ more research >>> before, say, switching any defaults. >> >> The performance issue with getentropy, particularly when len=1024, >> likely stems from the need for multiple getentropy() calls due to its >> 256-byte length restriction. >> >> Analysis of RAND_bytes() through strace reveals that it internally >> makes calls to getrandom() with a fixed length of 32 bytes. While I'm >> uncertain of the exact purpose, it's logical that a single >> getentropy() call would be more efficient than RAND_bytes(), which >> involves additional overhead beyond just calling getrandom(), >> especially when dealing with smaller byte sizes. >> >> I've updated the patch to support getentropy() instead of getrandom(). > > Thanks, just a few comments: > > The blog post at > https://dotat.at/@/2024-10-01-getentropy.html#portability-of-getentropy- > points out a couple of caveats: > > * Originally getentropy() was declared in <sys/random.h> but POSIX > declares it in <unistd.h>. You need to include both headers to be > sure. > > So the probes need to include both <sys/random.h> (if avaliable) and > <unistd.h>, I realised I got the conditional for this wrong, since cdata.get('HAVE_SYS_RANDOM_H') can return either the integer 1 or the boolean false, so it needs to be format()-ed and compared to a string. Updated patch attached. - ilmari From 88b155c691806bd116b001334cad8ffc1f43e741 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dagfinn=20Ilmari=20Manns=C3=A5ker?= <ilmari@ilmari.org> Date: Wed, 30 Jul 2025 12:36:46 +0100 Subject: [PATCH v4] Support getentropy() as source of pg_strong_random() where available --- configure | 24 ++++++++++++++++++- configure.ac | 8 ++++++- meson.build | 9 ++++++++ src/include/pg_config.h.in | 6 +++++ src/port/pg_strong_random.c | 46 ++++++++++++++++++++++++++++++++++--- 5 files changed, 88 insertions(+), 5 deletions(-) diff --git a/configure b/configure index 507a2437c33..f11cd4265bf 100755 --- a/configure +++ b/configure @@ -16243,6 +16243,25 @@ cat >>confdefs.h <<_ACEOF _ACEOF +ac_fn_c_check_header_mongrel "$LINENO" "sys/random.h" "ac_cv_header_sys_random_h" "$ac_includes_default" +if test "x$ac_cv_header_sys_random_h" = xyes; then : + for ac_func in getentropy +do : + ac_fn_c_check_func "$LINENO" "getentropy" "ac_cv_func_getentropy" +if test "x$ac_cv_func_getentropy" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_GETENTROPY 1 +_ACEOF + +$as_echo "#define HAVE_GETENTROPY 1" >>confdefs.h + +fi +done + +fi + + + ac_fn_c_check_func "$LINENO" "explicit_bzero" "ac_cv_func_explicit_bzero" if test "x$ac_cv_func_explicit_bzero" = xyes; then : $as_echo "#define HAVE_EXPLICIT_BZERO 1" >>confdefs.h @@ -18479,6 +18498,9 @@ $as_echo "Windows native" >&6; } elif test x"$cross_compiling" = x"yes"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: assuming /dev/urandom" >&5 $as_echo "assuming /dev/urandom" >&6; } +elif test x"$ac_cv_func_getentropy" = x"yes"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: getentropy" >&5 +$as_echo "getentropy" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: /dev/urandom" >&5 $as_echo "/dev/urandom" >&6; } @@ -18505,7 +18527,7 @@ fi if test x"$ac_cv_file__dev_urandom" = x"no" ; then as_fn_error $? " no source of strong random numbers was found -PostgreSQL can use OpenSSL, native Windows API or /dev/urandom as a source of random numbers." "$LINENO" 5 +PostgreSQL can use OpenSSL, native Windows API, getentropy function, or /dev/urandom as a source of random numbers." "$LINENO"5 fi fi diff --git a/configure.ac b/configure.ac index 5f4548adc5c..9cf453fcd23 100644 --- a/configure.ac +++ b/configure.ac @@ -1850,6 +1850,10 @@ AC_CHECK_DECLS([memset_s], [], [], [#define __STDC_WANT_LIB_EXT1__ 1 # This is probably only present on macOS, but may as well check always AC_CHECK_DECLS(F_FULLFSYNC, [], [], [#include <fcntl.h>]) +AC_CHECK_HEADER([sys/random.h], + [AC_CHECK_FUNCS([getentropy], + [AC_DEFINE(HAVE_GETENTROPY, 1, [Define to 1 if you have getentropy])])]) + AC_REPLACE_FUNCS(m4_normalize([ explicit_bzero getopt @@ -2314,6 +2318,8 @@ elif test x"$PORTNAME" = x"win32" ; then AC_MSG_RESULT([Windows native]) elif test x"$cross_compiling" = x"yes"; then AC_MSG_RESULT([assuming /dev/urandom]) +elif test x"$ac_cv_func_getentropy" = x"yes"; then + AC_MSG_RESULT(getentropy) else AC_MSG_RESULT([/dev/urandom]) AC_CHECK_FILE([/dev/urandom], [], []) @@ -2321,7 +2327,7 @@ else if test x"$ac_cv_file__dev_urandom" = x"no" ; then AC_MSG_ERROR([ no source of strong random numbers was found -PostgreSQL can use OpenSSL, native Windows API or /dev/urandom as a source of random numbers.]) +PostgreSQL can use OpenSSL, native Windows API, getentropy function, or /dev/urandom as a source of random numbers.]) fi fi diff --git a/meson.build b/meson.build index ca423dc8e12..70f5d6494cb 100644 --- a/meson.build +++ b/meson.build @@ -2627,6 +2627,7 @@ header_checks = [ 'sys/personality.h', 'sys/prctl.h', 'sys/procctl.h', + 'sys/random.h', 'sys/signalfd.h', 'sys/ucred.h', 'termios.h', @@ -2705,6 +2706,14 @@ return 0; don't.'''.format(func)) endforeach +if cc.has_function('getentropy', + args: test_c_args, + prefix: ''' +#include <unistd.h> +@0@ +'''.format('@0@'.format(cdata.get('HAVE_SYS_RANDOM_H')) == '1' ? '#include <sys/random.h>' : '')) + cdata.set('HAVE_GETENTROPY', 1) +endif if cc.has_type('struct option', args: test_c_args, include_directories: postgres_inc, diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index c4dc5d72bdb..147ab293518 100644 --- a/src/include/pg_config.h.in +++ b/src/include/pg_config.h.in @@ -172,6 +172,9 @@ /* Define to 1 if you have the `getauxval' function. */ #undef HAVE_GETAUXVAL +/* Define to 1 if you have getentropy */ +#undef HAVE_GETENTROPY + /* Define to 1 if you have the `getifaddrs' function. */ #undef HAVE_GETIFADDRS @@ -448,6 +451,9 @@ /* Define to 1 if you have the <sys/procctl.h> header file. */ #undef HAVE_SYS_PROCCTL_H +/* Define to 1 if you have the <sys/random.h> header file. */ +#undef HAVE_SYS_RANDOM_H + /* Define to 1 if you have the <sys/signalfd.h> header file. */ #undef HAVE_SYS_SIGNALFD_H diff --git a/src/port/pg_strong_random.c b/src/port/pg_strong_random.c index ea6780dcc9f..da482e96ae7 100644 --- a/src/port/pg_strong_random.c +++ b/src/port/pg_strong_random.c @@ -40,7 +40,8 @@ * * 1. OpenSSL's RAND_bytes() * 2. Windows' CryptGenRandom() function - * 3. /dev/urandom + * 3. getentropy() function + * 4. /dev/urandom * * Returns true on success, and false if none of the sources * were available. NB: It is important to check the return value! @@ -134,10 +135,49 @@ pg_strong_random(void *buf, size_t len) return false; } -#else /* not USE_OPENSSL or WIN32 */ +#elif HAVE_GETENTROPY + +#include <limits.h> + +#ifdef HAVE_SYS_RANDOM_H +#include <sys/random.h> +#endif + +#ifndef GETENTROPY_MAX +#define GETENTROPY_MAX 256 +#endif + +void +pg_strong_random_init(void) +{ + /* No initialization needed */ +} + +bool +pg_strong_random(void *buf, size_t len) +{ + char *p = buf; + ssize_t res; + + while (len) + { + size_t l = Min(len, GETENTROPY_MAX); + + res = getentropy(p, l); + if (res < 0) + return false; + + p += l; + len -= l; + } + + return true; +} + +#else /* not USE_OPENSSL, WIN32, or HAVE_GETENTROPY */ /* - * Without OpenSSL or Win32 support, just read /dev/urandom ourselves. + * Without OpenSSL, Win32, or getentropy() support, just read /dev/urandom ourselves. */ void -- 2.50.1
pgsql-hackers by date: