Dagfinn Ilmari Mannsåker <[email protected]> writes:
> Masahiko Sawada <[email protected]> writes:
>
>> On Tue, Jul 29, 2025 at 8:55 AM Jacob Champion
>> <[email protected]> wrote:
>>>
>>> On Mon, Jul 28, 2025 at 6:30 PM Michael Paquier <[email protected]> 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?= <[email protected]>
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