Main Content

Operator new not overloaded for possibly over-aligned class

Allocated storage might be smaller than object alignment requirement

Description

This defect occurs when you do not adequately overload operator new/new[] and you use this operator to create an object with an alignment requirement specified with alignas. The checker raises a defect for these versions of throwing and non-throwing operator new/new[].

  • void* operator new(std::size_t size)

  • void* operator new(std::size_t size, const std::nothrow_t&)

  • void* operator new[](std::size_t size)

  • void* operator new[](std::size_t size, const std::nothrow_t&)

The use of alignas indicates that you do not expect the default operator new/new[] to satisfy the alignment requirement or the object, and that the object is possibly over aligned. A type is over aligned if you use alignas to make the alignment requirement of the type larger than std::max_align_t. For instance, foo is over aligned in this code snippet because its alignment requirement is 32 bytes, but std::max_align_t has an alignment of 16 bytes in most implementations.

struct alignas(32) foo {
  char elems[32];
}

Operator new not overloaded for possibly overaligned class raises no defect if you do not overload the operator new/new[] and you use version C++17 or later of the Standard. The default operator new/new[] in C++17 or later supports over alignment by passing the alignment requirement as an argument of type std::align_val_t, for instance void* operator new(std::size_t size, std::align_val_t alignment).

Risk

The default operator new/new[] allocates storage with the alignment requirement of std::align_val_t at most. If you do not overload the operator when you create an object with over aligned type, the resulting object may be misaligned. Accessing this object might cause illegal access errors or abnormal program terminations.

Fix

If you use version C++14 or earlier of the Standard, pass the alignment requirement of over aligned types to the operator new/new[] by overloading the operator.

Examples

expand all

#include <new>
#include <cstdlib>
#include <iostream>

struct alignas(64) foo {
    char elems[32];
};

foo*  func()
{
    foo*  bar = 0x0;
    try {
        bar =  new  foo ;
    } catch (...) { return nullptr; }
    delete bar;
}

In this example, structure foo is declared with an alignment requirement of 32 bytes. When you use the default operator new to create object bar, the allocated memory for bar is smaller than the alignment requirement of type foo and bar might be misaligned.

Correction — Define Overloaded Operator new to Handle Alignment Requirement of Type foo

One possible correction, if you use C11 stdlib.h or POSIX-C malloc.h, is to define an overloaded operator new that uses aligned_alloc() or posix_memalign() or to obtain storage with the correct alignment.

#include <new>
#include <cstdlib>
#include <iostream>

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

foo*  func()
{
    foo*  bar = 0x0;
    try {
        bar =  new  foo ;
    } catch (...) { return nullptr; }
    delete bar;
} 

Result Information

Group: Object Oriented
Language: C++
Default: On for handwritten code, off for generated code
Command-Line Syntax: MISSING_OVERLOAD_NEW_FOR_ALIGNED_OBJ
Impact: Medium

Version History

Introduced in R2019b