Submitted By: Robert Connolly <robert at linuxfromscratch dot org> (ashes)
Date: 2007-06-11
Initial Package Version: 3.2
Upstream Status: Submitted - http://www.mail-archive.com/bug-bash%40gnu.org/msg02718.html
Origin: The idea came from Debian's Bash random.dpatch. See Note below.
Description: This patch uses ./configure to search for arc4random()
and use them with the "$RANDOM" Bash variable if found.

*** Note ***
The Bash manual page states:
RANDOM Each time this parameter is referenced, a random integer between
       0 and 32767 is generated.  The sequence of random numbers may be
       initialized by assigning a value to RANDOM.  If RANDOM is unset,
       it  loses  its  special  properties,  even if it is subsequently
       reset.

Coincidentally the Korn shell has the exact same behavior, but this is
not defined by Posix standards.

In all cases `echo $RANDOM` will change each time, if 'RANDOM' is not
initialized. The vanilla Bash uses OLD_RANDOM+getpid()+gettimeofday().
This patch uses arc4random() when 'RANDOM' is not initialized, which
makes the returned value much more difficult to predict.

Vanilla Bash, and the Bash using this patch:

$ RANDOM=1
$ echo $RANDOM
16838
$ RANDOM=1
$ echo $RANDOM
16838
$ RANDOM=1
$ echo $RANDOM
16838
$ RANDOM=2
$ echo $RANDOM
908
$ RANDOM=3
$ echo $RANDOM
17747
$ RANDOM=2
$ echo $RANDOM
908

This is the documented and expected behavior of the shell. Any Bash,
assuming the versions are the same, should produce "17747" if 'RANDOM=3',
and so on.

From the Debian random patch:

$ RANDOM=1
$ echo $RANDOM
17767
$ RANDOM=1
$ echo $RANDOM
9158
$ RANDOM=1
$ echo $RANDOM
17767
$ RANDOM=1
$ echo $RANDOM
9158

This breaks the expected behavior.

diff -Naur bash-3.2.orig/config.h.in bash-3.2/config.h.in
--- bash-3.2.orig/config.h.in	2006-09-12 20:00:54.000000000 +0000
+++ bash-3.2/config.h.in	2007-04-26 04:49:04.000000000 +0000
@@ -496,6 +496,9 @@
 
 /* Presence of system and C library functions. */
 
+/* Define if you have the arc4random function.  */
+#undef HAVE_ARC4RANDOM
+
 /* Define if you have the asprintf function.  */
 #undef HAVE_ASPRINTF
 
diff -Naur bash-3.2.orig/configure bash-3.2/configure
--- bash-3.2.orig/configure	2006-09-26 15:06:01.000000000 +0000
+++ bash-3.2/configure	2007-04-26 04:53:44.000000000 +0000
@@ -14542,6 +14542,109 @@
 
 
 
+for ac_func in arc4random
+do
+as_ac_var=`echo "ac_cv_func_$ac_func" | $as_tr_sh`
+echo "$as_me:$LINENO: checking for $ac_func" >&5
+echo $ECHO_N "checking for $ac_func... $ECHO_C" >&6
+if eval "test \"\${$as_ac_var+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+/* Define $ac_func to an innocuous variant, in case <limits.h> declares $ac_func.
+   For example, HP-UX 11i <limits.h> declares gettimeofday.  */
+#define $ac_func innocuous_$ac_func
+
+/* System header to define __stub macros and hopefully few prototypes,
+    which can conflict with char $ac_func (); below.
+    Prefer <limits.h> to <assert.h> if __STDC__ is defined, since
+    <limits.h> exists even on freestanding compilers.  */
+
+#ifdef __STDC__
+# include <limits.h>
+#else
+# include <assert.h>
+#endif
+
+#undef $ac_func
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char $ac_func ();
+/* The GNU C library defines this for functions which it implements
+    to always fail with ENOSYS.  Some functions are actually named
+    something starting with __ and the normal name is an alias.  */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+char (*f) () = $ac_func;
+#endif
+#ifdef __cplusplus
+}
+#endif
+
+int
+main ()
+{
+return f != $ac_func;
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  eval "$as_ac_var=yes"
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+eval "$as_ac_var=no"
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_var'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_var'}'`" >&6
+if test `eval echo '${'$as_ac_var'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_func" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+done
+
+
+
 
 
 
diff -Naur bash-3.2.orig/configure.in bash-3.2/configure.in
--- bash-3.2.orig/configure.in	2006-09-26 15:05:45.000000000 +0000
+++ bash-3.2/configure.in	2007-04-26 04:49:26.000000000 +0000
@@ -748,6 +748,9 @@
 
 AC_FUNC_MKTIME
 
+dnl Checks for the arc4random function
+AC_CHECK_FUNCS(arc4random)
+
 dnl
 dnl Checks for lib/intl and related code (uses some of the output from
 dnl AM_GNU_GETTEXT)
diff -Naur bash-3.2.orig/variables.c bash-3.2/variables.c
--- bash-3.2.orig/variables.c	2006-09-08 17:33:32.000000000 +0000
+++ bash-3.2/variables.c	2007-04-26 04:53:27.000000000 +0000
@@ -1188,7 +1188,11 @@
   /* Reset for command and process substitution. */
   if (subshell_environment && seeded_subshell == 0)
     {
+#if defined(HAVE_ARC4RANDOM)
+      sbrand (arc4random());
+#else
       sbrand (rseed + getpid() + NOW);
+#endif
       seeded_subshell = 1;
     }
 
