Skip to content

Possible Heap Buffer Overflow when BOOST_ASIO_HAS_IO_URING definition is inconsistent across compilation units #1635

@yerodin

Description

@yerodin

[SECURITY] Heap Buffer Overflow in boost::asio Recycling Allocator

Summary

A critical heap buffer overflow vulnerability exists in the boost::asio recycling allocator implementation (boost/asio/detail/thread_info_base.hpp). The issue occurs when memory allocated with one chunk_size value is incorrectly reused for allocations with a different chunk_size value, causing writes potentially many bytes beyond the allocated buffer bounds.

This vulnerability affects any application using boost::asio coroutines, timers, or async operations where the BOOST_ASIO_HAS_IO_URING definition is inconsistent across compilation units.

Environment

  • Asio Version: >= 1.21.0
  • Platform: Linux x86_64 (likely affects all platforms)
  • Compiler: GCC with AddressSanitizer (-fsanitize=address)
  • Detection: Observable with AddressSanitizer enabled

Root Cause Analysis

Location: /usr/include/boost/asio/detail/thread_info_base.hpp

// VULNERABLE CODE:
if (static_cast<std::size_t>(mem[0]) >= chunks
    && reinterpret_cast<std::size_t>(pointer) % align == 0)
{
  this_thread->reusable_memory_[mem_index] = 0;
  mem[size] = mem[0];  // ← HEAP BUFFER OVERFLOW HERE
  return pointer;
}

The Core Problem:
The recycling allocator compares chunk counts between allocations that may have been made with different chunk_size values, leading to incorrect reuse decisions.

Chunk Size Logic:

#if defined(BOOST_ASIO_HAS_IO_URING)
  enum { chunk_size = 8 };    // Linux with io_uring
#else
  enum { chunk_size = 4 };    // All other platforms  
#endif

Vulnerability Scenario:
The issue occurs when small chunk_size allocations are reused for large chunk_size requests:

  1. Original allocation (with chunk_size = 4):

    • Request: size = 32 bytes
    • Chunks: (32 + 4 - 1) / 4 = 8 chunks
    • Actual allocation: 8 * 4 + 1 = 33 bytes total
    • Metadata: mem[0] = 8
  2. Reuse (with chunk_size = 8):

    • Request: size = 64 bytes
    • Chunks needed: (64 + 8 - 1) / 8 = 8 chunks
    • Flawed check: if (8 >= 8)TRUE (incorrectly accepts!)
    • Overflow: Tries to write mem[64] = 8, but only 33 bytes allocated
    • Result: 31-byte buffer overflow

Reproduction

A minimal reproduction case can be created by:

  1. Building a library with BOOST_ASIO_HAS_IO_URING=1
  2. Building test code without the define
  3. Using coroutines or async operations in a way that trigger executor_function allocation and reuse with different chunk sizes

AddressSanitizer Output

==123456==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6040000007b0
WRITE of size 1 at 0x6040000007b0 thread T0
    #0 0x7f8b8c in boost::asio::detail::thread_info_base::allocate<> thread_info_base.hpp:157
    #1 0x7f8b7a in boost::asio::detail::recycling_allocator::allocate recycling_allocator.hpp:53  
    #2 0x7f8b68 in boost::asio::detail::executor_function::executor_function executor_function.hpp:76
    #3 0x7f8b56 in boost::asio::post<>
    
0x6040000007b0 is located 0 bytes to the right of 32-byte region
allocated by thread T0 here:
    #0 0x7f8b9e in malloc
    #1 0x7f8b8c in boost::asio::detail::aligned_new

Impact Assessment

Severity: Critical

  • Memory Safety: Heap corruption with potential for large multi-byte overflows
  • Detectability: Silent corruption - only caught with AddressSanitizer
  • Scope: Affects any boost::asio async operations (coroutines, timers, I/O)
  • Exploitability: Medium - requires specific build configuration mismatch but can cause substantial corruption
  • Overflow Size: Can range from a few bytes to 30+ bytes depending on size requests

Affected Operations:

  • boost::asio::co_spawn() and coroutine operations
  • boost::asio::post() with lambda captures
  • boost::asio::steady_timer and other timer operations
  • Any operation using executor_function internally

Potential Impact:

  • Heap corruption: Can corrupt adjacent heap objects
  • Application crashes: Unpredictable crashes due to metadata corruption
  • Memory disclosure: In complex scenarios, could lead to information leakage
  • Exploitation: Large overflows increase the potential for exploitation

Possible Fix

Use a constant chunk size

In thread_info_base.hpp

private:
//#if defined(ASIO_HAS_IO_URING)
  enum { chunk_size = 8 }; // <-- Use a constant size of 8
//#else // defined(ASIO_HAS_IO_URING)
//  enum { chunk_size = 4 };
//#endif // defined(ASIO_HAS_IO_URING)

Security Considerations

The exploitability is limited by:

  • Requires specific build configuration mismatch

However, heap corruption bugs can lead to:

  • Application crashes and denial of service
  • Potential memory disclosure and code execution in complex scenarios
  • Unpredictable behavior in production systems

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions