Skip to content

Commit cf8da88

Browse files
author
Fytch
committed
change the behavior of str2flt to return an error if the value is too big and would be inf. Also fix UB from integer overflow
1 parent 3ec92cc commit cf8da88

File tree

2 files changed

+65
-38
lines changed

2 files changed

+65
-38
lines changed

include/ProgramOptions.hxx

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -659,6 +659,7 @@ namespace po {
659659
static_assert(std::numeric_limits<T>::is_iec559, "platform doesn't meet requirements");
660660
static_assert(std::numeric_limits<T>::has_quiet_NaN, "type insufficient; doesn't support quiet NaNs");
661661
static_assert(std::numeric_limits<T>::has_infinity, "type insufficient; doesn't support infinities");
662+
using std::isinf;
662663

663664
for(; first != last && std::isspace(*first); ++first);
664665
const bool neg = expect(first, last, '-');
@@ -692,13 +693,28 @@ namespace po {
692693
expect(first, last, '+');
693694
if(first == last || !is_digit(*first))
694695
return error_code::conversion_error;
695-
int exp{};
696-
for(int d{}; first != last && (d = get_digit(*first)) >= 0; ++first)
696+
int exp = 0;
697+
bool exp_overflow = false;
698+
for(int d{}; first != last && (d = get_digit(*first)) >= 0; ++first) {
697699
exp = 10 * exp + static_cast<int>(d);
698-
if(neg_exp)
699-
exp = -exp;
700-
result *= std::pow(T{ 10 }, exp);
700+
if((exp_overflow |= (exp > std::numeric_limits<T>::max_exponent10))) {
701+
for(; first != last && is_digit(*first); ++first);
702+
break;
703+
}
704+
}
705+
if(exp_overflow) {
706+
if(neg_exp)
707+
result = 0;
708+
else
709+
return error_code::out_of_range;
710+
} else {
711+
if(neg_exp)
712+
exp = -exp;
713+
result *= std::pow(T(10), exp);
714+
}
701715
}
716+
if(isinf(result))
717+
return error_code::out_of_range;
702718
}
703719
for(; first != last && std::isspace(*first); ++first);
704720
if(first != last)

test/str2flt.cxx

Lines changed: 44 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,15 @@
11
#include <catch2/catch.hpp>
22
#define PROGRAMOPTIONS_EXCEPTIONS
33
#include <ProgramOptions.hxx>
4-
#include <string>
54
#include <limits>
5+
#include <string>
66
#include <cmath>
77

88
TEST_CASE("str2flt", "[ProgramOptions]") {
9+
const double inf = std::numeric_limits<double>::infinity();
10+
11+
CHECK(po::str2flt<double>("").error == po::error_code::conversion_error);
12+
913
CHECK(po::str2flt<double>("+0").good());
1014
CHECK(po::str2flt<double>("+0") == 0.0);
1115
CHECK(po::str2flt<double>("-0").good());
@@ -14,50 +18,57 @@ TEST_CASE("str2flt", "[ProgramOptions]") {
1418
CHECK(po::str2flt<double>("-42.36") == Approx(-42.36));
1519
CHECK(po::str2flt<double>(".53") == Approx(0.53));
1620
CHECK(po::str2flt<double>("21.") == Approx(21.0));
21+
CHECK(po::str2flt<double>("-.07") == Approx(-0.07));
22+
CHECK(po::str2flt<double>("-07.") == Approx(-7.0));
1723

1824
CHECK(po::str2flt<double>("123.456e-12") == Approx(123.456e-12));
1925
CHECK(po::str2flt<double>("-42.36E8") == Approx(-42.36e+8));
2026
CHECK(po::str2flt<double>(".53e0") == Approx(0.53));
2127
CHECK(po::str2flt<double>("21.e+3") == Approx(21.0e+3));
22-
CHECK(std::isinf(po::str2flt<double>("15.3e+806").get()));
28+
29+
CHECK(po::str2flt<double>("15.3e+806").error == po::error_code::out_of_range);
30+
CHECK(po::str2flt<double>("1e9999999999999999999999999999").error == po::error_code::out_of_range);
31+
std::string big(500, '9');
32+
CHECK(po::str2flt<double>(big.begin(), big.end()).error == po::error_code::out_of_range);
2333
CHECK(po::str2flt<double>("15.3e-806") == Approx(0.0));
34+
CHECK(po::str2flt<double>("1e-9999999999999999999999999999") == Approx(0.0));
35+
36+
CHECK(po::str2flt<double>(".").error == po::error_code::conversion_error);
37+
CHECK(po::str2flt<double>("-.").error == po::error_code::conversion_error);
38+
CHECK(po::str2flt<double>("++0").error == po::error_code::conversion_error);
39+
CHECK(po::str2flt<double>("+-0").error == po::error_code::conversion_error);
40+
CHECK(po::str2flt<double>("-+0").error == po::error_code::conversion_error);
41+
CHECK(po::str2flt<double>("--0").error == po::error_code::conversion_error);
42+
CHECK(po::str2flt<double>("4.a").error == po::error_code::conversion_error);
43+
CHECK(po::str2flt<double>("2.5 a").error == po::error_code::conversion_error);
44+
CHECK(po::str2flt<double>("e3").error == po::error_code::conversion_error);
45+
CHECK(po::str2flt<double>("1e").error == po::error_code::conversion_error);
46+
CHECK(po::str2flt<double>("1e+").error == po::error_code::conversion_error);
47+
CHECK(po::str2flt<double>("1e+2.0").error == po::error_code::conversion_error);
2448

25-
CHECK(!po::str2flt<double>(".").good());
26-
CHECK(!po::str2flt<double>("-.").good());
27-
CHECK(!po::str2flt<double>("++0").good());
28-
CHECK(!po::str2flt<double>("+-0").good());
29-
CHECK(!po::str2flt<double>("-+0").good());
30-
CHECK(!po::str2flt<double>("--0").good());
31-
CHECK(!po::str2flt<double>("4.a").good());
32-
CHECK(!po::str2flt<double>("2.5 a").good());
33-
CHECK(!po::str2flt<double>("e3").good());
34-
CHECK(!po::str2flt<double>("1e").good());
35-
CHECK(!po::str2flt<double>("1e+").good());
36-
CHECK(!po::str2flt<double>("1e+2.0").good());
37-
38-
CHECK(!po::str2flt<double>("n").good());
39-
CHECK(!po::str2flt<double>("na").good());
49+
CHECK(po::str2flt<double>("n").error == po::error_code::conversion_error);
50+
CHECK(po::str2flt<double>("na").error == po::error_code::conversion_error);
4051
CHECK(po::str2flt<double>("nan").good());
41-
CHECK(!po::str2flt<double>("nann").good());
42-
CHECK(!po::str2flt<double>("nanE0").good());
52+
CHECK(po::str2flt<double>("nann").error == po::error_code::conversion_error);
53+
CHECK(po::str2flt<double>("nanE0").error == po::error_code::conversion_error);
4354
CHECK(std::isnan(po::str2flt<double>("nan").get()));
4455
CHECK(std::isnan(po::str2flt<double>("+nan").get()));
4556
CHECK(std::isnan(po::str2flt<double>("-nan").get()));
4657

47-
CHECK(!po::str2flt<double>("i").good());
48-
CHECK(!po::str2flt<double>("in").good());
58+
CHECK(po::str2flt<double>("i").error == po::error_code::conversion_error);
59+
CHECK(po::str2flt<double>("in").error == po::error_code::conversion_error);
4960
CHECK(po::str2flt<double>("inf").good());
50-
CHECK(!po::str2flt<double>("infi").good());
51-
CHECK(!po::str2flt<double>("infin").good());
52-
CHECK(!po::str2flt<double>("infini").good());
53-
CHECK(!po::str2flt<double>("infinit").good());
61+
CHECK(po::str2flt<double>("infi").error == po::error_code::conversion_error);
62+
CHECK(po::str2flt<double>("infin").error == po::error_code::conversion_error);
63+
CHECK(po::str2flt<double>("infini").error == po::error_code::conversion_error);
64+
CHECK(po::str2flt<double>("infinit").error == po::error_code::conversion_error);
5465
CHECK(po::str2flt<double>("infinity").good());
55-
CHECK(!po::str2flt<double>("infinityy").good());
56-
CHECK(!po::str2flt<double>("infinityE0").good());
57-
CHECK(std::isinf(po::str2flt<double>("inf").get()));
58-
CHECK(std::isinf(po::str2flt<double>("+inf").get()));
59-
CHECK(std::isinf(po::str2flt<double>("-inf").get()));
60-
CHECK(std::isinf(po::str2flt<double>("infinity").get()));
61-
CHECK(std::isinf(po::str2flt<double>("+infinity").get()));
62-
CHECK(std::isinf(po::str2flt<double>("-infinity").get()));
66+
CHECK(po::str2flt<double>("infinityy").error == po::error_code::conversion_error);
67+
CHECK(po::str2flt<double>("infinityE0").error == po::error_code::conversion_error);
68+
CHECK(po::str2flt<double>("inf") == inf);
69+
CHECK(po::str2flt<double>("+inf") == inf);
70+
CHECK(po::str2flt<double>("-inf") == -inf);
71+
CHECK(po::str2flt<double>("infinity") == inf);
72+
CHECK(po::str2flt<double>("+infinity") == inf);
73+
CHECK(po::str2flt<double>("-infinity") == -inf);
6374
}

0 commit comments

Comments
 (0)