Skip to content

Commit 3f2d60c

Browse files
authored
Fix #14885 Shift by negative value in TemplateSimplifier::simplifyNumericalCalculations() (#8639)
the value shift operators only reject a count >= bigint_bits, so MathLib::value::shiftLeft still left-shifts a negative value and both shiftLeft and shiftRight still shift by a negative count, which is undefined behaviour. it is reachable when folding a template argument like 0x8000000000000000 << 1 in simplifyNumericCalculations, since MathLib::isNegative only checks for a leading minus while a large hex literal parses to a negative bigint and the guard there lets it through. mirror the negative-operand check calculate.h already uses and return the operand unchanged, as is already done for oversized counts. ubsan flags the left shift at mathlib.cpp:272 on that input.
1 parent ac061da commit 3f2d60c

3 files changed

Lines changed: 20 additions & 2 deletions

File tree

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,7 @@ Samuel Degrande
369369
Samuel Poláček
370370
Sandeep Dutta
371371
Savvas Etairidis
372+
Sayed Kaif
372373
Scott Ehlert
373374
Scott Furry
374375
Seafarix Ltd.

lib/mathlib.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,7 @@ MathLib::value MathLib::value::shiftLeft(const MathLib::value &v) const
266266
if (!isInt() || !v.isInt())
267267
throw InternalError(nullptr, "Shift operand is not integer");
268268
MathLib::value ret(*this);
269-
if (v.mIntValue >= MathLib::bigint_bits) {
269+
if (v.mIntValue < 0 || v.mIntValue >= MathLib::bigint_bits || ret.mIntValue < 0) {
270270
return ret;
271271
}
272272
ret.mIntValue <<= v.mIntValue;
@@ -278,7 +278,7 @@ MathLib::value MathLib::value::shiftRight(const MathLib::value &v) const
278278
if (!isInt() || !v.isInt())
279279
throw InternalError(nullptr, "Shift operand is not integer");
280280
MathLib::value ret(*this);
281-
if (v.mIntValue >= MathLib::bigint_bits) {
281+
if (v.mIntValue < 0 || v.mIntValue >= MathLib::bigint_bits) {
282282
return ret;
283283
}
284284
ret.mIntValue >>= v.mIntValue;

test/testsimplifytemplate.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,6 +320,8 @@ class TestSimplifyTemplate : public TestFixture {
320320

321321
TEST_CASE(templateArgPreserveType); // #13882 - type of template argument
322322

323+
TEST_CASE(template_shift_negative); // shift folding with a negative operand
324+
323325
TEST_CASE(dumpTemplateArgFrom);
324326
}
325327

@@ -6715,6 +6717,21 @@ class TestSimplifyTemplate : public TestFixture {
67156717
tok(code));
67166718
}
67176719

6720+
void template_shift_negative() {
6721+
// a large hex literal is not negative as a string but parses to a negative
6722+
// bigint, so folding the shift in simplifyNumericCalculations would left-shift
6723+
// a negative value / shift by a negative count, both UB. the operand must be
6724+
// returned unchanged. parentheses are needed so the numeric folding is reached.
6725+
const char code[] = "template <long long> struct S { };\n"
6726+
"S<(0x8000000000000000 << 1)> s1;\n"
6727+
"S<(1 << 0x8000000000000000)> s2;\n"
6728+
"S<(1 >> 0x8000000000000000)> s3;";
6729+
const char expected[] = "struct S<9223372036854775808U> ; struct S<1> ; "
6730+
"S<9223372036854775808U> s1 ; S<1> s2 ; S<1> s3 ; "
6731+
"struct S<9223372036854775808U> { } ; struct S<1> { } ;";
6732+
ASSERT_EQUALS(expected, tok(code));
6733+
}
6734+
67186735
void dumpTemplateArgFrom() {
67196736
const char code[] = "template<class T> void foo(T t) {}\n"
67206737
"foo<int>(23);";

0 commit comments

Comments
 (0)