MEM52-CPP. 检测并处理内存分配错误
原文链接:
MEM52-CPP. Detect and handle memory allocation errors
默认的内存分配操作符, ::operator new(std::size_t)
, 如果分配失败, 会抛出 std::bad_alloc
异常. 所以, 没必要去检查 ::operator new(std::size_t)
的调用结果是否是 nullptr
. 不抛出形式, ::operator new(std::size_t, const std::nothrow_t &)
, 如果分配失败, 不抛出异常, 而是返回 nullptr
. 同样的行为也体现在 operator new[]
的两个版本的分配函数上. 此外, 默认分配器 (std::allocator
) 使用 ::operator new(std::size_t)
来执行分配操作, 应该要被同等看待.
1 | T *p1 = new T; // Throws std::bad_alloc if allocation fails |
进一步, operator new[]
能抛出类型错误 std::bad_array_new_length
, 一个 std::bad_alloc
的子类, 如果传入 new
的 size
参数是个负数或者超大值.
当使用不抛出形式, 在访问该返回的指针前,检查其不是 nullptr
是非常必要的. 使用任一种形式, 确保遵循 ERR50-CPP. Do not abruptly terminate the program.
不合规代码示例
在这个不合规的代码示例中, 通过 ::operator new[](std::size_t)
构造了一个 int
数组, 但未检查分配的结果. 该函数被标记为 noexcept
, 所以调用者假定这个函数不会抛出任何异常. 因为 ::operator new[](std::size_t)
在分配失败时能抛出异常, 这可能导致程序 异常终止 .
1 |
|
合规方案 (std::nothrow
)
当使用 std::nothrow
, new
操作符既不会返回空指针,也不会返回指向已分配空间的指针. 在引用这个返回的指针前, 始终得检查它是否是 nullptr
. 这个合规方案恰当地处理了返回 nullptr
的这种错误情况.
1 |
|
合规方案 (std::bad_alloc
)
另外可选的是, 你可以使用不带 std::nothrow
的 ::operator new[]
来替换, 且如果没有分配到足够的内存, 捕捉 std::bad_alloc
异常.
1 |
|
合规方案 (noexcept(false))
如果函数设计为期望调用者处理异常情况, 则允许显式地标记该函数为可能会抛出异常, 如该合规方案中所示. 标记这类函数并非严格必须的, 和其他没有 noexcept
修饰符的函数一样, 默认是允许抛出异常.
If the design of the function is such that the caller is expected to handle exceptional situations, it is permissible to mark the function explicitly as one that may throw, as in this compliant solution. Marking the function is not strictly required, as any function without a noexcept
specifier is presumed to allow throwing.
1 |
|
不合规代码示例
在这个不合规的代码示例中, 同一个表达式中执行了两个内存分配操作. 因为内存分配是被当作参数传入到函数调用里的, 其中一个 new
的调用结果抛出的异常可能导致内存泄漏.
1 | struct A { /* ... */ }; |
考虑这种情况, A
被分配和构造在先, 然后 B
被分配并抛出异常. 将 g()
的调用包装在 try/catch
块中并不充分, 因为这不能释放已经分配好的 A
的内存.
这个不合规的代码示例也违背了 EXP50-CPP. Do not depend on the order of evaluation for side effects, 因为传入 g()
的实参在计算时顺序时未确定的.
合规方案 (std::unique_ptr
)
在这个合规方案中, 利用 std::unique_ptr
及 RAII 来管理对象 A
和 B
的资源. 在不合规代码示例所描述的情景中, B
抛出异常仍然将引起对象 A
的析构和释放, 接着 std::unique_ptr<A>
被销毁.
1 |
|
合规方案 (References)
如果可能, 更适应的合规方案是, 完全移除内存分配, 取而代之通过传入对象的引用.
1 | struct A { /* ... */ }; |
风险评估
未成功检测分配错误会导致 程序异常终止, abnormal program termination 和 拒绝服务攻击, denial-of-service attacks.
如果被攻击的程序从返回值中引用了内存偏移地址, 攻击者可以利用这个程序来任意读写内存. 这种 漏洞, vulnerability 已经被用于执行任意代码 [VU#159523].
Rule | Severity | Likelihood | Remediation Cost | Priority | Level |
---|---|---|---|---|---|
MEM52-CPP | High | Likely | Medium | P18 | L1 |
Automated Detection
Tool | Version | Checker | Description |
---|---|---|---|
Compass/ROSE | |||
Coverity | 7.5 | CHECKED_RETURN | Finds inconsistencies in how function call return values are handled |
Helix QAC | C++3225, C++3226, C++3227, C++3228, C++3229, C++4632 | ||
Klocwork | NPD.CHECK.CALL.MIGHT NPD.CHECK.CALL.MUST NPD.CHECK.MIGHTNPD.CHECK.MUSTNPD.CONST.CALL NPD.CONST.DEREF NPD.FUNC.CALL.MIGHT NPD.FUNC.CALL.MUST NPD.FUNC.MIGHT **NPD.FUNC.MUSTNPD.GEN.CALL.MIGHT NPD.GEN.CALL.MUST NPD.GEN.MIGHT NPD.GEN.MUSTRNPD.CALL RNPD.DEREF ** | ||
LDRA tool suite | 45 D** ** | Partially implemented | |
Parasoft C/C++test | CERT_CPP-MEM52-a CERT_CPP-MEM52-b | Check the return value of new Do not allocate resources in function argument list because the order of evaluation of a function’s parameters is undefined | |
Parasoft Insure++ | Runtime detection | ||
Polyspace Bug Finder | CERT C++: MEM52-CPP | Checks for unprotected dynamic memory allocation (rule partially covered) | |
PRQA QA-C++ | 3225, 3226, 3227, 3228, 3229, *4632* | ||
PVS-Studio | V522, V668 |
Related Vulnerabilities
The vulnerability in Adobe Flash [VU#159523] arises because Flash neglects to check the return value from calloc()
. Even though calloc()
returns NULL
, Flash does not attempt to read or write to the return value. Instead, it attempts to write to an offset from the return value. Dereferencing NULL
usually results in a program crash, but dereferencing an offset from NULL
allows an exploit to succeed without crashing the program.
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
Related Guidelines
SEI CERT C Coding Standard | ERR33-C. Detect and handle standard library errors |
---|---|
MITRE CWE | CWE 252, Unchecked Return Value CWE 391, Unchecked Error Condition CWE 476, NULL Pointer Dereference CWE 690, Unchecked Return Value to NULL Pointer Dereference CWE 703, Improper Check or Handling of Exceptional Conditions CWE 754, Improper Check for Unusual or Exceptional Conditions |
Bibliography
[ISO/IEC 9899:2011] | Subclause 7.20.3, “Memory Management Functions” |
---|---|
[ISO/IEC 14882-2014] | Subclause 18.6.1.1, “Single-Object Forms” Subclause 18.6.1.2, “Array Forms” Subclause 20.7.9.1, “Allocator Members” |
[Meyers 1996] | Item 7, “Be Prepared for Out-of-Memory Conditions” |
[Seacord 2013] | Chapter 4, “Dynamic Memory Management” |
本文标题:MEM52-CPP. 检测并处理内存分配错误
文章作者:xwnb
发布时间:2021-11-13
最后更新:2023-04-17
原始链接:https://xwnb.github.io/posts/1181891836/
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!并保留本声明。感谢您的阅读和支持!
分享