From 9a3e86b54cd381af2670989c825a1ec72bd37d4b Mon Sep 17 00:00:00 2001 From: Martijn Dekker Date: Sun, 21 Jan 2024 18:11:58 +0000 Subject: [PATCH] Fix undefined behaviour (re: c77999b6, 23b7a163) The previous commit exposed undefined behaviour elsewhere that is the result of old ksh bugs. My 2021 attempt to fix it in 23b7a163 was also wrong. src/cmd/ksh93/sh/init.c: put_rand(), put_lineno(), put_lastarg(): - An incorrect typecast to double instead of Sfdouble_t (which translates to 'long double' on most systems, but not on macOS M1) caused arithmetic assignments to RANDOM, LINENO and _ to produce undefined results where sizeof(long double)!=sizeof(double). As ksh has been using Sfdouble_t for all arithmetic operations for years, fix the bug by typecasting to Sfdouble_t instead. src/cmd/ksh93/tests/arith.sh: - Fix the tests added in the previous commit to tolerate system-dependent rounding errors. --- NEWS | 8 +++++--- src/cmd/ksh93/sh/init.c | 8 ++++---- src/cmd/ksh93/tests/arith.sh | 20 ++++++++++++++++---- 3 files changed, 25 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index a580d772bc5e..1b7445094073 100644 --- a/NEWS +++ b/NEWS @@ -8,9 +8,11 @@ Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library. functions of a variable to be triggered when performing an assignment in an arithmetic expression; only the 'set' discipline is now triggered when assigning (as was always the documented behaviour). This applies to the - shell's internal disciplines as well. For example, seeding the $RANDOM - generator via an arithmetic expression assignment now no longer takes the - first value away from the reproducible pseudorandom sequence. + shell's internal disciplines as well. For example, $RANDOM is no longer + spuriously read from while seeding it via an arithmetic expression. + +- Fixed: assigning to LINENO, RANDOM, or _ via an arithmetic expression gave + undefined results on systems where sizeof(long double)!=sizeof(double). 2024-01-16: diff --git a/src/cmd/ksh93/sh/init.c b/src/cmd/ksh93/sh/init.c index de82c67460d1..749cf22f386f 100644 --- a/src/cmd/ksh93/sh/init.c +++ b/src/cmd/ksh93/sh/init.c @@ -672,7 +672,7 @@ static Sfdouble_t nget_seconds(Namval_t* np, Namfun_t *fp) static void put_rand(Namval_t* np,const char *val,int flags,Namfun_t *fp) { struct rand *rp = (struct rand*)fp; - long n; + Sfdouble_t n; sh_save_rand_seed(rp, 0); if(!val) { @@ -683,7 +683,7 @@ static void put_rand(Namval_t* np,const char *val,int flags,Namfun_t *fp) return; } if(flags&NV_INTEGER) - n = *(double*)val; + n = *(Sfdouble_t*)val; else n = sh_arith(val); srand(rp->rand_seed = (unsigned int)n); @@ -752,7 +752,7 @@ static void put_lineno(Namval_t* np,const char *val,int flags,Namfun_t *fp) return; } if(flags&NV_INTEGER) - n = (Sfdouble_t)(*(double*)val); + n = *(Sfdouble_t*)val; else n = sh_arith(val); sh.st.firstline += (int)(nget_lineno(np,fp) + 1 - n); @@ -777,7 +777,7 @@ static void put_lastarg(Namval_t* np,const char *val,int flags,Namfun_t *fp) { if(flags&NV_INTEGER) { - sfprintf(sh.strbuf,"%.*g",12,*((double*)val)); + sfprintf(sh.strbuf,"%.*Lg",12,*((Sfdouble_t*)val)); val = sfstruse(sh.strbuf); } if(val) diff --git a/src/cmd/ksh93/tests/arith.sh b/src/cmd/ksh93/tests/arith.sh index de47f55200bc..650ff9d9429e 100755 --- a/src/cmd/ksh93/tests/arith.sh +++ b/src/cmd/ksh93/tests/arith.sh @@ -1000,9 +1000,10 @@ got=$(PATH=/dev/null; typeset -i z; redirect 2>&1; z='add(2 , 3)'; echo $z) # value should still be cast to the type of the variable that is assigned to float x -x.getn() { .sh.value=999.99; } -let "(got = x = 1234.56) == 1234.56" || err_exit "arithmetic assignment triggers getn discipline (got $got)" -let "(got = x) == 999.99" || err_exit "arithmetic comparison fails to trigger getn discipline (got $got)" +x.getn() { .sh.value=987.65; } +let "got = x = 1234.56" +[[ $got == 1234.56* ]] || err_exit "arithmetic assignment triggers getn discipline (got $got)" +[[ $x == 987.65* ]] || err_exit "arithmetic comparison fails to trigger getn discipline (got $x)" unset x whence -q x.getn && err_exit "unset x fails to unset -f x.getn" @@ -1015,7 +1016,8 @@ whence -q x.getn && err_exit "unset x fails to unset -f x.getn" then err_exit "arithmetic assignment does not return properly typecast value (-${sz}i, got $got)" fi typeset -${sz}F x=0 - if ! let "(got = x = 123.95) == 123.95" + let "got = x = 123.95" + if [[ $got != 123.95* ]] # ignore OS-dependent rounding error then err_exit "arithmetic assignment does not return properly typecast value (-${sz}F, got $got)" fi done @@ -1029,5 +1031,15 @@ if ! let "(got = RANDOM = 123.95) == 123" then err_exit "arithmetic assignment to RANDOM does not return typecast of assigned value (got $got)" fi +let "_ = 123.95, got = _" +if [[ $got != '123.95' ]] +then err_exit "arithmetic assignment to _ fails (got $got)" +fi + +got=$(let "LINENO = 123"; print $LINENO ) +if [[ $got != '122' ]] # TODO: should be 123 +then err_exit "arithmetic assignment to LINENO fails (got $got)" +fi + # ====== exit $((Errors<125?Errors:125))