summaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorjilles <jilles@FreeBSD.org>2014-08-15 22:36:41 +0000
committerjilles <jilles@FreeBSD.org>2014-08-15 22:36:41 +0000
commit948728c4a63d15bb4bc6fc777177b823be4ade7d (patch)
treebcbd242911551a39177c9ddee6a87c42fcc0aad9 /bin
parent82e14020fd65682e9c4e04af88589597b3609efa (diff)
downloadFreeBSD-src-948728c4a63d15bb4bc6fc777177b823be4ade7d.zip
FreeBSD-src-948728c4a63d15bb4bc6fc777177b823be4ade7d.tar.gz
sh: Mask off shift distance (<< and >>) in arithmetic.
In C, shift distances equal to or larger than the number of bits in the operand result in undefined behaviour. As part of eliminating undefined behaviour in arithmetic, mask off the distance like Java and JavaScript specify and C on x86 usually does. Assumption: conversion from unsigned to signed retains the two's complement bits. Assumption: uintmax_t has no padding bits.
Diffstat (limited to 'bin')
-rw-r--r--bin/sh/arith_yacc.c5
-rw-r--r--bin/sh/tests/expansion/Makefile1
-rw-r--r--bin/sh/tests/expansion/arith14.040
3 files changed, 44 insertions, 2 deletions
diff --git a/bin/sh/arith_yacc.c b/bin/sh/arith_yacc.c
index 815d885..46df847 100644
--- a/bin/sh/arith_yacc.c
+++ b/bin/sh/arith_yacc.c
@@ -139,9 +139,10 @@ static arith_t do_binop(int op, arith_t a, arith_t b)
case ARITH_SUB:
return (uintmax_t)a - (uintmax_t)b;
case ARITH_LSHIFT:
- return (uintmax_t)a << b;
+ return (uintmax_t)a <<
+ ((uintmax_t)b & (sizeof(uintmax_t) * CHAR_BIT - 1));
case ARITH_RSHIFT:
- return a >> b;
+ return a >> ((uintmax_t)b & (sizeof(uintmax_t) * CHAR_BIT - 1));
case ARITH_LT:
return a < b;
case ARITH_LE:
diff --git a/bin/sh/tests/expansion/Makefile b/bin/sh/tests/expansion/Makefile
index 8ded7e1..36b5fa5 100644
--- a/bin/sh/tests/expansion/Makefile
+++ b/bin/sh/tests/expansion/Makefile
@@ -20,6 +20,7 @@ FILES+= arith10.0
FILES+= arith11.0
FILES+= arith12.0
FILES+= arith13.0
+FILES+= arith14.0
FILES+= assign1.0
FILES+= cmdsubst1.0
FILES+= cmdsubst2.0
diff --git a/bin/sh/tests/expansion/arith14.0 b/bin/sh/tests/expansion/arith14.0
new file mode 100644
index 0000000..8369043
--- /dev/null
+++ b/bin/sh/tests/expansion/arith14.0
@@ -0,0 +1,40 @@
+# $FreeBSD$
+# Check that <</>> use the low bits of the shift count.
+
+if [ $((1<<16<<16)) = 0 ]; then
+ width=32
+elif [ $((1<<32<<32)) = 0 ]; then
+ width=64
+elif [ $((1<<64<<64)) = 0 ]; then
+ width=128
+elif [ $((1<<64>>64)) = 1 ]; then
+ # Integers are wider than 128 bits; assume arbitrary precision.
+ # Nothing to test here.
+ exit 0
+else
+ echo "Cannot determine integer width"
+ exit 2
+fi
+
+twowidth=$((width * 2))
+j=43 k=$((1 << (width - 2))) r=0
+
+i=0
+while [ $i -lt $twowidth ]; do
+ if [ "$((j << i))" != "$((j << (i + width)))" ]; then
+ echo "Problem with $j << $i"
+ r=2
+ fi
+ i=$((i + 1))
+done
+
+i=0
+while [ $i -lt $twowidth ]; do
+ if [ "$((k >> i))" != "$((k >> (i + width)))" ]; then
+ echo "Problem with $k >> $i"
+ r=2
+ fi
+ i=$((i + 1))
+done
+
+exit $r
OpenPOWER on IntegriCloud