Skip to content

Commit c7b7b04

Browse files
committed
[libc++] Fix __hash_table::erase(iterator, iterator) to update the bucket list correctly when erasing the last bucket
1 parent 478e45f commit c7b7b04

File tree

2 files changed

+21
-0
lines changed

2 files changed

+21
-0
lines changed

libcxx/include/__hash_table

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1910,6 +1910,8 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::erase(const_iterator __first, const_it
19101910
__bucket_list_[__next_chash] = __before_first;
19111911
__chash = __next_chash;
19121912
}
1913+
} else { // When __next is a nullptr we've fully erased the last bucket. Update the bucket list accordingly.
1914+
__bucket_list_[__chash] = nullptr;
19131915
}
19141916
}
19151917

libcxx/test/std/containers/unord/unord.multiset/erase_range.pass.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,25 @@ int main(int, char**) {
6464
for (const auto& v : map)
6565
assert(v == 1 || v == collision_val);
6666
}
67+
{ // Make sure that we're properly updating the bucket list when we're erasing to the end
68+
std::unordered_multiset<int> m;
69+
m.emplace(1);
70+
m.emplace(2);
71+
72+
{
73+
auto [it, end] = m.equal_range(1);
74+
assert(it != end);
75+
m.erase(it, end);
76+
}
77+
78+
{
79+
auto [it, end] = m.equal_range(2);
80+
assert(it != end);
81+
m.erase(it, end);
82+
}
83+
84+
m.emplace(3); // heap-use-after-free
85+
}
6786
#if TEST_STD_VER >= 11
6887
{
6988
typedef std::unordered_multiset<int, std::hash<int>, std::equal_to<int>, min_allocator<int>> C;

0 commit comments

Comments
 (0)