On CheriBSD, I see these test failures:

FAIL: test-pthread-tss
FAIL: test-thread_local
FAIL: test-tls
FAIL: test-tss

In the debugger I see this:

(gdb) where
#0  random_r (estate=0x42319000 [rwRW,0x42319000-0x423190d0])
    at 
/local/scratch/jenkins/workspace/CheriBSD-pipeline_releng_22.12@2/cheribsd/lib/libc/stdlib/random.c:471
#1  0x0000000040394578 in rand () at 
/local/scratch/jenkins/workspace/CheriBSD-pipeline_releng_22.12@2/cheribsd/lib/libc/stdlib/rand.c:85
#2  0x0000000000111468 in perhaps_yield () at 
../../gltests/test-thread_local.c:79
#3  0x00000000001113dc in worker_thread (arg=0x8) at 
../../gltests/test-thread_local.c:127
#4  0x00000000401a19d4 in thrd_entry (arg=<optimized out>)
    at 
/local/scratch/jenkins/workspace/CheriBSD-pipeline_releng_22.12@2/cheribsd/lib/libstdthreads/thrd.c:52
#5  0x00000000401e3de4 in thread_start (curthread=0x408f4a00 
[rwRW,0x408f4a00-0x408f53d0])
    at 
/local/scratch/jenkins/workspace/CheriBSD-pipeline_releng_22.12@2/cheribsd/lib/libthr/thread/thr_create.c:319
#6  0x00000000401e38ec in _pthread_create (thread=0xfffffff7fe90 
[rwRW,0xfffffff7fe10-0xfffffff7ff10], attr=<optimized out>, 
start_routine=<optimized out>, 
    arg=<optimized out>) at 
/local/scratch/jenkins/workspace/CheriBSD-pipeline_releng_22.12@2/cheribsd/lib/libthr/thread/thr_create.c:214

(gdb) info locals
r = 0x423190d0 [rwRW,0x42319000-0x423190d0]
f = 0x42319054 [rwRW,0x42319000-0x423190d0]
i = <optimized out>

So, rand() is crashing in multithreaded tests. But it works fine in the many
single-threaded test programs. Could it be that rand() is not multithread-
safe?

According to the standards, rand() should be multithread-safe.

When I add a 'rand' module and force it to invoke the gnulib implementation
of random_r (via random()), I see a crash inside gnulib's random_r instead.
But wait! That's because gnulib's 'random' implementation is also not
multithread-safe:

  #ifdef _LIBC
  # include <libc-lock.h>
  #else
  /* Allow memory races; that's random enough.  */
  # define __libc_lock_define_initialized(class, name)
  # define __libc_lock_lock(name) ((void) 0)
  # define __libc_lock_unlock(name) ((void) 0)
  #endif

Memory races are not "random"; they are undefined behaviour. They crash on
CheriBSD and they may crash (or return low-quality random numbers) on other
systems as well.

With the patches below, rand() becomes multithread-safe, and the 4 test failures
disappear.


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

        rand: Add tests.
        * tests/test-rand.c: New file.
        * modules/rand-tests: New file.

        rand: New module.
        * lib/rand.c: New file, based on glibc/stdlib/rand.c.
        * m4/rand.m4: New file.
        * modules/rand: New file.
        * doc/posix-functions/rand.texi: Mention the new module.

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

        random: Fix multithread-safety bug on CheriBSD.
        * m4/random.m4 (gl_FUNC_RANDOM): Override on CheriBSD.
        * lib/random.c: Include glthread/lock.h.
        (__libc_lock_define_initialized, __libc_lock_lock, __libc_lock_unlock):
        Define to do real locking.
        * modules/random (Depends-on): Add lock.
        * doc/posix-functions/random.texi: Mention the multithread-safety
        problem.

>From 0ab4517a51b70490b833ed099bfffe528c329503 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Thu, 9 Nov 2023 15:56:32 +0100
Subject: [PATCH 1/3] random: Fix multithread-safety bug on CheriBSD.

* m4/random.m4 (gl_FUNC_RANDOM): Override on CheriBSD.
* lib/random.c: Include glthread/lock.h.
(__libc_lock_define_initialized, __libc_lock_lock, __libc_lock_unlock):
Define to do real locking.
* modules/random (Depends-on): Add lock.
* doc/posix-functions/random.texi: Mention the multithread-safety
problem.
---
 ChangeLog                       | 11 +++++++++++
 doc/posix-functions/random.texi |  3 ++-
 lib/random.c                    |  8 ++++----
 m4/random.m4                    |  8 ++++++--
 modules/random                  |  3 ++-
 5 files changed, 25 insertions(+), 8 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 51ba73819b..35a043ce7d 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,14 @@
+2023-11-09  Bruno Haible  <br...@clisp.org>
+
+	random: Fix multithread-safety bug on CheriBSD.
+	* m4/random.m4 (gl_FUNC_RANDOM): Override on CheriBSD.
+	* lib/random.c: Include glthread/lock.h.
+	(__libc_lock_define_initialized, __libc_lock_lock, __libc_lock_unlock):
+	Define to do real locking.
+	* modules/random (Depends-on): Add lock.
+	* doc/posix-functions/random.texi: Mention the multithread-safety
+	problem.
+
 2023-11-09  Bruno Haible  <br...@clisp.org>
 
 	host-cpu-c-abi: Port to CHERI.
diff --git a/doc/posix-functions/random.texi b/doc/posix-functions/random.texi
index 19349617be..e0bb8e76bc 100644
--- a/doc/posix-functions/random.texi
+++ b/doc/posix-functions/random.texi
@@ -15,7 +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.
 @end itemize
 
 Portability problems not fixed by Gnulib:
diff --git a/lib/random.c b/lib/random.c
index 50080ef2d5..ac5999101a 100644
--- a/lib/random.c
+++ b/lib/random.c
@@ -67,10 +67,10 @@
 #ifdef _LIBC
 # include <libc-lock.h>
 #else
-/* Allow memory races; that's random enough.  */
-# define __libc_lock_define_initialized(class, name)
-# define __libc_lock_lock(name) ((void) 0)
-# define __libc_lock_unlock(name) ((void) 0)
+# include "glthread/lock.h"
+# define __libc_lock_define_initialized gl_lock_define_initialized
+# define __libc_lock_lock gl_lock_lock
+# define __libc_lock_unlock gl_lock_unlock
 #endif
 
 /* An improved random number generation package.  In addition to the standard
diff --git a/m4/random.m4 b/m4/random.m4
index b0d1cb86ee..69bac2459a 100644
--- a/m4/random.m4
+++ b/m4/random.m4
@@ -1,4 +1,4 @@
-# random.m4 serial 6
+# random.m4 serial 7
 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,
@@ -7,6 +7,7 @@
 AC_DEFUN([gl_FUNC_RANDOM],
 [
   AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
 
   dnl We can't use AC_CHECK_FUNC here, because random() is defined as a
   dnl static inline function when compiling for Android 4.4 or older.
@@ -43,7 +44,10 @@ AC_DEFUN([gl_FUNC_RANDOM]
       future*) REPLACE_SETSTATE=1 ;;
     esac
   fi
-  if test $ac_cv_func_initstate = no || test $ac_cv_func_setstate = no; then
+  dnl On CheriBSD, random() lacks locking, leading to an out-of-bounds read
+  dnl inside random_r.
+  if test $ac_cv_func_initstate = no || test $ac_cv_func_setstate = no \
+     || case "$host" in aarch64c-*-freebsd*) 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
diff --git a/modules/random b/modules/random
index a6ccbb777d..9774088bda 100644
--- a/modules/random
+++ b/modules/random
@@ -6,8 +6,9 @@ lib/random.c
 m4/random.m4
 
 Depends-on:
-libc-config     [test $HAVE_RANDOM = 0 || test $REPLACE_RANDOM = 1 || test $REPLACE_INITSTATE = 1 || test $REPLACE_SETSTATE = 1]
 stdlib
+libc-config     [test $HAVE_RANDOM = 0 || test $REPLACE_RANDOM = 1 || test $REPLACE_INITSTATE = 1 || test $REPLACE_SETSTATE = 1]
+lock            [test $HAVE_RANDOM = 0 || test $REPLACE_RANDOM = 1 || test $REPLACE_INITSTATE = 1 || test $REPLACE_SETSTATE = 1]
 stdint          [test $HAVE_RANDOM = 0 || test $REPLACE_RANDOM = 1 || test $REPLACE_INITSTATE = 1 || test $REPLACE_SETSTATE = 1]
 random_r        [test $HAVE_RANDOM = 0 || test $REPLACE_RANDOM = 1 || test $REPLACE_INITSTATE = 1 || test $REPLACE_SETSTATE = 1]
 
-- 
2.34.1

>From 9d7322634e8e64aed2d53a67ab221b16f83aaabd Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Thu, 9 Nov 2023 16:00:28 +0100
Subject: [PATCH 2/3] rand: New module.

* lib/rand.c: New file, based on glibc/stdlib/rand.c.
* m4/rand.m4: New file.
* modules/rand: New file.
* doc/posix-functions/rand.texi: Mention the new module.
---
 ChangeLog                     |  8 ++++++++
 doc/posix-functions/rand.texi |  5 ++++-
 lib/rand.c                    | 30 ++++++++++++++++++++++++++++++
 m4/rand.m4                    | 17 +++++++++++++++++
 modules/rand                  | 29 +++++++++++++++++++++++++++++
 5 files changed, 88 insertions(+), 1 deletion(-)
 create mode 100644 lib/rand.c
 create mode 100644 m4/rand.m4
 create mode 100644 modules/rand

diff --git a/ChangeLog b/ChangeLog
index 35a043ce7d..a6e6a8045a 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2023-11-09  Bruno Haible  <br...@clisp.org>
+
+	rand: New module.
+	* lib/rand.c: New file, based on glibc/stdlib/rand.c.
+	* m4/rand.m4: New file.
+	* modules/rand: New file.
+	* doc/posix-functions/rand.texi: Mention the new module.
+
 2023-11-09  Bruno Haible  <br...@clisp.org>
 
 	random: Fix multithread-safety bug on CheriBSD.
diff --git a/doc/posix-functions/rand.texi b/doc/posix-functions/rand.texi
index 3d23c8d609..2f36a548a7 100644
--- a/doc/posix-functions/rand.texi
+++ b/doc/posix-functions/rand.texi
@@ -4,10 +4,13 @@
 
 POSIX specification:@* @url{https://pubs.opengroup.org/onlinepubs/9699919799/functions/rand.html}
 
-Gnulib module: ---
+Gnulib module: rand
 
 Portability problems fixed by Gnulib:
 @itemize
+@item
+This function crashes when used in multithreaded programs on some platforms:
+CheriBSD.
 @end itemize
 
 Portability problems not fixed by Gnulib:
diff --git a/lib/rand.c b/lib/rand.c
new file mode 100644
index 0000000000..86f76cb422
--- /dev/null
+++ b/lib/rand.c
@@ -0,0 +1,30 @@
+/* Copyright (C) 1991-2023 Free Software Foundation, Inc.
+
+   This file is free software: you can redistribute it and/or modify
+   it under the terms of the GNU Lesser General Public License as
+   published by the Free Software Foundation, either version 3 of the
+   License, or (at your option) any later version.
+
+   This file 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 Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <stdlib.h>
+
+#ifndef _LIBC
+# define __random random
+#endif
+
+/* Return a random integer between 0 and RAND_MAX.  */
+int
+rand (void)
+{
+  return (int) __random ();
+}
diff --git a/m4/rand.m4 b/m4/rand.m4
new file mode 100644
index 0000000000..50b7fa3333
--- /dev/null
+++ b/m4/rand.m4
@@ -0,0 +1,17 @@
+# rand.m4 serial 1
+dnl Copyright (C) 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,
+dnl with or without modifications, as long as this notice is preserved.
+
+AC_DEFUN([gl_FUNC_RAND],
+[
+  AC_REQUIRE([gl_STDLIB_H_DEFAULTS])
+  AC_REQUIRE([AC_CANONICAL_HOST])
+
+  dnl On CheriBSD, rand() lacks locking, leading to an out-of-bounds read
+  dnl inside random_r.
+  case "$host" in
+    aarch64c-*-freebsd*) REPLACE_RAND=1 ;;
+  esac
+])
diff --git a/modules/rand b/modules/rand
new file mode 100644
index 0000000000..7cd6ca71c6
--- /dev/null
+++ b/modules/rand
@@ -0,0 +1,29 @@
+Description:
+global random number generator
+
+Files:
+lib/rand.c
+m4/rand.m4
+
+Depends-on:
+stdlib
+random          [test $REPLACE_RAND = 1]
+
+configure.ac:
+gl_FUNC_RAND
+gl_CONDITIONAL([GL_COND_OBJ_RAND], [test $REPLACE_RAND = 1])
+gl_STDLIB_MODULE_INDICATOR([rand])
+
+Makefile.am:
+if GL_COND_OBJ_RAND
+lib_SOURCES += rand.c
+endif
+
+Include:
+<stdlib.h>
+
+License:
+LGPL
+
+Maintainer:
+glibc
-- 
2.34.1

>From 23f0e4f7cdc5a03f44d7b204b7c78d51edc67eb7 Mon Sep 17 00:00:00 2001
From: Bruno Haible <br...@clisp.org>
Date: Thu, 9 Nov 2023 16:01:46 +0100
Subject: [PATCH 3/3] rand: Add tests.

* tests/test-rand.c: New file.
* modules/rand-tests: New file.
---
 ChangeLog          |  4 ++++
 modules/rand-tests | 11 +++++++++++
 tests/test-rand.c  | 33 +++++++++++++++++++++++++++++++++
 3 files changed, 48 insertions(+)
 create mode 100644 modules/rand-tests
 create mode 100644 tests/test-rand.c

diff --git a/ChangeLog b/ChangeLog
index a6e6a8045a..0c49471d04 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,9 @@
 2023-11-09  Bruno Haible  <br...@clisp.org>
 
+	rand: Add tests.
+	* tests/test-rand.c: New file.
+	* modules/rand-tests: New file.
+
 	rand: New module.
 	* lib/rand.c: New file, based on glibc/stdlib/rand.c.
 	* m4/rand.m4: New file.
diff --git a/modules/rand-tests b/modules/rand-tests
new file mode 100644
index 0000000000..0a2cb7e714
--- /dev/null
+++ b/modules/rand-tests
@@ -0,0 +1,11 @@
+Files:
+tests/test-rand.c
+tests/signature.h
+
+Depends-on:
+
+configure.ac:
+
+Makefile.am:
+TESTS += test-rand
+check_PROGRAMS += test-rand
diff --git a/tests/test-rand.c b/tests/test-rand.c
new file mode 100644
index 0000000000..eae68be611
--- /dev/null
+++ b/tests/test-rand.c
@@ -0,0 +1,33 @@
+/* Test rand.
+   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/>.  */
+
+#include <config.h>
+
+/* Specification.  */
+#include <stdlib.h>
+
+#include "signature.h"
+SIGNATURE_CHECK (rand, int, (void));
+
+int
+main ()
+{
+  rand ();
+  rand ();
+  rand ();
+
+  return 0;
+}
-- 
2.34.1

Reply via email to