Skip to content

Commit

Permalink
[v1.1] Replace usage of rand_r with nrand48 (#817)
Browse files Browse the repository at this point in the history
This replaces the call to rand_r with nrand48 and changes rand_seed
to be an array instead of an unsigned int.

With rand_r being deprecated in POSIX 2024, the best replacement is
nrand48, which stores its intermediate values as a 48-bit integer
in an array of shorts instead of an unsigned int and returns a
32-bit value not constrained by RAND_MAX.

nrand48 has been in the POSIX spec. since 2004 and on the large
UNIX systems since SVr4. Only things that could be affected are
BSDs older than ~2004, but those are presumably old enough that we
can ignore them.

bin/package test returned with all tests passing (particularly
tests/subshell.sh and tests/variables.sh) and monkey testing shows
getting and setting RANDOM works.
  • Loading branch information
dnewhall authored and McDutchie committed Jan 15, 2025
1 parent 84f6139 commit 23a6ba0
Show file tree
Hide file tree
Showing 6 changed files with 28 additions and 45 deletions.
5 changes: 5 additions & 0 deletions ANNOUNCE
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,8 @@ New features in shell variables:

- TIMEFORMAT now supports a precision of up to six places after the decimal
point. The default precision for format directives remains 3.

- The $RANDOM pseudorandom numbers are now generated by nrand48(3) instead
of rand_r(3). Since POSIX specifies a particular pseudorandom generator
for nrand48, this means the same pseudorandom sequence should now be
generated on all platforms if RANDOM is assigned the same seed value.
7 changes: 7 additions & 0 deletions NEWS
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ This documents significant changes in the dev branch of ksh 93u+m.
For full details, see the git log at: https://github.com/ksh93/ksh
Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library.

2025-01-15:

- [v1.1] The $RANDOM pseudorandom numbers are now generated by nrand48(3)
instead of rand_r(3). Since POSIX specifies a particular pseudorandom
generator for nrand48, this means the same pseudorandom sequence should now
be generated on all platforms if RANDOM is assigned the same seed value.

2025-01-08:

- Fixed buggy emacs mode behaviour on exceeding the maximum line length.
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/ksh93/include/variables.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
struct rand
{
Namfun_t hdr;
unsigned int rand_seed;
unsigned short rand_seed[3];
int32_t rand_last;
};
extern void sh_reseed_rand(struct rand *);
Expand Down
2 changes: 1 addition & 1 deletion src/cmd/ksh93/include/version.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#include <ast_release.h>
#include "git.h"

#define SH_RELEASE_DATE "2025-01-08" /* must be in this format for $((.sh.version)) */
#define SH_RELEASE_DATE "2025-01-15" /* must be in this format for $((.sh.version)) */
/*
* This comment keeps SH_RELEASE_DATE a few lines away from SH_RELEASE_SVER to avoid
* merge conflicts when cherry-picking dev branch commits onto a release branch.
Expand Down
51 changes: 11 additions & 40 deletions src/cmd/ksh93/sh/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -623,38 +623,15 @@ static Sfdouble_t nget_seconds(Namval_t* np, Namfun_t *fp)
return dtime(&tp) - offset;
}

#if !_lib_rand_r
#undef RAND_MAX
#define RAND_MAX 0x7fffffff
#define rand_r _ksh_rand_r
/*
* rand_r(3) fallback nicked from FreeBSD libc.
* License: BSD 3-clause. See the COPYRIGHT file.
* Not to be confused with actual randomness!
*
* Compute x = (7^5 * x) mod (2^31 - 1)
* without overflowing 31 bits:
* (2^31 - 1) = 127773 * (7^5) + 2836
* From "Random number generators: good ones are hard to find",
* Park and Miller, Communications of the ACM, vol. 31, no. 10,
* October 1988, p. 1195.
* Seeds the rand stucture using the same algorithm as srand48()
*/
static int rand_r(unsigned int *seed)
{
long hi, lo, x;
/* Transform to [1, 0x7ffffffe] range. */
x = (*seed % 0x7ffffffe) + 1;
hi = x / 127773;
lo = x % 127773;
x = 16807 * lo - 2836 * hi;
if (x < 0)
x += 0x7fffffff;
/* Transform to [0, 0x7ffffffd] range. */
x--;
*seed = x;
return x;
}
#endif /* !_lib_rand_r */
static void seed_rand_uint(struct rand *rp, unsigned int seed)
{
rp->rand_seed[0] = 0x330e; /* Constant from POSIX spec. */
rp->rand_seed[1] = (unsigned short)seed;
rp->rand_seed[2] = (unsigned short)(seed >> 16);
}

/*
* These four functions are used to get and set the RANDOM variable
Expand All @@ -676,7 +653,7 @@ static void put_rand(Namval_t* np,const char *val,int flags,Namfun_t *fp)
n = *(Sfdouble_t*)val;
else
n = sh_arith(val);
rp->rand_seed = (unsigned int)n;
seed_rand_uint(rp, (unsigned int)n);
rp->rand_last = -1;
if(!np->nvalue)
np->nvalue = &rp->rand_last;
Expand All @@ -694,14 +671,8 @@ static Sfdouble_t nget_rand(Namval_t* np, Namfun_t *fp)
int32_t last = *lp;
sh_save_rand_seed(rp, 1);
do
#if RAND_MAX > (RANDMASK << 3)
/* don't use lower bits when rand_r() generates large numbers */
cur = (rand_r(&rp->rand_seed) >> 3) & RANDMASK;
#elif RAND_MAX > RANDMASK
cur = rand_r(&rp->rand_seed) & RANDMASK;
#else
cur = rand_r(&rp->rand_seed);
#endif
/* don't use lower bits when generating large numbers */
cur = (nrand48(rp->rand_seed) >> 3) & RANDMASK;
while(cur==last);
*lp = cur;
return (Sfdouble_t)cur;
Expand All @@ -715,7 +686,7 @@ static char* get_rand(Namval_t* np, Namfun_t *fp)

void sh_reseed_rand(struct rand *rp)
{
rp->rand_seed = arc4random();
seed_rand_uint(rp, arc4random());
rp->rand_last = -1;
}

Expand Down
6 changes: 3 additions & 3 deletions src/cmd/ksh93/sh/subshell.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ static struct subshell
int cpipe;
char subshare;
char comsub;
unsigned int rand_seed; /* parent shell $RANDOM seed */
unsigned short rand_seed[3]; /* parent shell $RANDOM seed */
int rand_last; /* last random number from $RANDOM in parent shell */
char rand_state; /* 0 means sp->rand_seed hasn't been set, 1 is the opposite */
uint32_t srand_upper_bound; /* parent shell's upper bound for $SRANDOM */
Expand Down Expand Up @@ -226,7 +226,7 @@ void sh_save_rand_seed(struct rand *rp, int reseed)
struct subshell *sp = subshell_data;
if(!sh.subshare && sp && !sp->rand_state)
{
sp->rand_seed = rp->rand_seed;
memcpy(sp->rand_seed, rp->rand_seed, sizeof sp->rand_seed);
sp->rand_last = rp->rand_last;
sp->rand_state = 1;
if(reseed)
Expand Down Expand Up @@ -886,7 +886,7 @@ Sfio_t *sh_subshell(Shnode_t *t, volatile int flags, int comsub)
rp = (struct rand*)RANDNOD->nvfun;
if(sp->rand_state)
{
rp->rand_seed = sp->rand_seed;
memcpy(rp->rand_seed, sp->rand_seed, sizeof rp->rand_seed);
rp->rand_last = sp->rand_last;
}
/* restore $SRANDOM upper bound */
Expand Down

0 comments on commit 23a6ba0

Please sign in to comment.