Since CheriBSD is derived from FreeBSD, it sounds likely that FreeBSD's random()
function is equally not multithread-safe.

To verify this hypothesis, I wrote a MT-safety test for random(). The result
(with a REPEAT_COUNT of 1000000):

glibc         OK
musl libc     OK
FreeBSD 11.0  Expected value #932913 not found in multithreaded results. (18 
failures among 100 runs)
FreeBSD 13.2  Expected value #712890 not found in multithreaded results. (2 
failures among 100 runs)
NetBSD        OK
OpenBSD       Expected value #0 not found in multithreaded results. => SKIP
macOS         Expected value #16 not found in multithreaded results.
AIX           OK
Solaris10     Expected value #4 not found in multithreaded results.
Solaris11     Expected value #46 not found in multithreaded results.
Cygwin 2.9.0  Expected value #2499 not found in multithreaded results.
Cygwin 3.4.6  Expected value #367 not found in multithreaded results.
mingw         OK (uses gnulib implementation)
MSVC          OK (uses gnulib implementation)
Haiku         Expected value #188587 not found in multithreaded results. (1 
failure among 100 runs)
Minix         SKIP
Android       OK

So, in general, it requires 100'000'000 repetitions to determine
whether the function is MT-safe. But that's too long for a unit test.
I'm therefore reducing it to 100'000 repetitions and hardcoding the
known platforms in the configure test. Even these 100'000 repetitions
take 3 seconds on Haiku or 1 second on mingw or MSVC.


2023-11-10  Bruno Haible  <br...@clisp.org>

        doc: Mention an srandom limitation on OpenBSD.
        * doc/posix-functions/srandom.texi: Mention the OpenBSD limitation.

2023-11-10  Bruno Haible  <br...@clisp.org>

        random tests: Add multithread-safety test.
        * tests/test-random-mt.c: New file.
        * modules/random-tests (Files): Add it.
        (Depends-on): Add xalloc, thread, yield.
        (Makefile.am): Also build and test test-random-mt.

        random: Fix multithread-safety bug in general.
        * m4/random.m4 (gl_FUNC_RANDOM): Override also macOS, FreeBSD, Solaris,
        Cygwin, Haiku.
        * doc/posix-functions/random.texi: Mention the wider scope of the
        multithread-safety bug.

>From 53483b103f8d733df604e03f5260486ebf1a4496 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 10 Nov 2023 16:46:17 +0100
Subject: [PATCH 1/3] random: Fix multithread-safety bug in general.

* m4/random.m4 (gl_FUNC_RANDOM): Override also macOS, FreeBSD, Solaris,
Cygwin, Haiku.
* doc/posix-functions/random.texi: Mention the wider scope of the
multithread-safety bug.
---
 ChangeLog                       |  8 ++++++++
 doc/posix-functions/random.texi |  4 ++--
 m4/random.m4                    | 11 +++++++----
 3 files changed, 17 insertions(+), 6 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 5c92da54ec..d38e5b5665 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2023-11-10  Bruno Haible  <br...@clisp.org>
+
+	random: Fix multithread-safety bug in general.
+	* m4/random.m4 (gl_FUNC_RANDOM): Override also macOS, FreeBSD, Solaris,
+	Cygwin, Haiku.
+	* doc/posix-functions/random.texi: Mention the wider scope of the
+	multithread-safety bug.
+
 2023-11-09  Bruno Haible  <br...@clisp.org>
 
 	sigsegv: Improve port to CHERI.
diff --git a/doc/posix-functions/random.texi b/doc/posix-functions/random.texi
index e0bb8e76bc..84cacbe8b5 100644
--- a/doc/posix-functions/random.texi
+++ b/doc/posix-functions/random.texi
@@ -15,8 +15,8 @@
 This function is only defined as an inline function on some platforms:
 Android 4.4.
 @item
-This function crashes when used in multithreaded programs on some platforms:
-CheriBSD.
+This function is not multithread-safe on some platforms:
+macOS 12.5, FreeBSD 13.2, Solaris 11.4, Cygwin 3.4.6, Haiku.
 @end itemize
 
 Portability problems not fixed by Gnulib:
diff --git a/m4/random.m4 b/m4/random.m4
index 69bac2459a..4e4f01b8ed 100644
--- a/m4/random.m4
+++ b/m4/random.m4
@@ -1,4 +1,4 @@
-# random.m4 serial 7
+# random.m4 serial 8
 dnl Copyright (C) 2012-2023 Free Software Foundation, Inc.
 dnl This file is free software; the Free Software Foundation
 dnl gives unlimited permission to copy and/or distribute it,
@@ -44,10 +44,13 @@ AC_DEFUN([gl_FUNC_RANDOM]
       future*) REPLACE_SETSTATE=1 ;;
     esac
   fi
-  dnl On CheriBSD, random() lacks locking, leading to an out-of-bounds read
-  dnl inside random_r.
+  dnl On several platforms, random() is not multithread-safe.
   if test $ac_cv_func_initstate = no || test $ac_cv_func_setstate = no \
-     || case "$host" in aarch64c-*-freebsd*) true;; *) false;; esac; then
+     || case "$host_os" in \
+          darwin* | freebsd* | solaris* | cygwin* | haiku*) true ;; \
+          *) false ;; \
+        esac
+  then
     dnl In order to define initstate or setstate, we need to define all the
     dnl functions at once.
     REPLACE_RANDOM=1
-- 
2.34.1

>From b5eede6ff76d45112ea1973f12a131228f36a92e Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 10 Nov 2023 16:48:18 +0100
Subject: [PATCH 2/3] random tests: Add multithread-safety test.

* tests/test-random-mt.c: New file.
* modules/random-tests (Files): Add it.
(Depends-on): Add xalloc, thread, yield.
(Makefile.am): Also build and test test-random-mt.
---
 ChangeLog              |   6 ++
 modules/random-tests   |   9 ++-
 tests/test-random-mt.c | 169 +++++++++++++++++++++++++++++++++++++++++
 3 files changed, 182 insertions(+), 2 deletions(-)
 create mode 100644 tests/test-random-mt.c

diff --git a/ChangeLog b/ChangeLog
index d38e5b5665..6be74ca3c1 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,11 @@
 2023-11-10  Bruno Haible  <br...@clisp.org>
 
+	random tests: Add multithread-safety test.
+	* tests/test-random-mt.c: New file.
+	* modules/random-tests (Files): Add it.
+	(Depends-on): Add xalloc, thread, yield.
+	(Makefile.am): Also build and test test-random-mt.
+
 	random: Fix multithread-safety bug in general.
 	* m4/random.m4 (gl_FUNC_RANDOM): Override also macOS, FreeBSD, Solaris,
 	Cygwin, Haiku.
diff --git a/modules/random-tests b/modules/random-tests
index 27e5fb9387..d82e73c8c8 100644
--- a/modules/random-tests
+++ b/modules/random-tests
@@ -1,12 +1,17 @@
 Files:
 tests/test-random.c
+tests/test-random-mt.c
 tests/signature.h
 tests/macros.h
 
 Depends-on:
+xalloc
+thread
+yield
 
 configure.ac:
 
 Makefile.am:
-TESTS += test-random
-check_PROGRAMS += test-random
+TESTS += test-random test-random-mt
+check_PROGRAMS += test-random test-random-mt
+test_random_mt_LDADD = $(LDADD) $(LIBINTL) $(LIBMULTITHREAD) $(YIELD_LIB)
diff --git a/tests/test-random-mt.c b/tests/test-random-mt.c
new file mode 100644
index 0000000000..9e95477ce0
--- /dev/null
+++ b/tests/test-random-mt.c
@@ -0,0 +1,169 @@
+/* Multithread-safety test for random().
+   Copyright (C) 2023 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* Written by Bruno Haible <br...@clisp.org>, 2023.  */
+
+#include <config.h>
+
+#if USE_ISOC_THREADS || USE_POSIX_THREADS || USE_ISOC_AND_POSIX_THREADS || USE_WINDOWS_THREADS
+
+/* Whether to help the scheduler through explicit yield().
+   Uncomment this to see if the operating system has a fair scheduler.  */
+#define EXPLICIT_YIELD 1
+
+/* Number of simultaneous threads.  */
+#define THREAD_COUNT 4
+
+/* Number of random() invocations operations performed in each thread.
+   This value is chosen so that the unit test terminates quickly.
+   To reliably determine whether a random() implementation is multithread-safe,
+   set REPEAT_COUNT to 1000000 and run the test 100 times:
+     $ for i in `seq 100`; do ./test-random-mt; done
+ */
+#define REPEAT_COUNT 100000
+
+/* Specification.  */
+#include <stdlib.h>
+
+#include <stdio.h>
+
+#if EXPLICIT_YIELD
+# include <sched.h>
+#endif
+
+#include "glthread/thread.h"
+#include "xalloc.h"
+
+#if EXPLICIT_YIELD
+# define yield() sched_yield ()
+#else
+# define yield()
+#endif
+
+/* This test runs REPEAT_COUNT invocations of random() in each thread and stores
+   the result, then compares the first REPEAT_COUNT among these
+     THREAD_COUNT * REPEAT_COUNT
+   random numbers against a precomputed sequence with the same seed.  */
+
+static void *
+random_invocator_thread (void *arg)
+{
+  long *storage = (long *) arg;
+  int repeat;
+
+  for (repeat = 0; repeat < REPEAT_COUNT; repeat++)
+    {
+      storage[repeat] = random ();
+      yield ();
+    }
+
+  return NULL;
+}
+
+int
+main ()
+{
+  unsigned int seed = 19891109;
+
+  /* First, get the expected sequence of random() results.  */
+  srandom (seed);
+  long *expected = XNMALLOC (REPEAT_COUNT, long);
+  {
+    int repeat;
+    for (repeat = 0; repeat < REPEAT_COUNT; repeat++)
+      expected[repeat] = random ();
+  }
+
+  /* Then, run REPEAT_COUNT invocations of random() each, in THREAD_COUNT
+     separate threads.  */
+  gl_thread_t threads[THREAD_COUNT];
+  long *thread_results[THREAD_COUNT];
+  srandom (seed);
+  {
+    int i;
+    for (i = 0; i < THREAD_COUNT; i++)
+      thread_results[i] = XNMALLOC (REPEAT_COUNT, long);
+    for (i = 0; i < THREAD_COUNT; i++)
+      threads[i] =
+        gl_thread_create (random_invocator_thread, thread_results[i]);
+  }
+
+  /* Wait for the threads to terminate.  */
+  {
+    int i;
+    for (i = 0; i < THREAD_COUNT; i++)
+      gl_thread_join (threads[i], NULL);
+  }
+
+  /* Finally, determine whether the threads produced the same sequence of
+     random() results.  */
+  {
+    int expected_index;
+    int result_index[THREAD_COUNT];
+    int i;
+
+    for (i = 0; i < THREAD_COUNT; i++)
+      result_index[i] = 0;
+
+    for (expected_index = 0; expected_index < REPEAT_COUNT; expected_index++)
+      {
+        long expected_value = expected[expected_index];
+
+        for (i = 0; i < THREAD_COUNT; i++)
+          {
+            if (thread_results[i][result_index[i]] == expected_value)
+              {
+                result_index[i]++;
+                break;
+              }
+          }
+        if (i == THREAD_COUNT)
+          {
+            if (expected_index == 0)
+              {
+                /* This occurs on platforms like OpenBSD, where srandom() has no
+                   effect and random() always return non-deterministic values.
+                   Mark the test as SKIP.  */
+                fprintf (stderr, "Skipping test: random() is non-deterministic.\n");
+                return 77;
+              }
+            else
+              {
+                fprintf (stderr, "Expected value #%d not found in multithreaded results.\n",
+                         expected_index);
+                return 1;
+              }
+          }
+      }
+  }
+
+  return 0;
+}
+
+#else
+
+/* No multithreading available.  */
+
+#include <stdio.h>
+
+int
+main ()
+{
+  fputs ("Skipping test: multithreading not enabled\n", stderr);
+  return 77;
+}
+
+#endif
-- 
2.34.1

>From 6c7ea007d01115b6727e16bb070abbcf2dbef5ea Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Fri, 10 Nov 2023 16:51:02 +0100
Subject: [PATCH 3/3] doc: Mention an srandom limitation on OpenBSD.

* doc/posix-functions/srandom.texi: Mention the OpenBSD limitation.
---
 ChangeLog                        | 5 +++++
 doc/posix-functions/srandom.texi | 4 ++++
 2 files changed, 9 insertions(+)

diff --git a/ChangeLog b/ChangeLog
index 6be74ca3c1..27c6791238 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+2023-11-10  Bruno Haible  <br...@clisp.org>
+
+	doc: Mention an srandom limitation on OpenBSD.
+	* doc/posix-functions/srandom.texi: Mention the OpenBSD limitation.
+
 2023-11-10  Bruno Haible  <br...@clisp.org>
 
 	random tests: Add multithread-safety test.
diff --git a/doc/posix-functions/srandom.texi b/doc/posix-functions/srandom.texi
index 12df9161ae..395ce927a8 100644
--- a/doc/posix-functions/srandom.texi
+++ b/doc/posix-functions/srandom.texi
@@ -26,4 +26,8 @@
 The parameter is @code{unsigned long} instead of @code{unsigned int} on some
 platforms:
 MidnightBSD 2.0.
+@item
+This function has no effect on some platforms:
+OpenBSD 7.4.
+This platform has, instead, a function @code{srandom_deterministic}.
 @end itemize
-- 
2.34.1

Reply via email to