Search code examples
c++c++20address-sanitizerstdsetdangling-pointer

Is it possible to extract a struct containing fields that are unique/shared pointers from a set


so essentially I have a set of instances of struct A. I want to extract an instance, modify the fields. One of the fields is a unique ptr. I'm not that great at reading c++ errors, it looks like the field is deleted on extraction. I.e. the destructor of the unique pointer is called.

Example: Here when I try to access the field 'weight'

I get something along the lines of:

SUMMARY: AddressSanitizer: heap-use-after-free /usr/lib/llvm-11/include/c++/v1/memory:2490:19 in std::__1::unique_ptr<int, std::__1::default_delete<int> >::get() const
[ctest] Shadow bytes around the buggy address:
[ctest]   0x0c0e7fff84a0: fd fd fd fd fd fa fa fa fa fa fd fd fd fd fd fd
[ctest]   0x0c0e7fff84b0: fd fd fd fa fa fa fa fa fd fd fd fd fd fd fd fd
[ctest]   0x0c0e7fff84c0: fd fa fa fa fa fa fd fd fd fd fd fd fd fd fd fa
[ctest]   0x0c0e7fff84d0: fa fa fa fa 00 00 00 00 00 00 00 00 00 fa fa fa
[ctest]   0x0c0e7fff84e0: fa fa 00 00 00 00 00 00 00 00 00 fa fa fa fa fa
[ctest] =>0x0c0e7fff84f0: fd fd fd fd fd fd fd fd[fd]fa fa fa fa fa 00 00
[ctest]   0x0c0e7fff8500: 00 00 00 00 00 00 00 fa fa fa fa fa fa fa fa fa
[ctest]   0x0c0e7fff8510: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
[ctest]   0x0c0e7fff8520: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
[ctest]   0x0c0e7fff8530: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
[ctest]   0x0c0e7fff8540: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
[ctest] Shadow byte legend (one shadow byte represents 8 application bytes):
[ctest]   Addressable:           00
[ctest]   Partially addressable: 01 02 03 04 05 06 07 
[ctest]   Heap left redzone:       fa
[ctest]   Freed heap region:       fd
[ctest]   Stack left redzone:      f1
[ctest]   Stack mid redzone:       f2
[ctest]   Stack right redzone:     f3
[ctest]   Stack after return:      f5
[ctest]   Stack use after scope:   f8
[ctest]   Global redzone:          f9
[ctest]   Global init order:       f6
[ctest]   Poisoned by user:        f7
[ctest]   Container overflow:      fc
[ctest]   Array cookie:            ac
[ctest]   Intra object redzone:    bb
[ctest]   ASan internal:           fe
[ctest]   Left alloca redzone:     ca
[ctest]   Right alloca redzone:    cb
[ctest]   Shadow gap:              cc
Struct A {
    std::unique_ptr<int> weight;
}

auto& element = set_.extract(set_iterator).value();
std::cout << *(element.weight);

Solution

  • The node is not deleted on extraction, but shortly after, before you use it.

    Specifically, you don't save the node-handle .extract() returns, but directly get a reference to part of the node it manages.
    At the end of the full statement, that reference becomes dangling, as nobody keeps the node-handle owning it alive.

    Thus, the AddressSanitizer detects heap-use-after-free.

    Store the node-handle, or save the part of the node you want, but avoid dangling references.