Skip to content

Commit 9a4cfe6

Browse files
authored
Fix #12282 (Internal error: fail to parse 1.0e4931L) (#8952)
Trac: https://trac.cppcheck.net/ticket/12282 A float literal out of the range of `double` stops the analysis of the whole file: Internal Error. MathLib::toDoubleNumber: conversion failed: 1.0e4931L `MathLib::toDoubleNumber()` reads the literal with an `istringstream`. For a value out of range the stream stores the largest finite value and sets `failbit`, which became the internal error. Compilers make such a literal infinity, so this does the same. Besides `long double` constants like the one in the ticket, a common trigger is glibc's `math.h` when the GCC builtins are not used: `HUGE_VAL`/`HUGE_VALF` are then `1e10000`/`1e10000f`, so any file using `INFINITY` or `HUGE_VAL` hit this. Tests: new cases in `TestMathLib::toDoubleNumber` (`1e10000`, `1e10000f`, `1e10000L`, `1.5E+400`, `1.0e4931L`); they fail without the change.
1 parent 169f6ae commit 9a4cfe6

7 files changed

Lines changed: 75 additions & 8 deletions

File tree

‎lib/checkcondition.cpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1301,6 +1301,10 @@ void CheckConditionImpl::checkIncorrectLogicOperator()
13011301
if (isfloat && (op1 == "==" || op1 == "!=" || op2 == "==" || op2 == "!="))
13021302
continue;
13031303

1304+
// no double value to compare (1e400L would become infinity)
1305+
if (isfloat && (MathLib::isLongDoubleOutOfDoubleRange(value1) || MathLib::isLongDoubleOutOfDoubleRange(value2)))
1306+
continue;
1307+
13041308
// the expr are not the token of the value but they provide better context
13051309
const double d1 = isfloat ? MathLib::toDoubleNumber(value1) : 0;
13061310
const double d2 = isfloat ? MathLib::toDoubleNumber(value2) : 0;

‎lib/mathlib.cpp‎

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,8 +532,15 @@ double MathLib::toDoubleNumber(const std::string &str, const Token * const tok)
532532
std::istringstream istr(str);
533533
istr.imbue(std::locale::classic());
534534
double ret;
535-
if (!(istr >> ret))
536-
throw InternalError(tok, "Internal Error. MathLib::toDoubleNumber: conversion failed: " + str);
535+
if (!(istr >> ret)) {
536+
// Out of the range of double, such as 1e10000 (glibc's HUGE_VAL for
537+
// compilers other than GCC): the stream stores the largest finite
538+
// value and fails, a compiler makes it infinity.
539+
if (!isFloat(str) || std::fabs(ret) < std::numeric_limits<double>::max())
540+
throw InternalError(tok, "Internal Error. MathLib::toDoubleNumber: conversion failed: " + str);
541+
ret = std::copysign(std::numeric_limits<double>::infinity(), ret);
542+
istr.clear();
543+
}
537544
std::string s;
538545
if (istr >> s) {
539546
if (isDecimalFloat(str))
@@ -584,6 +591,13 @@ bool MathLib::isFloat(const std::string &str)
584591
return isDecimalFloat(str) || isFloatHex(str);
585592
}
586593

594+
bool MathLib::isLongDoubleOutOfDoubleRange(const std::string &str)
595+
{
596+
if (str.empty() || (str.back() != 'l' && str.back() != 'L') || !isFloat(str))
597+
return false;
598+
return std::isinf(toDoubleNumber(str));
599+
}
600+
587601
bool MathLib::isDecimalFloat(const std::string &str)
588602
{
589603
if (str.empty())

‎lib/mathlib.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ class CPPCHECKLIB MathLib {
115115

116116
static bool isInt(const std::string & str);
117117
static bool isFloat(const std::string &str);
118+
/** @brief a long double literal out of the range of double, such as 1e400L, which toDoubleNumber() makes infinity */
119+
static bool isLongDoubleOutOfDoubleRange(const std::string &str);
118120
static bool isDecimalFloat(const std::string &str);
119121
static bool isNegative(const std::string &str);
120122
static bool isPositive(const std::string &str);

‎lib/vf_common.cpp‎

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -139,12 +139,16 @@ namespace ValueFlow
139139
// Bad character literal
140140
}
141141
} else if (tok->isNumber() && MathLib::isFloat(tok->str())) {
142-
Value value;
143-
value.valueType = Value::ValueType::FLOAT;
144-
value.floatValue = MathLib::toDoubleNumber(tok);
145-
if (!tok->isTemplateArg())
146-
value.setKnown();
147-
setTokenValue(tok, std::move(value), settings);
142+
// A long double literal out of the range of double (1e400L) has no
143+
// double value: infinity would make different literals compare equal.
144+
if (!MathLib::isLongDoubleOutOfDoubleRange(tok->str())) {
145+
Value value;
146+
value.valueType = Value::ValueType::FLOAT;
147+
value.floatValue = MathLib::toDoubleNumber(tok);
148+
if (!tok->isTemplateArg())
149+
value.setKnown();
150+
setTokenValue(tok, std::move(value), settings);
151+
}
148152
} else if (tok->enumerator() && tok->enumerator()->value_known) {
149153
Value value(tok->enumerator()->value);
150154
if (!tok->isTemplateArg())

‎test/testcondition.cpp‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ class TestCondition : public TestFixture {
6969
TEST_CASE(incorrectLogicOperator15);
7070
TEST_CASE(incorrectLogicOperator16); // #10070
7171
TEST_CASE(incorrectLogicOperator17);
72+
TEST_CASE(incorrectLogicOperator18); // #12282
7273
TEST_CASE(secondAlwaysTrueFalseWhenFirstTrueError);
7374
TEST_CASE(incorrectLogicOp_condSwapping);
7475
TEST_CASE(testBug5895);
@@ -1758,6 +1759,17 @@ class TestCondition : public TestFixture {
17581759
ASSERT_EQUALS("", errout_str());
17591760
}
17601761

1762+
void incorrectLogicOperator18() { // #12282
1763+
// long double literals out of the range of double are different values
1764+
check("int f(long double x) {\n"
1765+
" return x > 1e400L && x < 1e500L;\n"
1766+
"}\n"
1767+
"int g() {\n"
1768+
" return 1e400L < 1e500L;\n"
1769+
"}\n");
1770+
ASSERT_EQUALS("", errout_str());
1771+
}
1772+
17611773
void incorrectLogicOperator17() { // #12471
17621774
check("struct R {\n"
17631775
" void set() { i = 1; }\n"

‎test/testmathlib.cpp‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "token.h"
2323
#include "tokenlist.h"
2424

25+
#include <cmath>
2526
#include <limits>
2627
#include <memory>
2728
#include <string>
@@ -44,6 +45,7 @@ class TestMathLib : public TestFixture {
4445
TEST_CASE(isnegative);
4546
TEST_CASE(ispositive);
4647
TEST_CASE(isFloat);
48+
TEST_CASE(isLongDoubleOutOfDoubleRange);
4749
TEST_CASE(isDecimalFloat);
4850
TEST_CASE(isGreater);
4951
TEST_CASE(isGreaterEqual);
@@ -598,6 +600,16 @@ class TestMathLib : public TestFixture {
598600
// TODO: test with 128-bit values
599601
}
600602

603+
void isLongDoubleOutOfDoubleRange() const {
604+
ASSERT_EQUALS(true, MathLib::isLongDoubleOutOfDoubleRange("1e400L"));
605+
ASSERT_EQUALS(true, MathLib::isLongDoubleOutOfDoubleRange("1.0e4931l"));
606+
ASSERT_EQUALS(false, MathLib::isLongDoubleOutOfDoubleRange("1e300L"));
607+
ASSERT_EQUALS(false, MathLib::isLongDoubleOutOfDoubleRange("1e10000"));
608+
ASSERT_EQUALS(false, MathLib::isLongDoubleOutOfDoubleRange("1e10000f"));
609+
ASSERT_EQUALS(false, MathLib::isLongDoubleOutOfDoubleRange("10L"));
610+
ASSERT_EQUALS(false, MathLib::isLongDoubleOutOfDoubleRange(""));
611+
}
612+
601613
void toDoubleNumber() const {
602614
// float values
603615
ASSERT_EQUALS_DOUBLE(1.0, MathLib::toDoubleNumber("1."), 0.001);
@@ -677,6 +689,13 @@ class TestMathLib : public TestFixture {
677689
ASSERT_EQUALS_DOUBLE(static_cast<double>('\200'), MathLib::toDoubleNumber("'\\200'"), 0.000001);
678690
ASSERT_EQUALS_DOUBLE(static_cast<double>(L'A'), MathLib::toDoubleNumber("L'A'"), 0.000001);
679691

692+
// out of the range of double, like glibc's HUGE_VAL for compilers other than GCC
693+
ASSERT_EQUALS(true, std::isinf(MathLib::toDoubleNumber("1e10000")));
694+
ASSERT_EQUALS(true, std::isinf(MathLib::toDoubleNumber("1e10000f")));
695+
ASSERT_EQUALS(true, std::isinf(MathLib::toDoubleNumber("1e10000L")));
696+
ASSERT_EQUALS(true, std::isinf(MathLib::toDoubleNumber("1.5E+400")));
697+
ASSERT_EQUALS(true, std::isinf(MathLib::toDoubleNumber("1.0e4931L"))); // #12282
698+
680699
ASSERT_THROW_INTERNAL_EQUALS(MathLib::toDoubleNumber("invalid"), INTERNAL, "Internal Error. MathLib::toDoubleNumber: conversion failed: invalid");
681700

682701
// AppleClang before 18 reports a different error

‎test/testother.cpp‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ class TestOther : public TestFixture {
204204
TEST_CASE(duplicateExpression20);
205205
TEST_CASE(duplicateExpression21);
206206
TEST_CASE(duplicateExpression22);
207+
TEST_CASE(duplicateExpression23); // #12282
207208
TEST_CASE(duplicateExpressionLoop);
208209
TEST_CASE(duplicateValueTernary);
209210
TEST_CASE(duplicateValueTernarySizeof); // #13773
@@ -8498,6 +8499,17 @@ class TestOther : public TestFixture {
84988499
ASSERT_EQUALS("", errout_str());
84998500
}
85008501

8502+
void duplicateExpression23() { // #12282
8503+
// long double literals out of the range of double are different values
8504+
check("long double f() {\n"
8505+
" return 1e400L / 1e500L;\n"
8506+
"}\n"
8507+
"int g(long double x) {\n"
8508+
" return x == 1e400L || x == 1e500L;\n"
8509+
"}\n");
8510+
ASSERT_EQUALS("", errout_str());
8511+
}
8512+
85018513
void duplicateExpression22() {
85028514
check("int f() {\n" // #14913
85038515
" return 0x1 | (0x2 | 0x4) | 0x1;\n"

0 commit comments

Comments
 (0)