清理向量时出现我无法理解的分段错误。删除数组访问(从底部第 3 行)可以消除崩溃,即使崩溃不在该行上。
下面的代码是我最终得到的最小的重现,是从更大的代码库中提取的(我确认它在 dtor 中崩溃,而不是在数组写入时崩溃)。
#include <array>
#include <cstddef>
#include <iostream>
#include <memory>
#include <vector>
class Deletable {
public:
virtual ~Deletable() = default;
};
template <typename V> class DeletableHandle : public Deletable {
public:
virtual V *get() = 0;
virtual const V *get() const = 0;
};
template <typename V> class DeletableHolder final : public DeletableHandle<V> {
public:
V *get() override { return &value_; }
const V *get() const override { return &value_; }
private:
V value_;
};
class InferenceContext2 {
public:
template <size_t LI> auto *LayerScratchSpace() {
using Area = std::array<int, 4>;
while (buffers_.size() <= LI) {
buffers_.emplace_back(nullptr);
}
if (buffers_[LI] == nullptr) {
buffers_[LI] = std::make_unique<DeletableHolder<Area>>();
}
return reinterpret_cast<Area *>(buffers_[LI].get());
}
private:
std::vector<std::unique_ptr<Deletable>> buffers_;
};
int main() {
InferenceContext2 ctx;
auto area = ctx.LayerScratchSpace<0>();
std::cout << area->size() << " " << (reinterpret_cast<intptr_t>(&area) % 128)
<< " " << alignof(std::remove_pointer_t<decltype(area)>) << "\n";
// Commenting out the line below removes the crash
(*area)[0] = 1;
return 0;
}
在我更大的项目中,我有 ASAN,这就是它的意思(堆栈跟踪不同):
AddressSanitizer:DEADLYSIGNAL
=================================================================
==12==ERROR: AddressSanitizer: SEGV on unknown address 0x55f800000009 (pc 0x55f85eadc1c1 bp 0x7ffd295108b0 sp 0x7ffd29510890 T0)
==12==The signal is caused by a READ memory access.
#0 0x55f85eadc1c1 in std::default_delete<uchen::memory::Deletable>::operator()(uchen::memory::Deletable*) const /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/unique_ptr.h:85:2
#1 0x55f85eadc0fb in std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>::~unique_ptr() /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/unique_ptr.h:361:4
#2 0x55f85eadc064 in void std::destroy_at<std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>>(std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>*) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_construct.h:88:15
#3 0x55f85eadd214 in void std::_Destroy<std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>>(std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>*) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_construct.h:149:7
#4 0x55f85eadd1e6 in void std::_Destroy_aux<false>::__destroy<std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>*>(std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>*, std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>*) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_construct.h:163:6
#5 0x55f85eadd1ac in void std::_Destroy<std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>*>(std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>*, std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>*) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_construct.h:195:7
#6 0x55f85eadd0d0 in void std::_Destroy<std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>*, std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>>(std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>*, std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>*, std::allocator<std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>>&) /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/alloc_traits.h:848:7
#7 0x55f85eadd09e in std::vector<std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>, std::allocator<std::unique_ptr<uchen::memory::Deletable, std::default_delete<uchen::memory::Deletable>>>>::~vector() /usr/lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/stl_vector.h:680:2
#8 0x55f85ead8d24 in uchen::(anonymous namespace)::InferenceContext2::~InferenceContext2() /proc/self/cwd/test/arena.test.cc:71:7
使用 GCC 和 Clang 进行复制。使用
--std=c++20
进行构建。编译器版本:
eugene-aurora:~/code$ g++ --version
g++ (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
Copyright (C) 2021 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
eugene-aurora:~/code$ clang++ --version
Ubuntu clang version 15.0.7
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
buffers_[LI].get()
返回 DeletableHolder
的地址。buffers_[LI]->get()
在 get()
上调用 DeletableHolder
,这确实是一个 Area *
。
在消除编译器警告之前请三思。