使用放置新运算符时如何检查超出范围?

How to check out-of-range when using placement new operator?

提问人:Leapfrog 提问时间:10/11/2023 最后编辑:Paul FloydLeapfrog 更新时间:10/11/2023 访问量:154

问:

在以下代码中

struct alignas(8) SimpleChar {
    SimpleChar(char c_) : c(c_) {}

    char c;
};

int main() {
    char slab[10] = {'\0'};
    // call to 'SimpleChar::SimpleChar(char)' too

    SimpleChar* c0 = new (slab) SimpleChar('a'); 
    SimpleChar* c1 = new (slab + 8) SimpleChar('b');
    SimpleChar* c2 =
      new (std::launder(reinterpret_cast<char*>(slab + 80))) SimpleChar('d');  // But how to detect the wrong usage?
    std::cout << c2->c << std::endl;                                           // d

    SimpleChar* c3 = new (slab + 180) SimpleChar('e');  // But how to detect the wrong usage?
    std::cout << c3->c << std::endl;                   // e
}

c2并且建在错误的地方。但是如何检测它呢? 在这种情况下,valgrind 和 don-work 都不起作用。c3-fsanitize=address

我想知道如何检测这种错误的用法?

C++ C++17 Valgrind Placement-新

评论

0赞 PaulMcKenzie 10/11/2023
但是如何检测它呢?-- 所以基本上你的代码只是一种做缓冲区溢出的花哨方法?
7赞 mch 10/11/2023
您使用哪种编译器?GCC 经常抱怨代码:godbolt.org/z/on3735P6q
0赞 Aconcagua 10/11/2023
您不应尝试在执行 placement new 时检测情况,但在此之前已经检测到:size_t indexToInsert = ...; if(indexToInsert < std::size(slab)) { /* can safely construct a new element */ } else { /* out of range – some appropriate error handling */ }
1赞 Aconcagua 10/11/2023
顺便说一句,关于清洗:你必须清洗新的指针,而不是传递给放置位置的内存地址......cN
1赞 Daniel Langr 10/11/2023
不是而且也建错了地方吗?c0c1

答:

5赞 user17732522 10/11/2023 #1

c1也放置不正确。的大小必须至少是因为 .因此,在偏移到数组中时,不再有足够的空间容纳一个。SimpleChar8alignas(8)8

根据您显示的选项,我假设您使用 GCC 或 Clang。

在这种情况下,特别是 ,可能会抱怨,因为示例中的偏移量与类型未对齐。-fsanitize=undefined-fsanitize=alignment

事实上,通常您的所有展示位置新闻都有 UB,因为不能保证它与 .如果你想在其中存储 s,你也需要添加到它的声明中。slab8alignas(SimpleChar)SimpleChar

在 Clang 上,具体来说,还会抱怨存储阵列上的越界访问。-fsanitize=undefined-fsanitize=array-bounds

GCC 似乎不支持 UB 消毒剂检查,而是抱怨 ,特别是 .array-bounds-Wall-Wplacement-new

(此外,从技术上讲,用于为其他对象提供存储的数组应具有元素类型 或 ,而不是 。unsigned charstd::bytechar

评论

0赞 Leapfrog 10/12/2023
谢谢你的帮助。-fsanitize=undefined 和 -fsanitize=alignment 帮助检查它。
1赞 Paul Floyd 10/11/2023 #2

Valgrind memcheck 截获并验证堆分配和释放函数。

placement new 运算符不是这样的函数。

因此,重要的是为放置新提供的记忆。Memcheck 不会对堆栈内存执行边界检查(除非访问偏离)。@user17732522似乎已经回答了 LLVM 消毒剂的问题。