Skip to content

Commit ac8e92f

Browse files
author
Fytch
committed
fix UB in str2int
1 parent cf8da88 commit ac8e92f

File tree

1 file changed

+17
-5
lines changed

1 file changed

+17
-5
lines changed

include/ProgramOptions.hxx

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -628,21 +628,33 @@ namespace po {
628628
template<typename T, typename forward_iterator_t>
629629
parsing_report<T> str2int(forward_iterator_t first, forward_iterator_t last) {
630630
static_assert(std::numeric_limits<T>::is_integer && std::numeric_limits<T>::is_signed, "str2uint only supports signed integral types");
631+
static_assert(std::numeric_limits<T>::min() == -std::numeric_limits<T>::max() - 1, "type insufficient; doesn't employ two's complement");
632+
633+
using unsigned_t = typename std::make_unsigned<T>::type;
634+
static_assert(std::numeric_limits<unsigned_t>::max() > std::numeric_limits<T>::max(), "type insufficient");
631635

632636
for(; first != last && std::isspace(*first); ++first);
633637
const bool neg = expect(first, last, '-');
634638
if(!neg)
635639
expect(first, last, '+');
636640
if(first == last || !is_digit(*first))
637641
return error_code::conversion_error;
638-
using unsigned_t = typename std::make_unsigned<T>::type;
639642
const auto report = str2uint<unsigned_t>(first, last);
640643
if(!report)
641644
return report.error;
642-
unsigned_t max = neg ? static_cast<unsigned_t>(-std::numeric_limits<T>::min()) : static_cast<unsigned_t>(std::numeric_limits<T>::max());
643-
if(report > max)
644-
return error_code::out_of_range;
645-
return neg ? -static_cast<T>(report) : static_cast<T>(report);
645+
if(neg) {
646+
constexpr unsigned_t neg_max = static_cast<unsigned_t>(std::numeric_limits<T>::max()) + 1;
647+
if(report > neg_max)
648+
return error_code::out_of_range;
649+
else if(report == neg_max)
650+
return std::numeric_limits<T>::min();
651+
else
652+
return -static_cast<T>(report);
653+
} else {
654+
if(report > static_cast<unsigned_t>(std::numeric_limits<T>::max()))
655+
return error_code::out_of_range;
656+
return static_cast<T>(report);
657+
}
646658
}
647659
template<typename T>
648660
parsing_report<T> str2int(std::string const& str) {

0 commit comments

Comments
 (0)