Skip to content

Commit

Permalink
Introduce save/restore for SRANDOM in virtual subshells
Browse files Browse the repository at this point in the history
This commit fixes a virtual subshell leak that caused the set upper
bound for SRANDOM to leak out into the parent shell. This is fixed by
saving and restoring the parent shell's upper bound for SRANDOM (the
upper bound variable has been moved to the sh struct for this purpose).
  • Loading branch information
JohnoKing committed Jan 23, 2024
1 parent acaac0a commit 5322b40
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/cmd/ksh93/include/shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ struct Shell_s
int savesig;
unsigned char *sigflag; /* pointer to signal states */
char intrap; /* set while executing a trap action */
uint32_t srand_upper_bound;
char forked;
char binscript;
char funload;
Expand Down
9 changes: 4 additions & 5 deletions src/cmd/ksh93/sh/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -725,7 +725,6 @@ void sh_reseed_rand(struct rand *rp)
/*
* The following three functions are for SRANDOM
*/
static uint32_t srand_upper_bound;

static void put_srand(Namval_t* np,const char *val,int flags,Namfun_t *fp)
{
Expand All @@ -740,19 +739,19 @@ static void put_srand(Namval_t* np,const char *val,int flags,Namfun_t *fp)
if(sh_isstate(SH_INIT))
return;
if(flags&NV_INTEGER)
srand_upper_bound = *(Sfdouble_t*)val;
sh.srand_upper_bound = *(Sfdouble_t*)val;
else
srand_upper_bound = sh_arith(val);
sh.srand_upper_bound = sh_arith(val);
}

static Sfdouble_t nget_srand(Namval_t* np, Namfun_t *fp)
{
return (Sfdouble_t)(srand_upper_bound ? arc4random_uniform(srand_upper_bound) : arc4random());
return (Sfdouble_t)(sh.srand_upper_bound ? arc4random_uniform(sh.srand_upper_bound) : arc4random());
}

static char* get_srand(Namval_t* np, Namfun_t *fp)
{
intmax_t n = (intmax_t)(srand_upper_bound ? arc4random_uniform(srand_upper_bound) : arc4random());
intmax_t n = (intmax_t)(sh.srand_upper_bound ? arc4random_uniform(sh.srand_upper_bound) : arc4random());
return fmtbase(n, 10, 0);
}

Expand Down
11 changes: 8 additions & 3 deletions src/cmd/ksh93/sh/subshell.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ static struct subshell
int cpipe;
char subshare;
char comsub;
unsigned int rand_seed; /* parent shell $RANDOM seed */
int rand_last; /* last random number from $RANDOM in parent shell */
int rand_state; /* 0 means sp->rand_seed hasn't been set, 1 is the opposite */
unsigned int rand_seed; /* parent shell $RANDOM seed */
int rand_last; /* last random number from $RANDOM in parent shell */
int 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 */
#if _lib_fchdir
int pwdfd; /* file descriptor for PWD */
char pwdclose;
Expand Down Expand Up @@ -585,6 +586,8 @@ Sfio_t *sh_subshell(Shnode_t *t, volatile int flags, int comsub)
sh_sigreset(0);
if(save_debugtrap)
sh.st.trap[SH_DEBUGTRAP] = save_debugtrap;
/* save upper bound for $SRANDOM */
sp->srand_upper_bound = sh.srand_upper_bound;
}
jmpval = sigsetjmp(checkpoint.buff,0);
if(jmpval==0)
Expand Down Expand Up @@ -857,6 +860,8 @@ Sfio_t *sh_subshell(Shnode_t *t, volatile int flags, int comsub)
srand(rp->rand_seed = sp->rand_seed);
rp->rand_last = sp->rand_last;
}
/* restore $SRANDOM upper bound */
sh.srand_upper_bound = sp->srand_upper_bound;
/* Real subshells have their exit status truncated to 8 bits by the kernel.
* Since virtual subshells should be indistinguishable, do the same here. */
sh.exitval &= SH_EXITMASK;
Expand Down
10 changes: 10 additions & 0 deletions src/cmd/ksh93/tests/variables.sh
Original file line number Diff line number Diff line change
Expand Up @@ -1647,6 +1647,16 @@ while read i
do ((got = i>=bound)) && break
done
((got)) || err_exit "SRANDOM upper bound inherited from environment"
# SRANDOM upper bound leaks out of virtual subshells
for i in 0 10000; do
(SRANDOM=$i)
for ((i=0; i<bound; i++))
do if let "got = SRANDOM, got >= bound"
then err_exit "SRANDOM upper bound leads out of virtual subshells ($got >= $bound)"
break
fi
done
done
unset i got bound
SRANDOM=0
Expand Down

0 comments on commit 5322b40

Please sign in to comment.