llvm / llvm/llvm-project

[libc++] Memory leak when assigning an initializer_list containing a duplicate key to a non-empty std::unordered_set

Open
#216,277 3 comments 0 reactions 0 assignees View on GitHub
libc++
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.