“Placement New” In C++

In their standard forms, C++ allocation and deallocation functions deal with untyped and uninitialized memory. The vast majority of developers should be familiar with malloc and free, however the new operator can offer a wider range of functionality; some of which is a bit strange. Objects can be placed anywhere in the free store by providing an allocator function with extra arguments and then supplying these arguments to “new”. This is called placement syntax. Stated once more, you can place objects at a known address. This is not possible with malloc as far as I know, and I’m quite surprised that this exists. Trying to use this functionality with a raw address, you’ll likely get a compiler note saying that you’re initializing __p, which usually is not done. A typical use case for this placement new would be constructing an object in memory that is already allocated. Using a pre-allocated buffer alleviates all danger of allocation failure for your object, so that can simplify code perhaps.

/usr/include/c++/5/new:129:14: note: initializing argument 2 of ‘void* operator new(std::size_t, void*)’
inline void* operator new(std::size_t, void* __p) _GLIBCXX_USE_NOEXCEPT

Here is an example of placing some data into an already existing stack buffer using placement new, then some code abusing placement new to write data 0x500 bytes away from the current stack:

#include <iostream>
#include <new>

using namespace std;

int main()
{
    char buffer[1024];

    /* Using new to place data at a specific address */

    /* syntax: new (address) (type) initializer */
    
    int* placed_1 = new (buffer) int(1337);
    int* placed_2 = new (buffer) int(4141);
    
    cout << "Address of `buffer` on stack: " << &buffer << endl;
    cout << "Address of `placed_1`: " << placed_1 << endl;
    cout << "Address of `placed_2`: " << placed_2 << endl;
    

    /* Using new to overwrite some stack space 0x500 bytes away from the stack.
     * Don't do this! 
     */

    /* Get stack addr */
    void *p = NULL;
    int* write_location = (int*)&p - 0x500;

    cout << "Before overwrite " << write_location << " has " << *write_location << endl;
    int* dont_do_this = new (write_location) int(6161);
    cout << "After overwrite " << write_location << " has " << *write_location << endl;

    
    return 0;
}

Output:

user@ubuntu:~/cpp/part_2/chapter_11$ g++ -std=c++11 -ggdb -o placement placement.cpp 
user@ubuntu:~/cpp/part_2/chapter_11$ ./placement 
Address of `buffer` on stack: 0x7ffc8e1354a0
Address of `placed_1`: 0x7ffc8e1354a0
Address of `placed_2`: 0x7ffc8e1354a0
Before overwrite 0x7ffc8e134078 has 0
After overwrite 0x7ffc8e134078 has 6161

 

Leave a Reply