std::function 在析构阶段触发非法内存访问

Bug Report: std::function 在析构阶段触发非法内存访问(UAF)

问题描述

在当前事件回调设计中,父类中定义了一个 std::function<> 类型的回调成员(例如 onHover),子类在构造阶段为其赋值,并在 lambda 中捕获了子类的成员(例如 innerC4Bthis 指针)。

在对象析构阶段,程序偶现崩溃。调用栈表面上显示为:

  • 崩溃发生在 std::function 相关代码
  • 或发生在 lambda 调用过程中
  • 或发生在 std::function 析构期间

但实际根因并非 std::function 本身,而是 析构顺序导致的悬空访问(Use-After-Free)问题


现象

  • 在销毁派生类对象时偶现崩溃
  • 崩溃位置可能显示为:
    • std::function::operator()
    • lambda 内部
    • std::_Func_impl
    • 或 STL 内部函数
  • AddressSanitizer 可能报错:
    • heap-use-after-free
    • stack-use-after-scope
    • invalid read

根因分析

1️⃣ 对象析构顺序

C++ 析构顺序如下:

  1. 执行派生类析构函数体
  2. 析构派生类成员
  3. 执行基类析构函数
  4. 析构基类成员

因此:

  • 子类成员(例如 innerC4B会先被销毁
  • 父类中的 std::function onHover 会更晚才销毁

2️⃣ 危险模式

当出现如下结构时会产生隐患:

cpp 复制代码
struct Base {
    std::function<void()> onHover;
};

struct Derived : Base {
    Inner innerC4B;

    Derived() {
        onHover = [this]() {
            innerC4B.use();
        };
    }
};

最小复现伪代码

cpp 复制代码
#include <functional>
#include <vector>
#include <iostream>

struct EventBus {
    std::vector<std::function<void()>> listeners;

    void add(std::function<void()> cb) {
        listeners.push_back(std::move(cb));
    }

    void removeAll_and_fire() {
        // 模拟析构期间错误触发
        for (auto& cb : listeners) {
            cb();  // <-- 此时可能访问已析构成员
        }
        listeners.clear();
    }
};

struct Base {
    std::function<void()> onHover;
    EventBus* bus = nullptr;

    virtual ~Base() {
        if (bus) {
            bus->removeAll_and_fire();
        }
    }
};

struct Derived : Base {
    struct Inner {
        int value = 42;
        void use() { std::cout << value << std::endl; }
        ~Inner() { std::cout << "~Inner\n"; }
    };

    Inner innerC4B;

    Derived(EventBus& b) {
        bus = &b;

        onHover = [this]() {
            innerC4B.use();  // <-- innerC4B 可能已析构
        };

        bus->add(onHover);
    }

    ~Derived() override {
        std::cout << "~Derived\n";
    }
};

int main() {
    EventBus bus;
    {
        Derived d(bus);
    } // 析构顺序:~Derived -> ~Inner -> ~Base(触发回调) -> UAF
}
相关推荐
程序喵大人6 分钟前
源码剖析:iostream 的缓冲区设计
开发语言·c++·iostream
liu****7 分钟前
4.哈希扩展
c++·算法·哈希算法·位图·bitset
70asunflower8 分钟前
CUDA基础知识巩固检验练习题【附有参考答案】(6)
c++·人工智能·cuda
仰泳的熊猫10 分钟前
题目1882:蓝桥杯2017年第八届真题-k倍区间
数据结构·c++·算法·蓝桥杯
Mikowoo00710 分钟前
Visual Studio 2022 下CUDA程序开发
c++·visual studio
Darkwanderor11 分钟前
图论——拓扑排序和图上DP
c++·算法·动态规划·图论·拓扑排序
hetao173383713 分钟前
2026-03-04~03-06 hetao1733837 的刷题记录
c++·算法
xiaoye-duck15 分钟前
《算法题讲解指南:优选算法-分治-归并》--47.归并排序,48.数组中的逆序对
c++·算法
Darkwanderor16 分钟前
图论——最短路问题
c++·算法·图论·最短路
小小unicorn18 分钟前
[微服务即时通讯系统]语音子服务的实现与测试
c++·算法·微服务·云原生·架构·xcode