You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用数组时析构函数出现Segmentation Error问题求助

清理vector时触发Segmentation Error,移除数组访问代码则崩溃消失

问题现象

清理vector时出现无法解释的段错误,崩溃发生在析构函数阶段而非数组访问行,但移除数组访问代码(倒数第三行)就能消除崩溃。以下是从大型代码库提取的最小复现代码,已确认崩溃发生在析构函数而非数组写入操作时:

#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

复现环境

  • 编译参数:--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

问题分析

崩溃的核心原因是错误的类型转换导致内存破坏:
在LayerScratchSpace函数中,直接将DeletableHolder<Area>*通过reinterpret_cast强制转换为Area*返回。但DeletableHolder<Area>的内存布局是:基类Deletable的虚表指针 + 成员value_(std::array<int,4>)。把DeletableHolder对象的指针当成Area指针使用,会导致访问的是基类虚表指针区域而非真正的value_成员。

执行(*area)[0] = 1;时,实际修改了DeletableHolder对象的虚表指针内存,后续析构时,unique_ptr尝试调用虚析构函数时,访问被篡改的虚表指针,触发段错误。注释掉该行后,虚表指针未被破坏,析构就能正常进行。

解决方案

修复类型转换逻辑,正确获取value_的地址:
修改LayerScratchSpace函数,先将buffers_[LI].get()转换为DeletableHandle<Area>*,再调用get()方法获取真正的Area*,避免直接使用reinterpret_cast:

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>>();
  }
  // 先转换为正确的派生类指针,再调用get()获取Area对象地址
  auto handle = static_cast<DeletableHandle<Area>*>(buffers_[LI].get());
  return handle->get();
}

这样就能保证返回的是DeletableHolder中value_的正确地址,写入操作不会破坏虚表指针,析构时也能正常执行。


内容的提问来源于stack exchange,提问作者Eugene

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 11:59:50