diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2018-11-13 22:57:44 +0000 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2018-11-13 22:57:44 +0000 |
commit | 6bdd58f73a33cceba4da5966c17c356ecc22b0c6 (patch) | |
tree | 0e71c13741900a8eab433e8a507bbfe08fc89621 /libstdc++-v3/src | |
parent | 874e50cbd5b254f1f171bf04ecdfeedc405dff5b (diff) |
Fix overflows in std::pmr::unsynchonized_pool_resource
* src/c++17/memory_resource.cc (bitset::full()): Handle edge case
for _M_next_word maximum value.
(bitset::get_first_unset(), bitset::set(size_type)): Use
update_next_word() to update _M_next_word.
(bitset::update_next_word()): New function, avoiding wraparound of
unsigned _M_next_word member.
(bitset::max_word_index()): New function.
(chunk::chunk(void*, uint32_t, void*, size_t)): Add assertion.
(chunk::max_bytes_per_chunk()): New function.
(pool::replenish(memory_resource*, const pool_options&)): Prevent
_M_blocks_per_chunk from exceeding max_blocks_per_chunk or from
causing chunk::max_bytes_per_chunk() to be exceeded.
* testsuite/20_util/unsynchronized_pool_resource/allocate-max-chunks.cc:
New test.
From-SVN: r266087
Diffstat (limited to 'libstdc++-v3/src')
-rw-r--r-- | libstdc++-v3/src/c++17/memory_resource.cc | 60 |
1 files changed, 45 insertions, 15 deletions
diff --git a/libstdc++-v3/src/c++17/memory_resource.cc b/libstdc++-v3/src/c++17/memory_resource.cc index 3595e255889..fdbbc914f2e 100644 --- a/libstdc++-v3/src/c++17/memory_resource.cc +++ b/libstdc++-v3/src/c++17/memory_resource.cc @@ -290,7 +290,18 @@ namespace pmr } // True if all bits are set - bool full() const noexcept { return _M_next_word >= nwords(); } + bool full() const noexcept + { + if (_M_next_word >= nwords()) + return true; + // For a bitset with size() > (max_blocks_per_chunk() - 64) we will + // have nwords() == (max_word_index() + 1) and so _M_next_word will + // never be equal to nwords(). + // In that case, check if the last word is full: + if (_M_next_word == max_word_index()) + return _M_words[_M_next_word] == word(-1); + return false; + } // True if size() != 0 and no bits are set. bool empty() const noexcept @@ -343,11 +354,7 @@ namespace pmr const word bit = word(1) << n; _M_words[i] |= bit; if (i == _M_next_word) - { - while (_M_words[_M_next_word] == word(-1) - && ++_M_next_word != nwords()) - { } - } + update_next_word(); return (i * bits_per_word) + n; } } @@ -361,11 +368,7 @@ namespace pmr const word bit = word(1) << (n % bits_per_word); _M_words[wd] |= bit; if (wd == _M_next_word) - { - while (_M_words[_M_next_word] == word(-1) - && ++_M_next_word != nwords()) - { } - } + update_next_word(); } void clear(size_type n) noexcept @@ -378,6 +381,18 @@ namespace pmr _M_next_word = wd; } + // Update _M_next_word to refer to the next word with an unset bit. + // The size of the _M_next_word bit-field means it cannot represent + // the maximum possible nwords() value. To avoid wraparound to zero + // this function saturates _M_next_word at max_word_index(). + void update_next_word() noexcept + { + size_t next = _M_next_word; + while (_M_words[next] == word(-1) && ++next < nwords()) + { } + _M_next_word = std::min(next, max_word_index()); + } + void swap(bitset& b) noexcept { std::swap(_M_words, b._M_words); @@ -396,6 +411,10 @@ namespace pmr static constexpr size_t max_blocks_per_chunk() noexcept { return (1ull << _S_size_digits) - 1; } + // Maximum value that can be stored in bitset::_M_next_word member (8191). + static constexpr size_t max_word_index() noexcept + { return (max_blocks_per_chunk() + bits_per_word - 1) / bits_per_word; } + word* data() const noexcept { return _M_words; } private: @@ -425,7 +444,7 @@ namespace pmr : bitset(words, n), _M_bytes(bytes), _M_p(static_cast<std::byte*>(p)) - { } + { __glibcxx_assert(bytes <= chunk::max_bytes_per_chunk()); } chunk(chunk&& c) noexcept : bitset(std::move(c)), _M_bytes(c._M_bytes), _M_p(c._M_p) @@ -451,6 +470,9 @@ namespace pmr // Number of blocks in this chunk using bitset::size; + static constexpr uint32_t max_bytes_per_chunk() noexcept + { return numeric_limits<decltype(_M_bytes)>::max(); } + // Determine if block with address p and size block_size // is contained within this chunk. bool owns(void* p, size_t block_size) @@ -639,8 +661,7 @@ namespace pmr void replenish(memory_resource* __r, const pool_options& __opts) { using word = chunk::word; - const size_t __blocks - = std::min<size_t>(__opts.max_blocks_per_chunk, _M_blocks_per_chunk); + const size_t __blocks = _M_blocks_per_chunk; const auto __bits = chunk::bits_per_word; const size_t __words = (__blocks + __bits - 1) / __bits; const size_t __block_size = block_size(); @@ -658,7 +679,16 @@ namespace pmr __r->deallocate(__p, __bytes, __alignment); } if (_M_blocks_per_chunk < __opts.max_blocks_per_chunk) - _M_blocks_per_chunk *= 2; + { + const size_t max_blocks + = (chunk::max_bytes_per_chunk() - sizeof(word)) + / (__block_size + 0.125); + _M_blocks_per_chunk = std::min({ + max_blocks, + __opts.max_blocks_per_chunk, + (size_t)_M_blocks_per_chunk * 2 + }); + } } void release(memory_resource* __r) |