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
}
相关推荐
小侯不躺平.3 小时前
C++ Boost库【4】 --分词器的使用
c++·windows·microsoft
码农-阿杰4 小时前
Java 线程中断机制深度解析:从 API 到底层 C++ 实现
java·开发语言·c++
Brilliantwxx4 小时前
【C++】priority_queue以及 仿函数 的学习
开发语言·c++·笔记·学习·算法
宠..4 小时前
VS Code 修改 C++ 标准同时修改错误检测标准
java·linux·开发语言·javascript·c++·python·qt
wuyoula4 小时前
如何在捷云鲸论坛高效获取高质量技术解答?
服务器·c++·人工智能·tcp/ip·源码
.YM.Z4 小时前
C++类和对象(中)
c++·类和对象
Tairitsu_H4 小时前
C++:优先队列的模拟实现
开发语言·c++·stl·优先队列
lzh200409194 小时前
效率与安全并重:C++ 线程安全
linux·c++
Shan12055 小时前
RAII妙用:使用标准库的包装器
开发语言·c++
Hua-Jay5 小时前
OpenCV联合C++/Qt 学习笔记(十八)----二维码检测及积分图像
c++·笔记·qt·opencv·学习