Commit ba7aac9c8af072ee5503112531e14a2ed3b607cd
Committed by
Henry Schreiner
1 parent
eab92ed9
remove undefined bahavior (#290)
* change the checked_multiply function to not use undefined behavior to check for potential undefined behavior and wrapping. * update the checked_multiply template to deal with mismatched sign in signed numbers and min val correctly. This involved adding to templates to clear up warnings
Showing
2 changed files
with
49 additions
and
5 deletions
include/CLI/Validators.hpp
| 1 | 1 | #pragma once |
| 2 | - | |
| 3 | 2 | // Distributed under the 3-Clause BSD License. See accompanying |
| 4 | 3 | // file LICENSE or https://github.com/CLIUtils/CLI11 for details. |
| 5 | 4 | |
| ... | ... | @@ -9,6 +8,7 @@ |
| 9 | 8 | #include <cmath> |
| 10 | 9 | #include <functional> |
| 11 | 10 | #include <iostream> |
| 11 | +#include <limits> | |
| 12 | 12 | #include <map> |
| 13 | 13 | #include <memory> |
| 14 | 14 | #include <string> |
| ... | ... | @@ -512,17 +512,37 @@ auto search(const T &set, const V &val, const std::function<V(V)> &filter_functi |
| 512 | 512 | return {(it != std::end(setref)), it}; |
| 513 | 513 | } |
| 514 | 514 | |
| 515 | +// the following suggestion was made by Nikita Ofitserov(@himikof) | |
| 516 | +// done in templates to prevent compiler warnings on negation of unsigned numbers | |
| 517 | + | |
| 518 | +/// Do a check for overflow on signed numbers | |
| 519 | +template <typename T> | |
| 520 | +inline typename std::enable_if<std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) { | |
| 521 | + if((a > 0) == (b > 0)) { | |
| 522 | + return ((std::numeric_limits<T>::max)() / (std::abs)(a) < (std::abs)(b)); | |
| 523 | + } else { | |
| 524 | + return ((std::numeric_limits<T>::min)() / (std::abs)(a) > -(std::abs)(b)); | |
| 525 | + } | |
| 526 | +} | |
| 527 | +/// Do a check for overflow on unsigned numbers | |
| 528 | +template <typename T> | |
| 529 | +inline typename std::enable_if<!std::is_signed<T>::value, T>::type overflowCheck(const T &a, const T &b) { | |
| 530 | + return ((std::numeric_limits<T>::max)() / a < b); | |
| 531 | +} | |
| 532 | + | |
| 515 | 533 | /// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise. |
| 516 | 534 | template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) { |
| 517 | - if(a == 0 || b == 0) { | |
| 535 | + if(a == 0 || b == 0 || a == 1 || b == 1) { | |
| 518 | 536 | a *= b; |
| 519 | 537 | return true; |
| 520 | 538 | } |
| 521 | - T c = a * b; | |
| 522 | - if(c / a != b) { | |
| 539 | + if(a == (std::numeric_limits<T>::min)() || b == (std::numeric_limits<T>::min)()) { | |
| 523 | 540 | return false; |
| 524 | 541 | } |
| 525 | - a = c; | |
| 542 | + if(overflowCheck(a, b)) { | |
| 543 | + return false; | |
| 544 | + } | |
| 545 | + a *= b; | |
| 526 | 546 | return true; |
| 527 | 547 | } |
| 528 | 548 | ... | ... |
tests/HelpersTest.cpp
| ... | ... | @@ -423,10 +423,34 @@ TEST(CheckedMultiply, Int) { |
| 423 | 423 | ASSERT_FALSE(CLI::detail::checked_multiply(a, b)); |
| 424 | 424 | ASSERT_EQ(a, std::numeric_limits<int>::min()); |
| 425 | 425 | |
| 426 | + b = std::numeric_limits<int>::min(); | |
| 427 | + a = -1; | |
| 428 | + ASSERT_FALSE(CLI::detail::checked_multiply(a, b)); | |
| 429 | + ASSERT_EQ(a, -1); | |
| 430 | + | |
| 426 | 431 | a = std::numeric_limits<int>::min() / 100; |
| 427 | 432 | b = 99; |
| 428 | 433 | ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); |
| 429 | 434 | ASSERT_EQ(a, std::numeric_limits<int>::min() / 100 * 99); |
| 435 | + | |
| 436 | + a = std::numeric_limits<int>::min() / 100; | |
| 437 | + b = -101; | |
| 438 | + ASSERT_FALSE(CLI::detail::checked_multiply(a, b)); | |
| 439 | + ASSERT_EQ(a, std::numeric_limits<int>::min() / 100); | |
| 440 | + a = 2; | |
| 441 | + b = std::numeric_limits<int>::min() / 2; | |
| 442 | + ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); | |
| 443 | + a = std::numeric_limits<int>::min() / 2; | |
| 444 | + b = 2; | |
| 445 | + ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); | |
| 446 | + | |
| 447 | + a = 4; | |
| 448 | + b = std::numeric_limits<int>::min() / 4; | |
| 449 | + ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); | |
| 450 | + | |
| 451 | + a = 48; | |
| 452 | + b = std::numeric_limits<int>::min() / 48; | |
| 453 | + ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); | |
| 430 | 454 | } |
| 431 | 455 | |
| 432 | 456 | TEST(CheckedMultiply, SizeT) { | ... | ... |