Skip to content

Commit fa1fb7f

Browse files
committed
Fix UB in bitwise logic of std/ranges::fill/fill_n algorithms
1 parent 657fb44 commit fa1fb7f

File tree

7 files changed

+321
-110
lines changed

7 files changed

+321
-110
lines changed

libcxx/include/__algorithm/fill_n.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ __fill_n_bool(__bit_iterator<_Cp, false> __first, typename _Cp::size_type __n) {
4141
if (__first.__ctz_ != 0) {
4242
__storage_type __clz_f = static_cast<__storage_type>(__bits_per_word - __first.__ctz_);
4343
__storage_type __dn = std::min(__clz_f, __n);
44-
__storage_type __m = (~__storage_type(0) << __first.__ctz_) & (~__storage_type(0) >> (__clz_f - __dn));
44+
__storage_type __m = std::__middle_mask<__storage_type>(__first.__ctz_, __clz_f - __dn);
4545
if (_FillVal)
4646
*__first.__seg_ |= __m;
4747
else
@@ -56,7 +56,7 @@ __fill_n_bool(__bit_iterator<_Cp, false> __first, typename _Cp::size_type __n) {
5656
// do last partial word
5757
if (__n > 0) {
5858
__first.__seg_ += __nw;
59-
__storage_type __m = ~__storage_type(0) >> (__bits_per_word - __n);
59+
__storage_type __m = std::__trailing_mask<__storage_type>(__bits_per_word - __n);
6060
if (_FillVal)
6161
*__first.__seg_ |= __m;
6262
else

libcxx/include/__fwd/bit_reference.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#define _LIBCPP___FWD_BIT_REFERENCE_H
1111

1212
#include <__config>
13+
#include <__type_traits/enable_if.h>
14+
#include <__type_traits/is_unsigned.h>
1315

1416
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
1517
# pragma GCC system_header
@@ -20,6 +22,22 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2022
template <class _Cp, bool _IsConst, typename _Cp::__storage_type = 0>
2123
class __bit_iterator;
2224

25+
template <class _StorageType, __enable_if_t<is_unsigned<_StorageType>::value, int> = 0>
26+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __leading_mask(unsigned __shift) {
27+
return static_cast<_StorageType>(static_cast<_StorageType>(~static_cast<_StorageType>(0)) << __shift);
28+
}
29+
30+
template <class _StorageType, __enable_if_t<is_unsigned<_StorageType>::value, int> = 0>
31+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __trailing_mask(unsigned __shift) {
32+
return static_cast<_StorageType>(static_cast<_StorageType>(~static_cast<_StorageType>(0)) >> __shift);
33+
}
34+
35+
template <class _StorageType, __enable_if_t<is_unsigned<_StorageType>::value, int> = 0>
36+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 _StorageType __middle_mask(unsigned __lshift, unsigned __rshift) {
37+
return static_cast<_StorageType>(
38+
std::__leading_mask<_StorageType>(__lshift) & std::__trailing_mask<_StorageType>(__rshift));
39+
}
40+
2341
_LIBCPP_END_NAMESPACE_STD
2442

2543
#endif // _LIBCPP___FWD_BIT_REFERENCE_H

libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill.pass.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include <cstddef>
2020
#include <vector>
2121

22+
#include "sized_allocator.h"
2223
#include "test_macros.h"
2324
#include "test_iterators.h"
2425

@@ -46,6 +47,37 @@ struct Test {
4647
}
4748
};
4849

50+
TEST_CONSTEXPR_CXX20 void test_bititer_with_custom_sized_types() {
51+
{
52+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
53+
std::vector<bool, Alloc> in(100, false, Alloc(1));
54+
std::vector<bool, Alloc> expected(100, true, Alloc(1));
55+
std::fill(in.begin(), in.end(), true);
56+
assert(in == expected);
57+
}
58+
{
59+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
60+
std::vector<bool, Alloc> in(200, false, Alloc(1));
61+
std::vector<bool, Alloc> expected(200, true, Alloc(1));
62+
std::fill(in.begin(), in.end(), true);
63+
assert(in == expected);
64+
}
65+
{
66+
using Alloc = sized_allocator<bool, std::uint32_t, std::int32_t>;
67+
std::vector<bool, Alloc> in(200, false, Alloc(1));
68+
std::vector<bool, Alloc> expected(200, true, Alloc(1));
69+
std::fill(in.begin(), in.end(), true);
70+
assert(in == expected);
71+
}
72+
{
73+
using Alloc = sized_allocator<bool, std::uint64_t, std::int64_t>;
74+
std::vector<bool, Alloc> in(200, false, Alloc(1));
75+
std::vector<bool, Alloc> expected(200, true, Alloc(1));
76+
std::fill(in.begin(), in.end(), true);
77+
assert(in == expected);
78+
}
79+
}
80+
4981
TEST_CONSTEXPR_CXX20 bool test() {
5082
types::for_each(types::forward_iterator_list<char*>(), Test<char>());
5183
types::for_each(types::forward_iterator_list<int*>(), Test<int>());
@@ -93,6 +125,9 @@ TEST_CONSTEXPR_CXX20 bool test() {
93125
assert(in == expected);
94126
}
95127
}
128+
129+
test_bititer_with_custom_sized_types();
130+
96131
return true;
97132
}
98133

libcxx/test/std/algorithms/alg.modifying.operations/alg.fill/fill_n.pass.cpp

Lines changed: 118 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -15,159 +15,175 @@
1515

1616
#include <algorithm>
1717
#include <cassert>
18+
#include <vector>
1819

20+
#include "sized_allocator.h"
1921
#include "test_macros.h"
2022
#include "test_iterators.h"
2123
#include "user_defined_integral.h"
2224

2325
#if TEST_STD_VER > 17
2426
TEST_CONSTEXPR bool test_constexpr() {
25-
const std::size_t N = 5;
26-
int ib[] = {0, 0, 0, 0, 0, 0}; // one bigger than N
27-
28-
auto it = std::fill_n(std::begin(ib), N, 5);
29-
return it == (std::begin(ib) + N)
30-
&& std::all_of(std::begin(ib), it, [](int a) {return a == 5; })
31-
&& *it == 0 // don't overwrite the last value in the output array
32-
;
33-
}
27+
const std::size_t N = 5;
28+
int ib[] = {0, 0, 0, 0, 0, 0}; // one bigger than N
29+
30+
auto it = std::fill_n(std::begin(ib), N, 5);
31+
return it == (std::begin(ib) + N) && std::all_of(std::begin(ib), it, [](int a) { return a == 5; }) &&
32+
*it == 0 // don't overwrite the last value in the output array
33+
;
34+
}
3435
#endif
3536

3637
typedef UserDefinedIntegral<unsigned> UDI;
3738

3839
template <class Iter>
39-
void
40-
test_char()
41-
{
42-
char a[4] = {};
43-
Iter it = std::fill_n(Iter(a), UDI(4), char(1));
44-
assert(base(it) == a + 4);
45-
assert(a[0] == 1);
46-
assert(a[1] == 1);
47-
assert(a[2] == 1);
48-
assert(a[3] == 1);
40+
void test_char() {
41+
char a[4] = {};
42+
Iter it = std::fill_n(Iter(a), UDI(4), char(1));
43+
assert(base(it) == a + 4);
44+
assert(a[0] == 1);
45+
assert(a[1] == 1);
46+
assert(a[2] == 1);
47+
assert(a[3] == 1);
4948
}
5049

5150
template <class Iter>
52-
void
53-
test_int()
54-
{
55-
int a[4] = {};
56-
Iter it = std::fill_n(Iter(a), UDI(4), 1);
57-
assert(base(it) == a + 4);
58-
assert(a[0] == 1);
59-
assert(a[1] == 1);
60-
assert(a[2] == 1);
61-
assert(a[3] == 1);
51+
void test_int() {
52+
int a[4] = {};
53+
Iter it = std::fill_n(Iter(a), UDI(4), 1);
54+
assert(base(it) == a + 4);
55+
assert(a[0] == 1);
56+
assert(a[1] == 1);
57+
assert(a[2] == 1);
58+
assert(a[3] == 1);
6259
}
6360

64-
void
65-
test_int_array()
66-
{
67-
int a[4] = {};
68-
assert(std::fill_n(a, UDI(4), static_cast<char>(1)) == a + 4);
69-
assert(a[0] == 1);
70-
assert(a[1] == 1);
71-
assert(a[2] == 1);
72-
assert(a[3] == 1);
61+
void test_int_array() {
62+
int a[4] = {};
63+
assert(std::fill_n(a, UDI(4), static_cast<char>(1)) == a + 4);
64+
assert(a[0] == 1);
65+
assert(a[1] == 1);
66+
assert(a[2] == 1);
67+
assert(a[3] == 1);
7368
}
7469

7570
struct source {
76-
source() : i(0) { }
71+
source() : i(0) {}
7772

78-
operator int() const { return i++; }
79-
mutable int i;
73+
operator int() const { return i++; }
74+
mutable int i;
8075
};
8176

82-
void
83-
test_int_array_struct_source()
84-
{
85-
int a[4] = {};
86-
assert(std::fill_n(a, UDI(4), source()) == a + 4);
87-
assert(a[0] == 0);
88-
assert(a[1] == 1);
89-
assert(a[2] == 2);
90-
assert(a[3] == 3);
77+
void test_int_array_struct_source() {
78+
int a[4] = {};
79+
assert(std::fill_n(a, UDI(4), source()) == a + 4);
80+
assert(a[0] == 0);
81+
assert(a[1] == 1);
82+
assert(a[2] == 2);
83+
assert(a[3] == 3);
9184
}
9285

9386
struct test1 {
94-
test1() : c(0) { }
95-
test1(char xc) : c(xc + 1) { }
96-
char c;
87+
test1() : c(0) {}
88+
test1(char xc) : c(xc + 1) {}
89+
char c;
9790
};
9891

99-
void
100-
test_struct_array()
101-
{
102-
test1 test1a[4] = {};
103-
assert(std::fill_n(test1a, UDI(4), static_cast<char>(10)) == test1a + 4);
104-
assert(test1a[0].c == 11);
105-
assert(test1a[1].c == 11);
106-
assert(test1a[2].c == 11);
107-
assert(test1a[3].c == 11);
92+
void test_struct_array() {
93+
test1 test1a[4] = {};
94+
assert(std::fill_n(test1a, UDI(4), static_cast<char>(10)) == test1a + 4);
95+
assert(test1a[0].c == 11);
96+
assert(test1a[1].c == 11);
97+
assert(test1a[2].c == 11);
98+
assert(test1a[3].c == 11);
10899
}
109100

110-
class A
111-
{
112-
char a_;
101+
class A {
102+
char a_;
103+
113104
public:
114-
A() {}
115-
explicit A(char a) : a_(a) {}
116-
operator unsigned char() const {return 'b';}
105+
A() {}
106+
explicit A(char a) : a_(a) {}
107+
operator unsigned char() const { return 'b'; }
117108

118-
friend bool operator==(const A& x, const A& y)
119-
{return x.a_ == y.a_;}
109+
friend bool operator==(const A& x, const A& y) { return x.a_ == y.a_; }
120110
};
121111

122-
void
123-
test5()
124-
{
125-
A a[3];
126-
assert(std::fill_n(&a[0], UDI(3), A('a')) == a+3);
127-
assert(a[0] == A('a'));
128-
assert(a[1] == A('a'));
129-
assert(a[2] == A('a'));
112+
void test5() {
113+
A a[3];
114+
assert(std::fill_n(&a[0], UDI(3), A('a')) == a + 3);
115+
assert(a[0] == A('a'));
116+
assert(a[1] == A('a'));
117+
assert(a[2] == A('a'));
130118
}
131119

132-
struct Storage
133-
{
134-
union
135-
{
120+
struct Storage {
121+
union {
136122
unsigned char a;
137123
unsigned char b;
138124
};
139125
};
140126

141-
void test6()
142-
{
127+
void test6() {
143128
Storage foo[5];
144129
std::fill_n(&foo[0], UDI(5), Storage());
145130
}
146131

132+
TEST_CONSTEXPR_CXX20 void test_bititer_with_custom_sized_types() {
133+
{
134+
using Alloc = sized_allocator<bool, std::uint8_t, std::int8_t>;
135+
std::vector<bool, Alloc> in(100, false, Alloc(1));
136+
std::vector<bool, Alloc> expected(100, true, Alloc(1));
137+
std::fill_n(in.begin(), in.size(), true);
138+
assert(in == expected);
139+
}
140+
{
141+
using Alloc = sized_allocator<bool, std::uint16_t, std::int16_t>;
142+
std::vector<bool, Alloc> in(200, false, Alloc(1));
143+
std::vector<bool, Alloc> expected(200, true, Alloc(1));
144+
std::fill_n(in.begin(), in.size(), true);
145+
assert(in == expected);
146+
}
147+
{
148+
using Alloc = sized_allocator<bool, std::uint32_t, std::int32_t>;
149+
std::vector<bool, Alloc> in(200, false, Alloc(1));
150+
std::vector<bool, Alloc> expected(200, true, Alloc(1));
151+
std::fill_n(in.begin(), in.size(), true);
152+
assert(in == expected);
153+
}
154+
{
155+
using Alloc = sized_allocator<bool, std::uint64_t, std::int64_t>;
156+
std::vector<bool, Alloc> in(200, false, Alloc(1));
157+
std::vector<bool, Alloc> expected(200, true, Alloc(1));
158+
std::fill_n(in.begin(), in.size(), true);
159+
assert(in == expected);
160+
}
161+
}
162+
163+
int main(int, char**) {
164+
test_char<cpp17_output_iterator<char*> >();
165+
test_char<forward_iterator<char*> >();
166+
test_char<bidirectional_iterator<char*> >();
167+
test_char<random_access_iterator<char*> >();
168+
test_char<char*>();
147169

148-
int main(int, char**)
149-
{
150-
test_char<cpp17_output_iterator<char*> >();
151-
test_char<forward_iterator<char*> >();
152-
test_char<bidirectional_iterator<char*> >();
153-
test_char<random_access_iterator<char*> >();
154-
test_char<char*>();
170+
test_int<cpp17_output_iterator<int*> >();
171+
test_int<forward_iterator<int*> >();
172+
test_int<bidirectional_iterator<int*> >();
173+
test_int<random_access_iterator<int*> >();
174+
test_int<int*>();
155175

156-
test_int<cpp17_output_iterator<int*> >();
157-
test_int<forward_iterator<int*> >();
158-
test_int<bidirectional_iterator<int*> >();
159-
test_int<random_access_iterator<int*> >();
160-
test_int<int*>();
176+
test_int_array();
177+
test_int_array_struct_source();
178+
test_struct_array();
161179

162-
test_int_array();
163-
test_int_array_struct_source();
164-
test_struct_array();
180+
test5();
181+
test6();
165182

166-
test5();
167-
test6();
183+
test_bititer_with_custom_sized_types();
168184

169185
#if TEST_STD_VER > 17
170-
static_assert(test_constexpr());
186+
static_assert(test_constexpr());
171187
#endif
172188

173189
return 0;

0 commit comments

Comments
 (0)