You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 4 Next »

Dynamic memory allocation and deallocation functions can be globally replaced by custom implementations, as specified by [replacement.functions], paragraph 2 of the C++ Standard [ISO/IEC 14882-2014]. For instance, a user may profile the dynamic memory usage of their application and decide that the default allocator is not optimal for their usage pattern, and a different allocation strategy may be a marked improvement. However, the C++ Standard, [res.on.functions], paragraph 1, states:

In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ standard library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.

Paragraph 2 goes on to state, in part:

In particular, the effects are undefined in the following cases:
— for replacement functions, if the installed replacement function does not implement the semantics of the applicable Required behavior: paragraph.

A replacement for any of the dynamic memory allocation or deallocation functions must meet the semantic requirements specified by the appropriate Required behavior: clause of the replaced function.

Noncompliant Code Example

In this noncompliant code example, the global operator new(std::size_t) function is replaced by a custom implementation. However, the custom implementation fails to honor the behavior required by the function it replaces. Specifically, if the custom allocator fails to allocate the requested amount of memory, the replacement function returns a null pointer instead of throwing an exception of type std::bad_alloc. By returning a null pointer instead of throwing, functions relying on the required behavior of operator new(std::size_t) to throw on memory allocations may instead attempt to dereference a null pointer. See EXP34-C. Do not dereference null pointers for more information.

#include <new>

void *operator new(std::size_t size) {
  extern void *alloc_mem(std::size_t); // Implemented elsewhere; may return nullptr
  return alloc_mem(size);
}
 
void operator delete(void *ptr) noexcept; // Defined elsewhere
void operator delete(void *ptr, std::size_t) noexcept; // Defined elsewhere

The declarations of the replacement operator delete() functions are indications that this noncompliant code example still complies with DCL54-CPP. Overload allocation and deallocation functions as a pair in the same scope.

Compliant Solution

This compliant solution implements the required behavior for the replaced global allocator function by properly throwing a std::bad_alloc exception when the allocation fails:

#include <new>

void *operator new(std::size_t size) {
  extern void *alloc_mem(std::size_t); // Implemented elsewhere; may return nullptr
  if (void *ret = alloc_mem(size)) {
    return ret;
  }
  throw std::bad_alloc();
}
 
void operator delete(void *ptr) noexcept; // Defined elsewhere
void operator delete(void *ptr, std::size_t) noexcept; // Defined elsewhere

Risk Assessment

Failing to meet the stated requirements for a replaceable dynamic storage function leads to undefined behavior. The severity of risk depends heavily on the caller of the allocation functions, but in some situations, dereferencing a null pointer can lead to the execution of arbitrary code [Jack 2007], [van Sprundel 2006]. The indicated severity is for this more severe case.

Rule

Severity

Likelihood

Remediation Cost

Priority

Level

MEM55-CPP

High

Likely

Medium

P18

L1

Automated Detection

Tool

Version

Checker

Description

    

Related Vulnerabilities

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

Related Guidelines

Bibliography

[ISO/IEC 14882-2014]17.6.4.8, "Other Functions"
18.6.1, "Storage Allocation and Deallocation" 
[Jack 2007] 
[van Sprundel 2006] 

  

  • No labels