原文链接:

MEM57-CPP. Avoid using default operator new for over-aligned types


非 placement new 表达式是专门用于调用那些为指定类型的对象分配内存的分配函数. 当分配成功时, 分配函数, 反过来, 需要返回一个适应任意对象的基本对齐要求的内存对齐的指针. 虽然全局 operator new , 被该 new 表达式调用的默认分配函数, 已经由 C++ standard [ISO/IEC 14882-2014] 所规定, 用来分配充足且恰当对齐的内存来表达特定大小的任一对象, 既然所期望的对齐并不是该函数接口的一部分, 大多数情况程序可以放心假设, 由实现定义的默认 operator new 分配的内存是以基本对齐为对象对齐的. 特别地, 对于有更严格对齐要求的类型的对象– 超出默认对齐 (over-aligned) 类型–使用该种内存并不安全.

进一步, 非 placement new 表达式的数组形式可能会增加它试图得到内存大小–未指定大小来调用相应的分配函数. 这块大小, 在 C++ standard 中被称为开销, 通常称为 cookie. cookie 被用来存储数组中元素的数量以便于数组的删除表达式或者异常展开机制能够对已成功构造的数组元素调用类型的析构函数. 然而在 C++ standard 并没有描述被数组 new 表达式所需的 cookie 的特定条件, 他们可能在其他说明中粗略表述过, 例如目标环境的 application binary interface (ABI) 文档. 举个例子, Itanium C++ ABI 表述了 cookie 大小的计算, 位置, 及元素数组正确对齐的实现的规则. 当这些规则应用在 cookie 被创建时, 获得超出默认对齐 (overaligned) [CodeSourcery 2016a] 的元素类型的, 恰当对齐的数组是可能的. 然而, 这些规则很复杂, Itanium C++ ABI 并没有普遍应用.

避免依靠默认 operator new 来获得那些超出默认对齐类型的对象的内存. 如果这么做会造成 未定义行为, undefined behavior , 且在对象被访问时造成 异常终止, abnormal termination , 即使在那些已知能容错非对齐访问的架构上.

不合规代码示例

在下述的不合规的代码示例中, new 表达式被用来调用默认的 operator new 来获取内存–后续用于构造用户自定义类型对象 Vector –对齐超出了大多数实现的基本对齐 (通常为 16 字节) . 这类超出默认对齐的类型的对象–SIMD (单指令, 多数据) 向量指令常需的, 当被传入不恰当对齐参数可能遭遇陷阱.

1
2
3
4
5
6
7
8
struct alignas(32) Vector {
char elems[32];
};

Vector *f() {
Vector *pv = new Vector;
return pv;
}

合规方案 (aligned_alloc)

在这个合规方案中, 定义了一个 operator new 函数, 通过调用 C11 的 aligned_alloc() 来获取恰当对齐的内存. 使用这种 new 表达式的数组形式的程序必须定义对应的成员, 数组 operator new[]operator delete[] . aligned_alloc() 寒素并不是 C++ 98, C++ 11, 或 C++ 14 的标准, 但是可能被这些标准作为扩展提供. 那些没有提供 C11 的 aligned_alloc() 函数的 C++ 实现的程序必须定义成员 operator new 来调整由各自选用的分配函数分配的内存的对齐.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <cstdlib>
#include <new>

struct alignas(32) Vector {
char elems[32];
static void *operator new(size_t nbytes) {
if (void *p = std::aligned_alloc(alignof(Vector), nbytes)) {
return p;
}
throw std::bad_alloc();
}
static void operator delete(void *p) {
free(p);
}
};

Vector *f() {
Vector *pv = new Vector;
return pv;
}

风险预估

使用不恰当对齐的指针将导致 未定义行为, undefined behavior, 通常造成 异常终止, abnormal termination.

Rule Severity Likelihood Remediation Cost Priority Level
MEM57-CPP Medium Unlikely Low P6 L2

Automated Detection

Tool Version Checker Description
Helix QAC 2021.2 C++3129
Parasoft C/C++test 2021.1 CERT_CPP-MEM57-a Avoid using the default operator ‘new’ for over-aligned types
Polyspace Bug Finder R2021b CERT C++: MEM57-CPP Checks for situations where operator new is not overloaded for possibly overaligned types (rule fully covered)

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

SEI CERT C++ Coding Standard MEM54-CPP. Provide placement new with properly aligned pointers to sufficient storage capacity

Bibliography

[ISO/IEC 14882-2014] Subclause 3.7.4, “Dynamic Storage Duration” Subclause 5.3.4, “New” Subclause 18.6.1, “Storage Allocation and Deallocation”
[[CodeSourcery 2016a](http://aa. bibliography/#codesourcery 2016a)] Itanium C++ ABI, version 1.86
[INCITS 2012] Dynamic memory allocation for over-aligned data, WG14 proposal

img img img