c++c++17valgrindplacement-new

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


In the following code

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 and c3 are constructed in a wrong place. But how to detect it? In this case, both valgrind and -fsanitize=address don't work.

I'm wondering how to detect such wrong usage?


Solution

  • c1 is also placed incorrectly. The size of SimpleChar must be at least 8 because of the alignas(8). So at offset 8 into the array there isn't enough space for one anymore.

    Based on the options you showed, I assume you use GCC or Clang.

    In that case -fsanitize=undefined, specifically -fsanitize=alignment, may complain because the offsets in your example are misaligned for the type.

    In fact, generally all of your placement-news have UB, because it isn't guaranteed that slab is aligned to 8. You'll need to add alignas(SimpleChar) to its declaration as well if you want to store SimpleChars in it.

    On Clang -fsanitize=undefined, specifically -fsanitize=array-bounds, will also complain for access out-of-bounds on the storage array.

    GCC doesn't seem to support the array-bounds UB sanitizer check, but instead complains with a warning included in -Wall, specifically -Wplacement-new.

    (Also, technically, an array that is used to provide storage for other objects should have element type unsigned char or std::byte, not char.)