Skip to content

Commit 9a3e86b

Browse files
committed
Fix undefined behaviour (re: c77999b, 23b7a16)
The previous commit exposed undefined behaviour elsewhere that is the result of old ksh bugs. My 2021 attempt to fix it in 23b7a16 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.
1 parent c77999b commit 9a3e86b

File tree

3 files changed

+25
-11
lines changed

3 files changed

+25
-11
lines changed

NEWS

+5-3
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@ Uppercase BUG_* IDs are shell bug IDs as used by the Modernish shell library.
88
functions of a variable to be triggered when performing an assignment in
99
an arithmetic expression; only the 'set' discipline is now triggered when
1010
assigning (as was always the documented behaviour). This applies to the
11-
shell's internal disciplines as well. For example, seeding the $RANDOM
12-
generator via an arithmetic expression assignment now no longer takes the
13-
first value away from the reproducible pseudorandom sequence.
11+
shell's internal disciplines as well. For example, $RANDOM is no longer
12+
spuriously read from while seeding it via an arithmetic expression.
13+
14+
- Fixed: assigning to LINENO, RANDOM, or _ via an arithmetic expression gave
15+
undefined results on systems where sizeof(long double)!=sizeof(double).
1416

1517
2024-01-16:
1618

src/cmd/ksh93/sh/init.c

+4-4
Original file line numberDiff line numberDiff line change
@@ -672,7 +672,7 @@ static Sfdouble_t nget_seconds(Namval_t* np, Namfun_t *fp)
672672
static void put_rand(Namval_t* np,const char *val,int flags,Namfun_t *fp)
673673
{
674674
struct rand *rp = (struct rand*)fp;
675-
long n;
675+
Sfdouble_t n;
676676
sh_save_rand_seed(rp, 0);
677677
if(!val)
678678
{
@@ -683,7 +683,7 @@ static void put_rand(Namval_t* np,const char *val,int flags,Namfun_t *fp)
683683
return;
684684
}
685685
if(flags&NV_INTEGER)
686-
n = *(double*)val;
686+
n = *(Sfdouble_t*)val;
687687
else
688688
n = sh_arith(val);
689689
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)
752752
return;
753753
}
754754
if(flags&NV_INTEGER)
755-
n = (Sfdouble_t)(*(double*)val);
755+
n = *(Sfdouble_t*)val;
756756
else
757757
n = sh_arith(val);
758758
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)
777777
{
778778
if(flags&NV_INTEGER)
779779
{
780-
sfprintf(sh.strbuf,"%.*g",12,*((double*)val));
780+
sfprintf(sh.strbuf,"%.*Lg",12,*((Sfdouble_t*)val));
781781
val = sfstruse(sh.strbuf);
782782
}
783783
if(val)

src/cmd/ksh93/tests/arith.sh

+16-4
Original file line numberDiff line numberDiff line change
@@ -1000,9 +1000,10 @@ got=$(PATH=/dev/null; typeset -i z; redirect 2>&1; z='add(2 , 3)'; echo $z)
10001000
# value should still be cast to the type of the variable that is assigned to
10011001

10021002
float x
1003-
x.getn() { .sh.value=999.99; }
1004-
let "(got = x = 1234.56) == 1234.56" || err_exit "arithmetic assignment triggers getn discipline (got $got)"
1005-
let "(got = x) == 999.99" || err_exit "arithmetic comparison fails to trigger getn discipline (got $got)"
1003+
x.getn() { .sh.value=987.65; }
1004+
let "got = x = 1234.56"
1005+
[[ $got == 1234.56* ]] || err_exit "arithmetic assignment triggers getn discipline (got $got)"
1006+
[[ $x == 987.65* ]] || err_exit "arithmetic comparison fails to trigger getn discipline (got $x)"
10061007
unset x
10071008
whence -q x.getn && err_exit "unset x fails to unset -f x.getn"
10081009

@@ -1015,7 +1016,8 @@ whence -q x.getn && err_exit "unset x fails to unset -f x.getn"
10151016
then err_exit "arithmetic assignment does not return properly typecast value (-${sz}i, got $got)"
10161017
fi
10171018
typeset -${sz}F x=0
1018-
if ! let "(got = x = 123.95) == 123.95"
1019+
let "got = x = 123.95"
1020+
if [[ $got != 123.95* ]] # ignore OS-dependent rounding error
10191021
then err_exit "arithmetic assignment does not return properly typecast value (-${sz}F, got $got)"
10201022
fi
10211023
done
@@ -1029,5 +1031,15 @@ if ! let "(got = RANDOM = 123.95) == 123"
10291031
then err_exit "arithmetic assignment to RANDOM does not return typecast of assigned value (got $got)"
10301032
fi
10311033

1034+
let "_ = 123.95, got = _"
1035+
if [[ $got != '123.95' ]]
1036+
then err_exit "arithmetic assignment to _ fails (got $got)"
1037+
fi
1038+
1039+
got=$(let "LINENO = 123"; print $LINENO )
1040+
if [[ $got != '122' ]] # TODO: should be 123
1041+
then err_exit "arithmetic assignment to LINENO fails (got $got)"
1042+
fi
1043+
10321044
# ======
10331045
exit $((Errors<125?Errors:125))

0 commit comments

Comments
 (0)