[libc++] Memory leak when assigning an initializer_list containing a duplicate key to a non-empty std::unordered_set
- Dominant language
- LLVM
- Stars
- 40.5k
- Forks
- 18.7k
- PR merge metrics
- PR metrics pending
Description
Assigning a `std::initializer_list` that contains a duplicate key to a non-empty `std::unordered_set` leaks.
### Minimal reproducer (AddressSanitizer):
https://godbolt.org/z/zE1sKjfxE
```c++
#include
int main()
{
std::unordered_set my_set = {2, 1};
my_set = {0, 0};
return 0;
}
```
Compiled with `-stdlib=libc++ -fsanitize=address`:
```
==1==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 24 byte(s) in 1 object(s) allocated from:
...
SUMMARY: AddressSanitizer: 24 byte(s) leaked in 1 allocation(s).
```
### Reproducer with a counting allocator (no sanitizer required)
https://godbolt.org/z/vGMbnfbsM
This variant shows that the leaked node was obtained from the user-provided allocator's `allocate()` and that no matching `deallocate()` is ever called for it, even after the container has been destroyed:
```c++
#include
#include
#include
static long g_live = 0;
template
struct CountingAllocator
{
using value_type = T;
CountingAllocator() = default;
template CountingAllocator(const CountingAllocator&) {}
T* allocate(std::size_t n) { ++g_live; return static_cast(::operator new(n * sizeof(T))); }
void deallocate(T* p, std::size_t) { --g_live; ::operator delete(p); }
template bool operator==(const CountingAllocator&) const { return true; }
template bool operator!=(const CountingAllocator&) const { return false; }
};
using Set = std::unordered_set, std::equal_to, CountingAllocator>;
int main()
{
{
Set my_set = {2, 1};
my_set = {0, 0};
}
std::cout << "live allocations after destruction: " << g_live << '\n';
return g_live != 0; // libc++: 1, libstdc++: 0
}
```
Compiled with `-stdlib=libc++`:
```
ASM generation compiler returned: 0
Execution build compiler returned: 0
Program returned: 1
live allocations after destruction: 1
```
Contributor guide
Research direction
Start by running the AddressSanitizer and counting-allocator reproducers linked in the issue, then trace libc++'s std::unordered_set initializer_list assignment path. Confirm which allocation is left outstanding when duplicate keys are assigned to a non-empty set, and verify that the allocation is released after assignment and destruction without introducing regressions.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- cpp
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 48/100