Opened 6 years ago

Closed 4 years ago

#12915 closed Bugs (fixed)

Buffer overflow in boost::container::vector (affects flat_set)

Reported by: mykmartin@… Owned by: Ion Gaztañaga
Milestone: To Be Determined Component: container
Version: Boost Development Trunk Severity: Problem
Keywords: Cc: fdegros@…

Description

boost::container::flat_set's ranged insert with ordered_unique_range_t uses vector::priv_merge to perform the insertion. The implementation of vector::priv_merge contains a buffer overflow bug for some combinations of inputs.

(Note: trac won't let me submit this with links to the source on github, so for future reference the line numbers are referring to commit 1261ac33089cbc08108b4beaed97f307270892a8 of boost/container/vector.hpp)

Line 2260 in vector.hpp calculates whether there is enough spare space in the vector's storage buffer to hold index values used during the merge operation:

std::size_t index_capacity = (aligned_addr >= capaddr) ? 0u : (capaddr - addr)/sizeof(size_type);
  • addr = start address of buffer + num of currently stored values (s) + num of new values to add (n)
  • aligned_addr = addr rounded up to the memory alignment boundary for size_type (typically 8)
  • capaddr = end address of buffer (start address + capacity)

When the byte size of the elements being stored is not a multiple of sizeof(size_type), the calculation of index_capacity can overestimate the available spare space. The attached test case uses a flat_set containing 4-byte integers and has the following layout for the ordered_unique_range insertion (numbers are element counts, not bytes):

  <-       capacity=623         ->
  +-------------+----------------+
  |    s=311    |    free=312    |
  +-------------+----------------+
                +---------+ :    :
                |  n=118  | :    :
                +---------+ :    ^ capaddr
                          : :
                     addr ^ ^ aligned_addr (= addr + 4 bytes)

The 8-byte index values are stored using an 8-byte aligned pointer (size_type *position in priv_insert_ordered_range). This is correctly initialised to aligned_addr but the index_capacity is incorrectly calculated using addr; it should use aligned_addr:

capaddr - addr         = 776 bytes / 8 = 97.0
capaddr - aligned_addr = 772 bytes / 8 = 96.5

The actual spare capacity is 96 index values of 8 bytes each, with a slack of 4 bytes. The code attempts to write 97 elements and overflows at line 2307. To detect the overflow, pass capaddr to priv_insert_ordered_range and add an assert before that line:

assert(boost::uintptr_t(position) + sizeof(size_type) < capaddr);
*position = static_cast<size_type>(pcur - pbeg);

The fix for this is to replace addr with aligned_addr in line 2260:

std::size_t index_capacity = (aligned_addr >= capaddr) ? 0u : (capaddr - aligned_addr)/sizeof(size_type);

Attachments (3)

overflow.cc (607 bytes ) - added by mykmartin@… 6 years ago.
test.cpp (707 bytes ) - added by karl.tarbe@… 4 years ago.
When run with valgrind: invalid write of size 8
log (31.2 KB ) - added by karl.tarbe@… 4 years ago.
Log of valgrind output

Download all attachments as: .zip

Change History (15)

by mykmartin@…, 6 years ago

Attachment: overflow.cc added

comment:1 by mykmartin@…, 6 years ago

Minor correction: the assert to show the problem should use <=:

assert(boost::uintptr_t(position) + sizeof(size_type) <= capaddr);

comment:2 by fdegros@…, 5 years ago

Ping? Any progress on this ticket? The diagnosis and fix have been provided by the original reporter. Is the fix making its way into new releases of Boost Containers?

comment:3 by François Degros <fdegros@…>, 5 years ago

Cc: fdegros@… added

comment:4 by karl.tarbe@…, 4 years ago

I might have also run into this. Why is this bug still new?

comment:5 by Ion Gaztañaga, 4 years ago

vector's merge implementation was rewritten some releases ago. Provided overflow.cc correctly works under current develop branch with gcc with ASAN+UBSAN.

by karl.tarbe@…, 4 years ago

Attachment: test.cpp added

When run with valgrind: invalid write of size 8

by karl.tarbe@…, 4 years ago

Attachment: log added

Log of valgrind output

comment:6 by karl.tarbe@…, 4 years ago

I have attached test.cpp and log of valgrind output on it.

I am using Arch linux with boost 1.67.0-4 and gcc 8.1.1+20180531-1

This might be a separate bug, but I am not sure.

comment:7 by Ion Gaztañaga, 4 years ago

Thanks for the new test case, it's a separate bug, working on it.

comment:8 by Ion Gaztañaga, 4 years ago

Looks like the following commit:

https://github.com/boostorg/move/commit/ddeb2341271583d25a0a9974d339b519b8e18dc9

fixes the issue. Will be released in 1.68.

comment:9 by karl.tarbe@…, 4 years ago

Thank you for quickly addressing the issue.

comment:10 by karl.tarbe@…, 4 years ago

I have one follow-up question. Which Boost versions are affected by this (the one I provided the test case for) bug.

So I can use BOOST_VERSION to switch between workaround (insert one-by-one) and the faster method.

comment:11 by Ion Gaztañaga, 4 years ago

There were other bugs related to this in Boost 1.67, so I would say soon to be released version 1.68 will be safe.

comment:12 by Ion Gaztañaga, 4 years ago

Resolution: fixed
Status: newclosed

Closing the bug.

Note: See TracTickets for help on using tickets.