From 5eadcbb448db9b657328adcf809416123301ac65 Mon Sep 17 00:00:00 2001 From: metsw24-max Date: Tue, 9 Jun 2026 10:14:57 +0530 Subject: [PATCH 1/2] guard negative operands in MathLib::value shift operators --- lib/mathlib.cpp | 4 ++-- test/testmathlib.cpp | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/mathlib.cpp b/lib/mathlib.cpp index 403662f4df2..c7c055899fd 100644 --- a/lib/mathlib.cpp +++ b/lib/mathlib.cpp @@ -266,7 +266,7 @@ MathLib::value MathLib::value::shiftLeft(const MathLib::value &v) const if (!isInt() || !v.isInt()) throw InternalError(nullptr, "Shift operand is not integer"); MathLib::value ret(*this); - if (v.mIntValue >= MathLib::bigint_bits) { + if (v.mIntValue < 0 || v.mIntValue >= MathLib::bigint_bits || ret.mIntValue < 0) { return ret; } ret.mIntValue <<= v.mIntValue; @@ -278,7 +278,7 @@ MathLib::value MathLib::value::shiftRight(const MathLib::value &v) const if (!isInt() || !v.isInt()) throw InternalError(nullptr, "Shift operand is not integer"); MathLib::value ret(*this); - if (v.mIntValue >= MathLib::bigint_bits) { + if (v.mIntValue < 0 || v.mIntValue >= MathLib::bigint_bits) { return ret; } ret.mIntValue >>= v.mIntValue; diff --git a/test/testmathlib.cpp b/test/testmathlib.cpp index 8cbe5b50019..4133d710e4f 100644 --- a/test/testmathlib.cpp +++ b/test/testmathlib.cpp @@ -64,6 +64,7 @@ class TestMathLib : public TestFixture { TEST_CASE(tan); TEST_CASE(abs); TEST_CASE(toString); + TEST_CASE(valueShift); } void isGreater() const { @@ -1511,6 +1512,20 @@ class TestMathLib : public TestFixture { ASSERT_EQUALS("2.22507385851e-308", MathLib::toString(std::numeric_limits::min())); ASSERT_EQUALS("1.79769313486e+308", MathLib::toString(std::numeric_limits::max())); } + + void valueShift() const { + // ordinary shifts + ASSERT_EQUALS("16", (MathLib::value("1") << MathLib::value("4")).str()); + ASSERT_EQUALS("64", (MathLib::value("256") >> MathLib::value("2")).str()); + + // a large hex literal is not negative as a string but parses to a negative + // bigint; shifting it must not be performed (left shift of a negative value + // and shifting by a negative count are both undefined). the operand is + // returned unchanged, as already done for counts >= bigint_bits. + ASSERT_EQUALS("9223372036854775808U", (MathLib::value("0x8000000000000000") << MathLib::value("1")).str()); + ASSERT_EQUALS("1", (MathLib::value("1") << MathLib::value("0x8000000000000000")).str()); + ASSERT_EQUALS("1", (MathLib::value("1") >> MathLib::value("0x8000000000000000")).str()); + } }; REGISTER_TEST(TestMathLib) From 21951e6dc7a041313a27654e5f091d730c2decff Mon Sep 17 00:00:00 2001 From: Sayed Kaif Date: Mon, 29 Jun 2026 11:51:35 +0530 Subject: [PATCH 2/2] test negative shift folding via template simplifier Move the regression test to testsimplifytemplate.cpp so it exercises the reachable path through simplifyNumericCalculations instead of calling the MathLib::value operators directly, which are not exported and broke the Windows/test link. --- test/testmathlib.cpp | 15 --------------- test/testsimplifytemplate.cpp | 17 +++++++++++++++++ 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/test/testmathlib.cpp b/test/testmathlib.cpp index 4133d710e4f..8cbe5b50019 100644 --- a/test/testmathlib.cpp +++ b/test/testmathlib.cpp @@ -64,7 +64,6 @@ class TestMathLib : public TestFixture { TEST_CASE(tan); TEST_CASE(abs); TEST_CASE(toString); - TEST_CASE(valueShift); } void isGreater() const { @@ -1512,20 +1511,6 @@ class TestMathLib : public TestFixture { ASSERT_EQUALS("2.22507385851e-308", MathLib::toString(std::numeric_limits::min())); ASSERT_EQUALS("1.79769313486e+308", MathLib::toString(std::numeric_limits::max())); } - - void valueShift() const { - // ordinary shifts - ASSERT_EQUALS("16", (MathLib::value("1") << MathLib::value("4")).str()); - ASSERT_EQUALS("64", (MathLib::value("256") >> MathLib::value("2")).str()); - - // a large hex literal is not negative as a string but parses to a negative - // bigint; shifting it must not be performed (left shift of a negative value - // and shifting by a negative count are both undefined). the operand is - // returned unchanged, as already done for counts >= bigint_bits. - ASSERT_EQUALS("9223372036854775808U", (MathLib::value("0x8000000000000000") << MathLib::value("1")).str()); - ASSERT_EQUALS("1", (MathLib::value("1") << MathLib::value("0x8000000000000000")).str()); - ASSERT_EQUALS("1", (MathLib::value("1") >> MathLib::value("0x8000000000000000")).str()); - } }; REGISTER_TEST(TestMathLib) diff --git a/test/testsimplifytemplate.cpp b/test/testsimplifytemplate.cpp index b28bc503869..6960b791426 100644 --- a/test/testsimplifytemplate.cpp +++ b/test/testsimplifytemplate.cpp @@ -320,6 +320,8 @@ class TestSimplifyTemplate : public TestFixture { TEST_CASE(templateArgPreserveType); // #13882 - type of template argument + TEST_CASE(template_shift_negative); // shift folding with a negative operand + TEST_CASE(dumpTemplateArgFrom); } @@ -6715,6 +6717,21 @@ class TestSimplifyTemplate : public TestFixture { tok(code)); } + void template_shift_negative() { + // a large hex literal is not negative as a string but parses to a negative + // bigint, so folding the shift in simplifyNumericCalculations would left-shift + // a negative value / shift by a negative count, both UB. the operand must be + // returned unchanged. parentheses are needed so the numeric folding is reached. + const char code[] = "template struct S { };\n" + "S<(0x8000000000000000 << 1)> s1;\n" + "S<(1 << 0x8000000000000000)> s2;\n" + "S<(1 >> 0x8000000000000000)> s3;"; + const char expected[] = "struct S<9223372036854775808U> ; struct S<1> ; " + "S<9223372036854775808U> s1 ; S<1> s2 ; S<1> s3 ; " + "struct S<9223372036854775808U> { } ; struct S<1> { } ;"; + ASSERT_EQUALS(expected, tok(code)); + } + void dumpTemplateArgFrom() { const char code[] = "template void foo(T t) {}\n" "foo(23);";