Tcl Source Code

Artifact [52daca28da]
Login

Artifact 52daca28dae710ba283788e26b3acbb08ede89ec646985cee8b4bb0ddf162221:

Attachment "fix-ubsan-shift-exponent.diff" to ticket [cea2c63928] added by chrstphrchvz 2021-12-06 08:18:43. (unpublished)
diff --git generic/tclStrToD.c generic/tclStrToD.c
index 35848f7d8..9a62eedcb 100644
--- generic/tclStrToD.c
+++ generic/tclStrToD.c
@@ -694,9 +694,9 @@ TclParseNumber(
 
 		    if (!octalSignificandOverflow) {
 			/*
-			 * Shifting by more bits than are in the value being
-			 * shifted is at least de facto nonportable. Check for
-			 * too large shifts first.
+			 * Shifting by as many or more bits than are in the
+			 * value being shifted is undefined behavior. Check
+			 * for too large shifts first.
 			 */
 
 			if ((octalSignificandWide != 0)
@@ -710,8 +710,17 @@ TclParseNumber(
 			}
 		    }
 		    if (!octalSignificandOverflow) {
-			octalSignificandWide =
-				(octalSignificandWide << shift) + (c - '0');
+			/*
+			 * When the significand is 0, it is possible for the
+			 * amount to be shifted to equal or exceed the width
+			 * of the significand. Do not shift when the
+			 * significand is 0 to avoid undefined behavior.
+			 */
+
+			if (0 != octalSignificandWide) {
+			    octalSignificandWide <<= shift;
+			}
+			octalSignificandWide += c - '0';
 		    } else {
 			mp_mul_2d(&octalSignificandBig, shift,
 				&octalSignificandBig);
@@ -813,9 +822,9 @@ TclParseNumber(
 		shift = 4 * (numTrailZeros + 1);
 		if (!significandOverflow) {
 		    /*
-		     * Shifting by more bits than are in the value being
-		     * shifted is at least de facto nonportable. Check for too
-		     * large shifts first.
+		     * Shifting by as many or more bits than are in the
+		     * value being shifted is undefined behavior. Check
+		     * for too large shifts first.
 		     */
 
 		    if (significandWide != 0 &&
@@ -827,7 +836,17 @@ TclParseNumber(
 		    }
 		}
 		if (!significandOverflow) {
-		    significandWide = (significandWide << shift) + d;
+		    /*
+		     * When the significand is 0, it is possible for the
+		     * amount to be shifted to equal or exceed the width
+		     * of the significand. Do not shift when the
+		     * significand is 0 to avoid undefined behavior.
+		     */
+
+		    if (0 != significandWide) {
+			significandWide <<= shift;
+		    }
+		    significandWide += d;
 		} else {
 		    mp_mul_2d(&significandBig, shift, &significandBig);
 		    mp_add_d(&significandBig, (mp_digit) d, &significandBig);
@@ -855,9 +874,9 @@ TclParseNumber(
 		shift = numTrailZeros + 1;
 		if (!significandOverflow) {
 		    /*
-		     * Shifting by more bits than are in the value being
-		     * shifted is at least de facto nonportable. Check for too
-		     * large shifts first.
+		     * Shifting by as many or more bits than are in the
+		     * value being shifted is undefined behavior. Check
+		     * for too large shifts first.
 		     */
 
 		    if (significandWide != 0 &&
@@ -869,7 +888,17 @@ TclParseNumber(
 		    }
 		}
 		if (!significandOverflow) {
-		    significandWide = (significandWide << shift) + 1;
+		    /*
+		     * When the significand is 0, it is possible for the
+		     * amount to be shifted to equal or exceed the width
+		     * of the significand. Do not shift when the
+		     * significand is 0 to avoid undefined behavior.
+		     */
+
+		    if (0 != significandWide) {
+			significandWide <<= shift;
+		    }
+		    significandWide += 1;
 		} else {
 		    mp_mul_2d(&significandBig, shift, &significandBig);
 		    mp_add_d(&significandBig, (mp_digit) 1, &significandBig);
@@ -1202,7 +1231,15 @@ TclParseNumber(
 	    }
 	    if (shift) {
 		if (!significandOverflow) {
-		    significandWide <<= shift;
+		    /*
+		     * When the significand is 0, it is possible for the
+		     * amount to be shifted to equal or exceed the width
+		     * of the significand. Do not shift when the
+		     * significand is 0 to avoid undefined behavior.
+		     */
+		    if (0 != significandWide) {
+			significandWide <<= shift;
+		    }
 		} else {
 		    mp_mul_2d(&significandBig, shift, &significandBig);
 		}
@@ -1223,7 +1260,15 @@ TclParseNumber(
 	    }
 	    if (shift) {
 		if (!significandOverflow) {
-		    significandWide <<= shift;
+		    /*
+		     * When the significand is 0, it is possible for the
+		     * amount to be shifted to equal or exceed the width
+		     * of the significand. Do not shift when the
+		     * significand is 0 to avoid undefined behavior.
+		     */
+		    if (0 != significandWide) {
+			significandWide <<= shift;
+		    }
 		} else {
 		    mp_mul_2d(&significandBig, shift, &significandBig);
 		}
@@ -1245,7 +1290,15 @@ TclParseNumber(
 	    }
 	    if (shift) {
 		if (!octalSignificandOverflow) {
-		    octalSignificandWide <<= shift;
+		    /*
+		     * When the significand is 0, it is possible for the
+		     * amount to be shifted to equal or exceed the width
+		     * of the significand. Do not shift when the
+		     * significand is 0 to avoid undefined behavior.
+		     */
+		    if (0 != octalSignificandWide) {
+			octalSignificandWide <<= shift;
+		    }
 		} else {
 		    mp_mul_2d(&octalSignificandBig, shift,
 			    &octalSignificandBig);