MEM56-CPP. 不要在不相关的智能指针中存放一个已有所属的指针值
原文链接:
MEM56-CPP. Do not store an already-owned pointer value in an unrelated smart pointer
类似 std::unique_ptr
和 std::shared_ptr
的智能指针将指针所有权语义编码为类型系统的一部分. 它们包裹指针值, 通过 opereator *()
和 operator->()
成员函数提供类似指针语义, 并且控制他们所管理指针的生命周期. 当从一个指针值构造出一个智能指针时, 称这个值被这个智能指针所拥有.
调用 std::unique_ptr::release()
将释放所管理指针值的所有权. std::unique_ptr
对象的析构, 移动赋值及对其调用 std::unique_ptr::reset()
也将会释放所管理指针值的所有权, 但是会导致所管理指针值析构. 如果调用 std::shared_ptr::unique()
返回 true, 那么 std::shared_ptr
的析构或对其调用 std::shared_ptr::reset()
将释放所管理的指针值, 但是会导致所管理指针值的析构.
有些智能指针, 比如 std::shared_ptr
, 允许多个智能指针对象来管理同一个指针值. 在这类情况中, 最初的智能指针对象拥有该指针值, 其他后续的智能指针对象与最初的指针指针关联. 举个例子, 将一个 std::shared_ptr
对象通过拷贝赋值拷贝到另一个 std::shared_ptr
对象中, 这将创建两个智能指针间的联系, 然而, 从被另一个 std::shared_ptr
对象所属的指针值创建一个 std::shared_ptr
对象并不会 (注: 产生关联).
不要从被另一个指针指针对象所属的指针值创建一个不相关的智能指针对象, 包括将智能指针所管理的指针重置为已有所属的指针值, 比如通过调用 reset()
.
不合规代码示例
在这个不合规代码示例中, 两个不相关的指针指针从同一个指针值从构造出. 当局部自动变量 p2
被销毁时, 它会删除它所管理的指针值. 然后, 当局部自动变量 p1
被销毁时, 它也会删除同一个指针值, 导致二次释放 漏洞, vulnerability.
1 |
|
合规方案
在这个合规方案中, std::shared_ptr
通过拷贝构造和另一个关联. 当局部自动变量 p2
被销毁时, 共享指针的引用计数将递减, 但是依然不为零. 然后, 当局部自动变量 p1
被销毁时, 共享指针的引用计数将递减为零, 然后所管理的指针被销毁. 这个合规方案也通过调用 std::make_shared()
来代替分配一个裸指针且将其存储在局部变量中的操作.
1 |
|
不合规代码示例
在这个不合规代码示例中, 被一个 std::shared_ptr
对象所属的指针值 poly
通过 dynamic_cast
转换到了指针类型 D *
, 来尝试获得一个多态派生类型的 std::shared_ptr
. 然而, 这最终导致 未定义行为, undefined behavior , 因为同一个指针被存储在两个不同的 std::shared_ptr
对象中. 当 g()
退出时, 存储在 derived
的指针被默认删除器释放. 后续使用 poly
造成访问已经释放的内存. 当 f()
退出时, 存储在 poly
的指针被销毁, 造成二次释放漏洞.
1 |
|
合规方案
在这个合规方案中, dynamic_cast
被替换为调用 std::dynamic_pointer_cast()
– 将返回一个含有有效共享指针值的多态类型的 std::shared_ptr
. 当 g()
退出时, 指针 (注: B) 的引用计数随着 derived
的析构而递减. 但是由于由 poly
(在 f()
中) 持有(注: D) 的引用, 存储的指针值在 g()
退出时依然有效.
1 |
|
不合规代码示例
在这个不合规代码示例中, 构造了一个类型 S
的 std::shared_ptr
, 存储于 s1
中. 调用 S::g()
来获取另一个值被 s1
管理的共享指针. 然而, 由 S::g()
返回的智能指针和存储在 s1
中的智能指针无关. 当 s2
被销毁时, 这会释放由 s1
管理的指针. 当 s1
被销毁时, 造成二次释放漏洞.
1 |
|
合规方案
这个合规方案利用 std::enable_shared_from_this::shared_from_this()
来从 S
中获取与之现存 std::shared_ptr
对象关联的共享指针. 一个常规的实现策略是使 std::shared_ptr
构造函数探测到继承于 std::enable_shared_from_this
的指针的存在, 并自动更新 std::enable_shared_from_this::shared_from_this()
执行所需的内部数据 ( the internal bookkeeping). 记住, std::enable_shared_from_this::shared_from_this()
需要一个管理着指向 this
指针的存活着的 std::shared_ptr
实例. 如果不满足这个条件将导致 未定义行为, undefined behavior, 因为这会造成这个试图管理对象生命周期的智能指针自身并没有生命周期管理的语义.
当不存在引用 this
的 std::shared_ptr
对象时, 我们是否应该有准则来制止 shared_from_this()
的调用? 这么做将是未定义行为, 但是我觉得那也是一个非常具体的情景.
1 |
|
风险预估
将一个先前并非由匹配的分配函数获取的指针值传递到析构函数中会造成 未定义行为, undefined behavior, 导致可利用 漏洞, vulnerabilities.
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MEM56-CPP | High | Likely | Medium | P18 | L1 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Astrée | 20.10 | **dangling_pointer_use ** | |
Axivion Bauhaus Suite | 7.2.0 | CertC++-MEM56 | |
Helix QAC | 2021.2 | C++4721, C++4722, C++4723 | |
Parasoft C/C++test | 2021.1 | CERT_CPP-MEM56-a | Do not store an already-owned pointer value in an unrelated smart pointer |
Polyspace Bug Finder | R2021b | CERT C++: MEM56-CPP | Checks for use of already-owned pointers (rule fully covered) |
PVS-Studio | 7.15 | V1006 |
Related Vulnerabilities
Search for other vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
SEI CERT C++ Coding Standard | MEM50-CPP. Do not access freed memory MEM51-CPP. Properly deallocate dynamically allocated resources |
---|---|
MITRE CWE | CWE-415, Double Free CWE-416, Use After Free CWE 762, Mismatched Memory Management Routines |
Bibliography
[ISO/IEC 14882-2014] | Subclause 20.8, “Smart Pointers” |
---|---|
本文标题:MEM56-CPP. 不要在不相关的智能指针中存放一个已有所属的指针值
文章作者:xwnb
发布时间:2021-11-15
最后更新:2023-04-17
原始链接:https://xwnb.github.io/posts/2305000242/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!并保留本声明。感谢您的阅读和支持!
分享