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
}
相关推荐
卷无止境1 天前
C++ 的Eigen 库全解析
c++
卷无止境1 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端
郝学胜_神的一滴1 天前
CMake 27:缓存变量的特性、语法、类型与实操全解
c++·cmake
博客18003 天前
酷宝的使用方法,超好用的免费界面库,C++、MFC可用
c++·mfc·界面库·库来帮·酷宝
郝学胜_神的一滴3 天前
CMake 026:属性体系精讲、四大作用域全解 & 实战代码落地
c++·cmake
众少成多积小致巨4 天前
JNI (Java Native Interface) 技术手册中文参考指南
android·java·c++
clint4568 天前
C++进阶(1)——前景提要
c++
夜悊8 天前
C++代码示例:进制数简单生成工具
c++
郝学胜_神的一滴8 天前
CMake 021: IF 条件判据详诠
c++·cmake
_wyt0019 天前
洛谷 B3930 [GESP202312 五级] 烹饪问题 题解
c++·gesp