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 | #pragma once | 1 | #pragma once |
| 2 | - | ||
| 3 | // Distributed under the 3-Clause BSD License. See accompanying | 2 | // Distributed under the 3-Clause BSD License. See accompanying |
| 4 | // file LICENSE or https://github.com/CLIUtils/CLI11 for details. | 3 | // file LICENSE or https://github.com/CLIUtils/CLI11 for details. |
| 5 | 4 | ||
| @@ -9,6 +8,7 @@ | @@ -9,6 +8,7 @@ | ||
| 9 | #include <cmath> | 8 | #include <cmath> |
| 10 | #include <functional> | 9 | #include <functional> |
| 11 | #include <iostream> | 10 | #include <iostream> |
| 11 | +#include <limits> | ||
| 12 | #include <map> | 12 | #include <map> |
| 13 | #include <memory> | 13 | #include <memory> |
| 14 | #include <string> | 14 | #include <string> |
| @@ -512,17 +512,37 @@ auto search(const T &set, const V &val, const std::function<V(V)> &filter_functi | @@ -512,17 +512,37 @@ auto search(const T &set, const V &val, const std::function<V(V)> &filter_functi | ||
| 512 | return {(it != std::end(setref)), it}; | 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 | /// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise. | 533 | /// Performs a *= b; if it doesn't cause integer overflow. Returns false otherwise. |
| 516 | template <typename T> typename std::enable_if<std::is_integral<T>::value, bool>::type checked_multiply(T &a, T b) { | 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 | a *= b; | 536 | a *= b; |
| 519 | return true; | 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 | return false; | 540 | return false; |
| 524 | } | 541 | } |
| 525 | - a = c; | 542 | + if(overflowCheck(a, b)) { |
| 543 | + return false; | ||
| 544 | + } | ||
| 545 | + a *= b; | ||
| 526 | return true; | 546 | return true; |
| 527 | } | 547 | } |
| 528 | 548 |
tests/HelpersTest.cpp
| @@ -423,10 +423,34 @@ TEST(CheckedMultiply, Int) { | @@ -423,10 +423,34 @@ TEST(CheckedMultiply, Int) { | ||
| 423 | ASSERT_FALSE(CLI::detail::checked_multiply(a, b)); | 423 | ASSERT_FALSE(CLI::detail::checked_multiply(a, b)); |
| 424 | ASSERT_EQ(a, std::numeric_limits<int>::min()); | 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 | a = std::numeric_limits<int>::min() / 100; | 431 | a = std::numeric_limits<int>::min() / 100; |
| 427 | b = 99; | 432 | b = 99; |
| 428 | ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); | 433 | ASSERT_TRUE(CLI::detail::checked_multiply(a, b)); |
| 429 | ASSERT_EQ(a, std::numeric_limits<int>::min() / 100 * 99); | 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 | TEST(CheckedMultiply, SizeT) { | 456 | TEST(CheckedMultiply, SizeT) { |