原文链接:

MEM55-CPP. Honor replacement dynamic storage management requirements


依照 [replacement.functions], 段落 2, C++ 标准 [ISO/IEC 14882-2014] 的说明, 动态内存分配和释放函数可以被自定义的实现全局替换. 举个例子, 用户可能分析了应用程序的动态内存的使用情况, 觉得默认分配器并不是他们使用情景下的最佳选择, 另一个不同的分配策略可能带来性能提升. 然而, C++ 标准, [res.on.functions], 段落1, 陈述如下:

在某些情况下 (用于实例化标准库模板组件的替换函数, 句柄函数, 类型运算符), C++ 标准库依赖于 C++ 程序提供的组件. 如果这些组件没有满足它们的要求, 标准库不会对这些实现施加限制.

In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ standard library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.

段落 2 局部, 进一步做了如下陈述:

特别地, 下述情况下的效果是未定义的

— 对于替换函数, 如果被置入的替换函数没有实现适用性的语义

所需行为 :段落.

In particular, the effects are undefined in the following cases:
— for replacement functions, if the installed replacement function does not implement the semantics of the applicable Required behavior: paragraph.

任何动态内存分配和释放函数的替换必须满足语义要求–由相称的 所需行为 : 替换代换分条款 指定

不合规代码示例

在这个不合规代码示例中, 全局 operator new(std::size_t) 函数被自定义的实现替代. 然而, 自定义实现没有遵循所替代函数所需的行为–依照 C++ Standard, [new.delete.single], paragraph 3. 特别地, 如果自定义分配器没有成功分配所请求地内存大小, 替换函数返回空指针而不是抛出 std::bad_alloc 类型的异常. 通过返回空指针来代替异常抛出, 对于那些依赖 operator new(std::size_t) 在内存分配时抛出异常的行为的函数可能试图解引用空指针. 见 EXP34-C. Do not dereference null pointers 获取更多信息.

当用 C++ 重写 EXP34-C, 其引用将需要被更新.

1
2
3
4
5
6
7
8
9
#include <new>

void *operator new(std::size_t size) {
extern void *alloc_mem(std::size_t); // Implemented elsewhere; may return nullptr
return alloc_mem(size);
}

void operator delete(void *ptr) noexcept; // Defined elsewhere
void operator delete(void *ptr, std::size_t) noexcept; // Defined elsewhere

operator delete() 替换函数的声明表明, 这个不合规的代码示例仍然遵循 DCL54-CPP. Overload allocation and deallocation functions as a pair in the same scope.

合规方案

这个合规方案实现了替换全局分配函数所需的行为–当分配失败时正确抛出 std::bad_alloc 异常.

1
2
3
4
5
6
7
8
9
10
11
12
#include <new>

void *operator new(std::size_t size) {
extern void *alloc_mem(std::size_t); // Implemented elsewhere; may return nullptr
if (void *ret = alloc_mem(size)) {
return ret;
}
throw std::bad_alloc();
}

void operator delete(void *ptr) noexcept; // Defined elsewhere
void operator delete(void *ptr, std::size_t) noexcept; // Defined elsewhere

风险预估

未满足可替换动态内存分配函数的要求加工导致 未定义行为, undefined behavior. 风险的严重性密切依赖于分配函数的调用者, 但是在某些情况下, 解引用空指针会导致任意代码执行 [Jack 2007, van Sprundel 2006]. 下述的严重性针对的是更严重的情况.

Rule Severity Likelihood Remediation Cost Priority Level
MEM55-CPP High Likely Medium P18 L1

Automated Detection

Tool Version Checker Description
Helix QAC img C++4736, C++4737, C++4738, C++4739
Klocwork img CERT.MEM.OVERRIDE.DELETE CERT.MEM.OVERRIDE.NEW
Parasoft C/C++test img CERT_CPP-MEM55-a The user defined ‘new’ operator should throw the ‘std::bad_alloc’ exception when the allocation fails
Polyspace Bug Finder img CERT C++: MEM55-CPP Checks for replacement allocation/deallocation functions that do not meet requirements of the Standard (rule fully covered)

Search for vulnerabilities resulting from the violation of this rule on the CERT website.

SEI CERT C++ Coding Standard DCL54-CPP. Overload allocation and deallocation functions as a pair in the same scope OOP56-CPP. Honor replacement handler requirements
SEI CERT C Coding Standard EXP34-C. Do not dereference null pointers

Bibliography

[ISO/IEC 14882-2014] Subclause 17.6.4.8, “Other Functions” Subclause 18.6.1, “Storage Allocation and Deallocation”
[Jack 2007]
[van Sprundel 2006]

SEI CERT C++ Coding Standard > SEI CERT C++ Coding Standard > button_arrow_left.png SEI CERT C++ Coding Standard > SEI CERT C++ Coding Standard > button_arrow_up.png SEI CERT C++ Coding Standard > SEI CERT C++ Coding Standard > button_arrow_right.png