摘自Björn Fahller在2023年CPP会议上的演讲。=> youtu.be/ LKKmPAQFNgE
它是关于如何在不触及new
甚至malloc
的情况下强制c++泄漏内存。
struct V : vector<V> {};
auto v = V{};
v.emplace_back();
v.swap(v.front()); // v representation becomes uint64_t[3]{ 0x0, 0x0, 0x0},
// so the vector allocation gets lost because no refs are left on the scope.
字符串
所以我想知道我能不能手动摧毁它。
struct V : vector<V> {};
auto v = V{};
v.emplace_back();
v.emplace_back();
v.emplace_back();
v.emplace_back();
auto front = v.front();
v.swap(v.front());
using allocator = std::allocator<V>;
using atraits = std::allocator_traits<allocator>;
auto a = front.get_allocator();
atraits::destroy(a, &front + 1); // Ok
atraits::destroy(a, &front + 2); // Ok
atraits::destroy(a, &front + 3); // Ok
// atraits::destroy(a, &front); // error SIGSEGV
atraits::deallocate(a, &front, 4); // still seems Ok?
型SIGSEGV
在试图销毁拥有自己地址的V对象时发生。
0x1796320 : 0x1796320 (alloc_begin_ptr) // It owns itself!!!
0x1796328 : 0x1796380 (one_pass_content_end_ptr)
0x1796330 : 0x1796380 (one_pass_alloc_end_ptr)
0x1796338 : 0x0 (alloc_begin_ptr)
0x1796340 : 0x0 (one_pass_content_end_ptr)
0x1796348 : 0x0 (one_pass_alloc_end_ptr)
0x1796350 : 0x0 (alloc_begin_ptr)
0x1796358 : 0x0 (one_pass_content_end_ptr)
0x1796360 : 0x0 (one_pass_alloc_end_ptr)
0x1796368 : 0x0 (alloc_begin_ptr)
0x1796370 : 0x0 (one_pass_content_end_ptr)
0x1796378 : 0x0 (one_pass_alloc_end_ptr)
型
所以我试着把它移到堆栈。它似乎工作正常。
struct V : vector<V> {};
auto v = V{};
v.emplace_back();
v.emplace_back();
v.emplace_back();
v.emplace_back();
auto front = v.front();
v.swap(v.front());
auto v2 = std::move(front);
型
没有任何对象拥有自己。
0x7ffc44d02b20 : 0x927320 (alloc_begin_ptr) // v2 on stack
0x7ffc44d02b28 : 0x927380 (one_pass_content_end_ptr)
0x7ffc44d02b30 : 0x927380 (one_pass_alloc_end_ptr)
0x927320 : 0x0 (alloc_begin_ptr)
0x927328 : 0x0 (one_pass_content_end_ptr)
0x927330 : 0x0 (one_pass_alloc_end_ptr)
0x927338 : 0x0 (alloc_begin_ptr)
0x927340 : 0x0 (one_pass_content_end_ptr)
0x927348 : 0x0 (one_pass_alloc_end_ptr)
0x927350 : 0x0 (alloc_begin_ptr)
0x927358 : 0x0 (one_pass_content_end_ptr)
0x927360 : 0x0 (one_pass_alloc_end_ptr)
0x927368 : 0x0 (alloc_begin_ptr)
0x927370 : 0x0 (one_pass_content_end_ptr)
0x927378 : 0x0 (one_pass_alloc_end_ptr)
型
为什么拥有自身的vector
上的allocator_traits::destroy()
会触发SIGSEGV
?
// atraits::destroy(a, &front); // error SIGSEGV
0x1796320 : 0x1796320 (alloc_begin_ptr) // It owns itself!!!
0x1796328 : 0x1796380 (one_pass_content_end_ptr)
0x1796330 : 0x1796380 (one_pass_alloc_end_ptr)
型
[LIVE]
1条答案
按热度按时间zour9fqk1#
(我假设这条线:
字符串
这是一个输入错误,因为
v.front()
是一个默认构造的V
对象,您可以从中复制。这意味着该行本质上是auto front = V{}
。您的意思是auto& front = v.front()
)&front + 1
、&front + 2
和&front + 3
是指向空向量的指针,它们可以被销毁。如果你试图销毁
&front
指向的东西,这是一个有4个元素的向量。最后三个元素是那些空向量,它们被销毁没有问题。但是
front
的第一个元素是front
本身。这是一个未定义的行为,因为你会在一个已经被销毁的对象上调用析构函数,但实际上它会导致一个无限循环,因为它只会再次调用析构函数并递归地试图销毁同一个向量(以及导致segfault的堆栈溢出)。如果你只是简单地释放它,没有析构函数被调用,所以没有无限循环。如果vector包含其他分配内存的vector,你可能会泄漏内存,因为这些vector的析构函数也不会被调用。
正如您已经尝试过的,“修复”这个问题的方法是打破“所有权”循环,比如将其移动到一个新的向量(
V{} = std::move(front)
/V{}.swap(front)
)。