Skip to content

Commit e8d9aa0

Browse files
committed
🐛 Fix UB in integer overflow
Some tests cause integer overflow: they need to be guarded by `RC_PRE`. This fixes signed 64-bit negation, addition and subtraction. Multiplication still has this problem.
1 parent 3c64345 commit e8d9aa0

File tree

7 files changed

+416
-338
lines changed

7 files changed

+416
-338
lines changed

test/safe/CMakeLists.txt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@ function(add_test_suites)
88
add_unit_test(
99
${test_name}
1010
GTEST
11-
FILES
12-
${test_file}
11+
FILES
12+
${test_file}
1313
INCLUDE_DIRECTORIES
14-
${CMAKE_SOURCE_DIR}/test/
14+
${CMAKE_SOURCE_DIR}/test/
1515
LIBRARIES
16-
safe_arithmetic)
16+
safe_arithmetic)
1717
endforeach()
1818
endfunction()
1919

@@ -47,5 +47,4 @@ add_test_suites(
4747
dsl/bitwise_xor.cpp
4848
dsl/bitwise_invert.cpp
4949
dsl.cpp
50-
dsl/minus.cpp
51-
)
50+
dsl/minus.cpp)

test/safe/big_integer.cpp

Lines changed: 182 additions & 155 deletions
Original file line numberDiff line numberDiff line change
@@ -1,160 +1,187 @@
1-
#include <gtest/gtest.h>
2-
#include <rapidcheck/gtest.h>
1+
#include "big_integer/detail/properties.hpp"
32

4-
#include <safe/big_integer_gen.hpp>
53
#include <safe/big_integer.hpp>
4+
#include <safe/big_integer_gen.hpp>
65

6+
#include <gtest/gtest.h>
7+
#include <rapidcheck/gtest.h>
78

89
namespace safe {
9-
RC_GTEST_PROP(big_integer, is_plus_64, (int64_t a, int64_t b)) {
10-
RC_ASSERT(big_integer<64>(a + b) == big_integer<64>(big_integer<64>(a) + big_integer<64>(b)));
11-
}
12-
13-
RC_GTEST_PROP(big_integer, is_minus_64, (int64_t a, int64_t b)) {
14-
RC_ASSERT(big_integer<64>(a - b) == big_integer<64>(big_integer<64>(a) - big_integer<64>(b)));
15-
}
16-
17-
RC_GTEST_PROP(big_integer, is_multiply_64, (int64_t a, int64_t b)) {
18-
RC_ASSERT(big_integer<64>(a * b) == big_integer<64>(big_integer<64>(a) * big_integer<64>(b)));
19-
}
20-
21-
RC_GTEST_PROP(big_integer, is_divide_64, (int64_t a, int64_t b)) {
22-
RC_PRE(b != 0);
23-
RC_ASSERT(big_integer<64>(a / b) == big_integer<64>(big_integer<64>(a) / big_integer<64>(b)));
24-
}
25-
26-
RC_GTEST_PROP(big_integer, is_mod_64, (int64_t a, int64_t b)) {
27-
RC_PRE(b != 0);
28-
RC_ASSERT(big_integer<64>(a % b) == big_integer<64>(big_integer<64>(a) % big_integer<64>(b)));
29-
}
30-
31-
RC_GTEST_PROP(big_integer, is_bit_or_64, (int64_t a, int64_t b)) {
32-
RC_ASSERT(big_integer<64>(a | b) == (big_integer<64>(a) | big_integer<64>(b)));
33-
}
34-
35-
RC_GTEST_PROP(big_integer, is_bit_and_64, (int64_t a, int64_t b)) {
36-
RC_ASSERT(big_integer<64>(a & b) == (big_integer<64>(a) & big_integer<64>(b)));
37-
}
38-
39-
RC_GTEST_PROP(big_integer, is_bit_xor_64, (int64_t a, int64_t b)) {
40-
RC_ASSERT(big_integer<64>(a ^ b) == (big_integer<64>(a) ^ big_integer<64>(b)));
41-
}
42-
43-
RC_GTEST_PROP(big_integer, is_bit_shift_left_64, (int64_t a)) {
44-
auto const i = *rc::gen::inRange<int32_t>(0, 63);
45-
RC_ASSERT(big_integer<64>(a << i) == (big_integer<64>(a) << i));
46-
}
47-
48-
RC_GTEST_PROP(big_integer, is_bit_shift_right_64, (int64_t a)) {
49-
auto const i = *rc::gen::inRange<int32_t>(0, 63);
50-
RC_ASSERT(big_integer<64>(a >> i) == (big_integer<64>(a) >> i));
51-
}
52-
53-
RC_GTEST_PROP(big_integer, plus_is_commutative, (big_integer<256> a, big_integer<256> b)) {
54-
RC_ASSERT((a + b) == (b + a));
55-
}
56-
57-
RC_GTEST_PROP(big_integer, plus_is_associative, (big_integer<256> a, big_integer<256> b, big_integer<256> c)) {
58-
RC_ASSERT(((a + b) + c) == (a + (b + c)));
59-
}
60-
61-
RC_GTEST_PROP(big_integer, plus_identity, (big_integer<256> a)) {
62-
RC_ASSERT((a + 0) == a);
63-
}
64-
65-
RC_GTEST_PROP(big_integer, minus_itself_is_zero, (big_integer<256> a)) {
66-
RC_ASSERT((a - a) == 0);
67-
}
68-
69-
RC_GTEST_PROP(big_integer, multiply_is_commutative, (big_integer<256> a, big_integer<256> b)) {
70-
RC_ASSERT((a * b) == (b * a));
71-
}
72-
73-
RC_GTEST_PROP(big_integer, multiply_is_associative, (big_integer<256> a, big_integer<256> b, big_integer<256> c)) {
74-
RC_ASSERT(((a * b) * c) == (a * (b * c)));
75-
}
76-
77-
RC_GTEST_PROP(big_integer, multiply_identity, (big_integer<256> a)) {
78-
RC_ASSERT((a * 1) == a);
79-
}
80-
81-
RC_GTEST_PROP(big_integer, multiply_by_zero_is_zero, (big_integer<256> a)) {
82-
RC_ASSERT((a * 0) == 0);
83-
}
84-
85-
RC_GTEST_PROP(big_integer, divmod_and_multiply_relation, (big_integer<256> dividend, big_integer<256> divisor)) {
86-
RC_PRE(divisor != 0);
87-
auto [q, r] = divmod(dividend, divisor);
88-
RC_ASSERT(((divisor * q) + r) == dividend);
89-
}
90-
91-
RC_GTEST_PROP(big_integer, division_remainder_and_multiply_relation, (big_integer<256> dividend, big_integer<256> divisor)) {
92-
RC_PRE(divisor != 0);
93-
RC_ASSERT(((divisor * (dividend / divisor)) + (dividend % divisor)) == dividend);
94-
}
95-
96-
RC_GTEST_PROP(big_integer, division_identity, (big_integer<256> a)) {
97-
RC_ASSERT((a / 1) == a);
98-
}
99-
100-
RC_GTEST_PROP(big_integer, division_zero_dividend, (big_integer<256> a)) {
101-
RC_PRE(a != 0);
102-
RC_ASSERT((0 / a) == 0);
103-
}
104-
105-
RC_GTEST_PROP(big_integer, shift_left_and_right, (big_integer<256> a)) {
106-
big_integer<512> b{a};
107-
108-
auto const i = *rc::gen::inRange<int32_t>(0, 256);
109-
RC_ASSERT(b == ((b << i) >> i));
110-
}
111-
112-
RC_GTEST_PROP(big_integer, shift_distributes_over_add, (big_integer<256> a, big_integer<256> b)) {
113-
big_integer<512> a_prime{a};
114-
big_integer<512> b_prime{b};
115-
116-
auto const i = *rc::gen::inRange<int32_t>(0, 256);
117-
118-
RC_ASSERT(((a_prime + b_prime) << i) == ((a_prime << i) + (b_prime << i)));
119-
}
120-
121-
RC_GTEST_PROP(big_integer, bitwise_demorgan_laws, (big_integer<512> a, big_integer<512> b)) {
122-
RC_ASSERT(~(a | b) == (~a & ~b));
123-
RC_ASSERT(~(a & b) == (~a | ~b));
124-
}
125-
126-
void shifted_operation(auto op, big_integer<64> small_a, big_integer<64> small_b) {
127-
big_integer<1024> a{small_a};
128-
big_integer<1024> b{small_b};
129-
130-
auto const i = *rc::gen::inRange<int32_t>(0, 959);
131-
132-
RC_ASSERT(op(a, b) == op((a << i), (b << i)) >> i);
133-
}
134-
135-
RC_GTEST_PROP(big_integer, shifted_bit_and, (big_integer<64> small_a, big_integer<64> small_b)) {
136-
shifted_operation(std::bit_and{}, small_a, small_b);
137-
}
138-
139-
RC_GTEST_PROP(big_integer, shifted_bit_or, (big_integer<64> small_a, big_integer<64> small_b)) {
140-
shifted_operation(std::bit_or{}, small_a, small_b);
141-
}
142-
143-
RC_GTEST_PROP(big_integer, shifted_bit_xor, (big_integer<64> small_a, big_integer<64> small_b)) {
144-
shifted_operation(std::bit_xor{}, small_a, small_b);
145-
}
146-
147-
RC_GTEST_PROP(big_integer, shifted_plus, (big_integer<64> small_a, big_integer<64> small_b)) {
148-
shifted_operation(std::plus{}, small_a, small_b);
149-
}
150-
151-
RC_GTEST_PROP(big_integer, shifted_minus, (big_integer<64> small_a, big_integer<64> small_b)) {
152-
shifted_operation(std::minus{}, small_a, small_b);
153-
}
154-
155-
156-
TEST(big_integer, plus) {
157-
using T = big_integer<32>;
158-
ASSERT_EQ(T(1) + T(2), T(3));
159-
}
160-
}
10+
RC_GTEST_PROP(big_integer, is_plus_64, (int64_t a, int64_t b)) {
11+
RC_PRE(_big_integer::detail::addition_will_not_overflow(a, b));
12+
RC_ASSERT(big_integer<64>(a + b) ==
13+
big_integer<64>(big_integer<64>(a) + big_integer<64>(b)));
14+
}
15+
16+
RC_GTEST_PROP(big_integer, is_minus_64, (int64_t a, int64_t b)) {
17+
RC_PRE(_big_integer::detail::subtraction_will_not_overflow(a, b));
18+
RC_ASSERT(big_integer<64>(a - b) ==
19+
big_integer<64>(big_integer<64>(a) - big_integer<64>(b)));
20+
}
21+
22+
RC_GTEST_PROP(big_integer, is_multiply_64, (int32_t a, int32_t b)) {
23+
auto a64 = static_cast<std::int64_t>(a);
24+
auto b64 = static_cast<std::int64_t>(b);
25+
RC_ASSERT(big_integer<64>(a64 * b64) ==
26+
big_integer<64>(big_integer<64>(a64) * big_integer<64>(b64)));
27+
}
28+
29+
RC_GTEST_PROP(big_integer, is_divide_64, (int64_t a, int64_t b)) {
30+
RC_PRE(b != 0);
31+
RC_ASSERT(big_integer<64>(a / b) ==
32+
big_integer<64>(big_integer<64>(a) / big_integer<64>(b)));
33+
}
34+
35+
RC_GTEST_PROP(big_integer, is_mod_64, (int64_t a, int64_t b)) {
36+
RC_PRE(b != 0);
37+
RC_ASSERT(big_integer<64>(a % b) ==
38+
big_integer<64>(big_integer<64>(a) % big_integer<64>(b)));
39+
}
40+
41+
RC_GTEST_PROP(big_integer, is_bit_or_64, (int64_t a, int64_t b)) {
42+
RC_ASSERT(big_integer<64>(a | b) ==
43+
(big_integer<64>(a) | big_integer<64>(b)));
44+
}
45+
46+
RC_GTEST_PROP(big_integer, is_bit_and_64, (int64_t a, int64_t b)) {
47+
RC_ASSERT(big_integer<64>(a & b) ==
48+
(big_integer<64>(a) & big_integer<64>(b)));
49+
}
50+
51+
RC_GTEST_PROP(big_integer, is_bit_xor_64, (int64_t a, int64_t b)) {
52+
RC_ASSERT(big_integer<64>(a ^ b) ==
53+
(big_integer<64>(a) ^ big_integer<64>(b)));
54+
}
55+
56+
RC_GTEST_PROP(big_integer, is_bit_shift_left_64, (int64_t a)) {
57+
auto const i = *rc::gen::inRange<int32_t>(0, 63);
58+
RC_ASSERT(big_integer<64>(a << i) == (big_integer<64>(a) << i));
59+
}
60+
61+
RC_GTEST_PROP(big_integer, is_bit_shift_right_64, (int64_t a)) {
62+
auto const i = *rc::gen::inRange<int32_t>(0, 63);
63+
RC_ASSERT(big_integer<64>(a >> i) == (big_integer<64>(a) >> i));
64+
}
65+
66+
RC_GTEST_PROP(big_integer, plus_is_commutative,
67+
(big_integer<256> a, big_integer<256> b)) {
68+
RC_ASSERT((a + b) == (b + a));
69+
}
70+
71+
RC_GTEST_PROP(big_integer, plus_is_associative,
72+
(big_integer<256> a, big_integer<256> b, big_integer<256> c)) {
73+
RC_ASSERT(((a + b) + c) == (a + (b + c)));
74+
}
75+
76+
RC_GTEST_PROP(big_integer, plus_identity, (big_integer<256> a)) {
77+
RC_ASSERT((a + 0) == a);
78+
}
79+
80+
RC_GTEST_PROP(big_integer, minus_itself_is_zero, (big_integer<256> a)) {
81+
RC_ASSERT((a - a) == 0);
82+
}
83+
84+
RC_GTEST_PROP(big_integer, multiply_is_commutative,
85+
(big_integer<256> a, big_integer<256> b)) {
86+
RC_ASSERT((a * b) == (b * a));
87+
}
88+
89+
RC_GTEST_PROP(big_integer, multiply_is_associative,
90+
(big_integer<256> a, big_integer<256> b, big_integer<256> c)) {
91+
RC_ASSERT(((a * b) * c) == (a * (b * c)));
92+
}
93+
94+
RC_GTEST_PROP(big_integer, multiply_identity, (big_integer<256> a)) {
95+
RC_ASSERT((a * 1) == a);
96+
}
97+
98+
RC_GTEST_PROP(big_integer, multiply_by_zero_is_zero, (big_integer<256> a)) {
99+
RC_ASSERT((a * 0) == 0);
100+
}
101+
102+
RC_GTEST_PROP(big_integer, divmod_and_multiply_relation,
103+
(big_integer<256> dividend, big_integer<256> divisor)) {
104+
RC_PRE(divisor != 0);
105+
auto [q, r] = divmod(dividend, divisor);
106+
RC_ASSERT(((divisor * q) + r) == dividend);
107+
}
108+
109+
RC_GTEST_PROP(big_integer, division_remainder_and_multiply_relation,
110+
(big_integer<256> dividend, big_integer<256> divisor)) {
111+
RC_PRE(divisor != 0);
112+
RC_ASSERT(((divisor * (dividend / divisor)) + (dividend % divisor)) ==
113+
dividend);
114+
}
115+
116+
RC_GTEST_PROP(big_integer, division_identity, (big_integer<256> a)) {
117+
RC_ASSERT((a / 1) == a);
118+
}
119+
120+
RC_GTEST_PROP(big_integer, division_zero_dividend, (big_integer<256> a)) {
121+
RC_PRE(a != 0);
122+
RC_ASSERT((0 / a) == 0);
123+
}
124+
125+
RC_GTEST_PROP(big_integer, shift_left_and_right, (big_integer<256> a)) {
126+
big_integer<512> b{a};
127+
128+
auto const i = *rc::gen::inRange<int32_t>(0, 256);
129+
RC_ASSERT(b == ((b << i) >> i));
130+
}
131+
132+
RC_GTEST_PROP(big_integer, shift_distributes_over_add,
133+
(big_integer<256> a, big_integer<256> b)) {
134+
big_integer<512> a_prime{a};
135+
big_integer<512> b_prime{b};
136+
137+
auto const i = *rc::gen::inRange<int32_t>(0, 256);
138+
139+
RC_ASSERT(((a_prime + b_prime) << i) == ((a_prime << i) + (b_prime << i)));
140+
}
141+
142+
RC_GTEST_PROP(big_integer, bitwise_demorgan_laws,
143+
(big_integer<512> a, big_integer<512> b)) {
144+
RC_ASSERT(~(a | b) == (~a & ~b));
145+
RC_ASSERT(~(a & b) == (~a | ~b));
146+
}
147+
148+
void shifted_operation(auto op, big_integer<64> small_a,
149+
big_integer<64> small_b) {
150+
big_integer<1024> a{small_a};
151+
big_integer<1024> b{small_b};
152+
153+
auto const i = *rc::gen::inRange<int32_t>(0, 959);
154+
155+
RC_ASSERT(op(a, b) == op((a << i), (b << i)) >> i);
156+
}
157+
158+
RC_GTEST_PROP(big_integer, shifted_bit_and,
159+
(big_integer<64> small_a, big_integer<64> small_b)) {
160+
shifted_operation(std::bit_and{}, small_a, small_b);
161+
}
162+
163+
RC_GTEST_PROP(big_integer, shifted_bit_or,
164+
(big_integer<64> small_a, big_integer<64> small_b)) {
165+
shifted_operation(std::bit_or{}, small_a, small_b);
166+
}
167+
168+
RC_GTEST_PROP(big_integer, shifted_bit_xor,
169+
(big_integer<64> small_a, big_integer<64> small_b)) {
170+
shifted_operation(std::bit_xor{}, small_a, small_b);
171+
}
172+
173+
RC_GTEST_PROP(big_integer, shifted_plus,
174+
(big_integer<64> small_a, big_integer<64> small_b)) {
175+
shifted_operation(std::plus{}, small_a, small_b);
176+
}
177+
178+
RC_GTEST_PROP(big_integer, shifted_minus,
179+
(big_integer<64> small_a, big_integer<64> small_b)) {
180+
shifted_operation(std::minus{}, small_a, small_b);
181+
}
182+
183+
TEST(big_integer, plus) {
184+
using T = big_integer<32>;
185+
ASSERT_EQ(T(1) + T(2), T(3));
186+
}
187+
} // namespace safe

0 commit comments

Comments
 (0)