Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Is std::vector<std::aligned_storage> ill-formed?

address sanitizer goes crazy when I try to manipulate with std::vector<std::aligned_storage<sizeof(T), alignof(T)>> vec:

  1. Sometimes placement new ::new(&vec[some_index]) T(std::forward<Args>(args)...) at the element vec[some_index] of the vector vec works well, unless T is a small std::string
  2. std::destroy_at(std::launder(reinterpret_cast<T *>(&vec[some_index]))) doesn’t work at all — and also, as cppreference says: "If T is an array type, the program is ill-formed" — while cppreference example on std::aligned_storage uses std::destroy_at at instances of std::aligned_storage finely.

Following example is provided:

#include <string>
#include <type_traits>
#include <vector>
#include <new>
#include <memory>

int main() {
    std::vector<std::aligned_storage<sizeof(std::string), alignof(std::string)>>
        vec(1);
    
    ::new(&vec[0]) std::string(334, 'a');  // Fine
    std::destroy_at(std::launder(reinterpret_cast<std::string *>(&vec[0])));  // Still fine

    ::new(&vec[0]) std::string(334, 'a');  // Okay
    std::destroy_at(std::launder(reinterpret_cast<std::string *>(&vec[0])));  // Still okay

    ::new(&vec[0]) std::string(3, 'a');  // Cool
    std::destroy_at(std::launder(reinterpret_cast<std::string *>(&vec[0])));  // Breaks the code at previous line
}

asan output is following:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

==22152==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x602000000020 at pc 0x7fa1a8c27f26 bp 0x7fff54d86410 sp 0x7fff54d85bb8
WRITE of size 3 at 0x602000000020 thread T0
    #0 0x7fa1a8c27f25 in __interceptor_memset ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:795
    #1 0x7fa1a8b4ef56 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_construct(unsigned long, char) (/lib/x86_64-linux-gnu/libstdc++.so.6+0x142f56)
    #2 0x557bafbcb8a5 in std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string<std::allocator<char> >(unsigned long, char, std::allocator<char> const&) /usr/include/c++/10/bits/basic_string.h:542
    #3 0x557bafbcb613 in main testing.cpp:14
    #4 0x7fa1a86d4082 in __libc_start_main ../csu/libc-start.c:308
    #5 0x557bafbcb34d in _start (/home/valery/repos/effective-cpp/tasks/a.out+0x234d)

0x602000000020 is located 15 bytes to the right of 1-byte region [0x602000000010,0x602000000011)
allocated by thread T0 here:
    #0 0x7fa1a8c9ff27 in operator new(unsigned long) ../../../../src/libsanitizer/asan/asan_new_delete.cpp:99
    #1 0x557bafbcc532 in __gnu_cxx::new_allocator<std::aligned_storage<32ul, 8ul> >::allocate(unsigned long, void const*) /usr/include/c++/10/ext/new_allocator.h:115
    #2 0x557bafbcc39d in std::allocator_traits<std::allocator<std::aligned_storage<32ul, 8ul> > >::allocate(std::allocator<std::aligned_storage<32ul, 8ul> >&, unsigned long) /usr/include/c++/10/bits/alloc_traits.h:460
    #3 0x557bafbcc2d7 in std::_Vector_base<std::aligned_storage<32ul, 8ul>, std::allocator<std::aligned_storage<32ul, 8ul> > >::_M_allocate(unsigned long) /usr/include/c++/10/bits/stl_vector.h:346
    #4 0x557bafbcc034 in std::_Vector_base<std::aligned_storage<32ul, 8ul>, std::allocator<std::aligned_storage<32ul, 8ul> > >::_M_create_storage(unsigned long) /usr/include/c++/10/bits/stl_vector.h:361
    #5 0x557bafbcbc70 in std::_Vector_base<std::aligned_storage<32ul, 8ul>, std::allocator<std::aligned_storage<32ul, 8ul> > >::_Vector_base(unsigned long, std::allocator<std::aligned_storage<32ul, 8ul> > const&) /usr/include/c++/10/bits/stl_vector.h:305
    #6 0x557bafbcb952 in std::vector<std::aligned_storage<32ul, 8ul>, std::allocator<std::aligned_storage<32ul, 8ul> > >::vector(unsigned long, std::allocator<std::aligned_storage<32ul, 8ul> > const&) /usr/include/c++/10/bits/stl_vector.h:511
    #7 0x557bafbcb4e8 in main testing.cpp:9
    #8 0x7fa1a86d4082 in __libc_start_main ../csu/libc-start.c:308

SUMMARY: AddressSanitizer: heap-buffer-overflow ../../../../src/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc:795 in __interceptor_memset
Shadow bytes around the buggy address:
  0x0c047fff7fb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7fe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
  0x0c047fff7ff0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x0c047fff8000: fa fa 01 fa[fa]fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8010: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8020: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8030: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8040: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
  0x0c047fff8050: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable:           00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone:       fa
  Freed heap region:       fd
  Stack left redzone:      f1
  Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone:          f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone:    bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
  Shadow gap:              cc
==22152==ABORTING

My setup is g++-10/clang++-12 on x86_64 Ubuntu 20.04.

I couldn’t find any information on std::aligned_storage, maybe because it’s deprecated.

>Solution :

The problem, as written above, is that std::vector<std::aligned_storage<sizeof(T),alignof(T)>> is incorrect.

std::aligned_storage<size,size> has a nested typedef type that has the properties you want.

What you want is std::vector<std::aligned_storage<sizeof(T),alignof(T)>::type>

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading