From c30924b77c585cf5a28e4eb34924892f8e7e73b4 Mon Sep 17 00:00:00 2001
From: Masahiko Sawada <sawada.mshk@gmail.com>
Date: Tue, 21 Oct 2025 17:35:15 -0700
Subject: [PATCH v2 2/2] Support getrandom() as random source where available.

Use glibc's getrandom() function when strong_random_source is set to
'system' on Linux and Unix-like operating systems. This method is much
faster than reading from /dev/urandom. In particular, recent Linux
kernels support a vDSO implementation of getrandom(), which our
performance tests showed to be approximately 10x faster.

Reviewed-by:
Discussion: https://postgr.es/m/
---
 configure                   |  7 +++++--
 configure.ac                |  5 ++++-
 meson.build                 |  1 +
 src/include/pg_config.h.in  |  3 +++
 src/port/pg_strong_random.c | 41 ++++++++++++++++++++++++++++++++-----
 5 files changed, 49 insertions(+), 8 deletions(-)

diff --git a/configure b/configure
index c591f8d8fbd..1f269148851 100755
--- a/configure
+++ b/configure
@@ -15444,7 +15444,7 @@ fi
 LIBS_including_readline="$LIBS"
 LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
 
-for ac_func in backtrace_symbols copyfile copy_file_range elf_aux_info getauxval getifaddrs getpeerucred inet_pton kqueue localeconv_l mbstowcs_l posix_fallocate ppoll pthread_is_threaded_np setproctitle setproctitle_fast strsignal syncfs sync_file_range uselocale wcstombs_l
+for ac_func in backtrace_symbols copyfile copy_file_range elf_aux_info getauxval getifaddrs getpeerucred getrandom inet_pton kqueue localeconv_l mbstowcs_l posix_fallocate ppoll pthread_is_threaded_np setproctitle setproctitle_fast strsignal syncfs sync_file_range uselocale wcstombs_l
 do :
   as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
 ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
@@ -18326,6 +18326,9 @@ else
   if test x"$PORTNAME" = x"win32" ; then
     { $as_echo "$as_me:${as_lineno-$LINENO}: result: Windows native" >&5
 $as_echo "Windows native" >&6; }
+  elif test x"$ac_cv_func_getrandom" = x"yes" ; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: getrandom" >&5
+$as_echo "getrandom" >&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; }
@@ -18355,7 +18358,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, getrandom, or /dev/urandom as a source of random numbers." "$LINENO" 5
     fi
   fi
 
diff --git a/configure.ac b/configure.ac
index bfdb764f059..0ceced87ada 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1780,6 +1780,7 @@ AC_CHECK_FUNCS(m4_normalize([
 	getauxval
 	getifaddrs
 	getpeerucred
+	getrandom
 	inet_pton
 	kqueue
 	localeconv_l
@@ -2321,6 +2322,8 @@ else
   # be used.
   if test x"$PORTNAME" = x"win32" ; then
     AC_MSG_RESULT([Windows native])
+  elif test x"$ac_cv_func_getrandom" = x"yes" ; then
+    AC_MSG_RESULT(getrandom)
   elif test x"$cross_compiling" = x"yes"; then
     AC_MSG_RESULT([assuming /dev/urandom])
   else
@@ -2330,7 +2333,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, getrandom, or /dev/urandom as a source of random numbers.])
     fi
   fi
   AC_DEFINE([STRONG_RANDOM_SOURCE_SYSTEM], 1, [Define to 1 to use system native source for strong random number generation])
diff --git a/meson.build b/meson.build
index 90918fc12da..db55125a2bf 100644
--- a/meson.build
+++ b/meson.build
@@ -2892,6 +2892,7 @@ func_checks = [
   ['getopt_long', {'dependencies': [getopt_dep, gnugetopt_dep], 'skip': always_replace_getopt_long}],
   ['getpeereid'],
   ['getpeerucred'],
+  ['getrandom'],
   ['inet_aton'],
   ['inet_pton'],
   ['kqueue'],
diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in
index 4aeceb53eb4..4534db6d0ef 100644
--- a/src/include/pg_config.h.in
+++ b/src/include/pg_config.h.in
@@ -190,6 +190,9 @@
 /* Define to 1 if you have the `getpeerucred' function. */
 #undef HAVE_GETPEERUCRED
 
+/* Define to 1 if you have the `getrandom' function. */
+#undef HAVE_GETRANDOM
+
 /* Define to 1 if you have the <gssapi_ext.h> header file. */
 #undef HAVE_GSSAPI_EXT_H
 
diff --git a/src/port/pg_strong_random.c b/src/port/pg_strong_random.c
index 0d3f00f588f..66ee335ce17 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. glibc's getrandom() 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,12 +135,42 @@ pg_strong_random(void *buf, size_t len)
 	return false;
 }
 
-#else							/* STRONG_RANDOM_SOURCE_SYSTEM and not WIN32 */
+#elif HAVE_GETRANDOM			/* STRONG_RANDOM_SOURCE_SYSTEM and
+								 * HAVE_GETRANDOM */
+#include <sys/random.h>
 
-/*
- * Without OpenSSL or Win32 support, just read /dev/urandom ourselves.
- */
+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)
+	{
+		/* Get random data from the urandom source in blocking mode */
+		res = getrandom(p, len, 0);
+		if (res <= 0)
+		{
+			if (errno == EINTR)
+				continue;		/* interrupted by signal, just retry */
+
+			return false;
+		}
+
+		p += res;
+		len -= res;
+	}
+
+	return true;
+}
 
+#else							/* not OpenSSL, WIN32, or HAVE_GETRANDOM */
 void
 pg_strong_random_init(void)
 {
-- 
2.47.3

