Fedora Account System
Red Hat Associate
Red Hat Customer
Disclaimer: Community trackers are created by Red Hat Product Security team on a best effort basis. Package maintainers are required to ascertain if the flaw indeed affects their package, before starting the update process. AI_ONLY_REPORT package: gcc-toolset-15-gcc-15.2.1-7.el10 ------ Summary: Integer wrap in `std::vector<bool>` size-to-word conversion can bypass expected length checks in constructor/init paths: attacker-influenced near-`SIZE_MAX` lengths can wrap the internal word-count calculation, leave `vector<bool>` with inconsistent storage and logical size state, and later trigger undefined behavior or process termination. Requirements to exploit: An attacker must be able to influence a very large element count passed into a `std::vector<bool>` constructor or initialization path in code built against the affected `libstdc++` implementation, then reach a follow-on operation such as `push_back` or `insert`. The exact result depends on allocator and build behavior. Component affected: `gcc-toolset-15-gcc-15.2.1-7.el10`, specifically the `libstdc++` `std::vector<bool>` implementation in `libstdc++-v3/include/bits/stl_bvector.h` (`_S_nword`, constructor/init paths such as `vector(size_type, ...)` and `_M_initialize`) Version affected: `gcc-toolset-15-gcc-15.2.1-7.el10` Patch available: no released package fix established; proposed patch included below Version fixed: unknown Upstream coordination: Not notified. CVSS: CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H - 6.2 (MEDIUM) AV:L - The issue is in a local library/container implementation, and no package-specific network-reachable path is established by the available evidence. AC:L - Once a near-wrap size reaches the unchecked constructor/init path, the wrap and inconsistent state follow directly without a race or similarly uncommon prerequisite. PR:N - No privileges are required beyond the ability to supply the size that reaches the affected code path. UI:N - No separate user interaction is required. S:U - The impact remains within the security scope of the consuming process. C:N - The available evidence does not establish a reliable confidentiality impact. I:N - The available evidence does not establish a reliable integrity impact beyond undefined behavior. A:H - The inconsistent container state can lead to sanitizer-detected failures, crashes, or process termination in affected applications. Impact: Moderate. Under Red Hat's severity guidance, this issue is harder to exploit than an easy privilege-gain or remote compromise flaw because it requires an unusual attacker-controlled near-maximum length to reach an unchecked constructor/init path. Even so, the reachable memory-unsafe state can still cause denial of service and other undefined behavior in affected applications, which is more than minimal impact. Embargo: no Reason: The current evidence supports a local, application-specific denial-of-service/undefined-behavior condition with demanding input preconditions. The record does not currently support a strong claim of easy remote compromise or a broadly wormable condition. Acknowledgement: Aisle Research Vulnerability Details: The issue is in the packed `std::vector<bool>` size-to-word conversion and its unchecked constructor/init path in `libstdc++-v3/include/bits/stl_bvector.h`: ```cpp static size_t _S_nword(size_t __n) { return (__n + int(_S_word_bit) - 1) / int(_S_word_bit); } ... vector(size_type __n, const bool& __value, const allocator_type& __a = allocator_type()) : Base(_a) { M_initialize(_n); M_initialize_value(_value); } ... _Bit_pointer _q = this->_M_allocate(_n); this->_M_impl._M_end_of_storage = __q + S_nword(_n); iterator _start = iterator(std::addressof(*_q), 0); this->_M_impl._M_start = __start; this->_M_impl._M_finish = _start + difference_type(_n); ``` For very large `size_t` inputs, the addition inside `_S_nword` can wrap before division. In the affected constructor/init path, allocation and `_M_end_of_storage` then follow the wrapped word count, while `_M_finish` is advanced from the original requested element count. This can leave `std::vector<bool>` in an inconsistent state and later operations can encounter out-of-bounds behavior or other undefined behavior. By contrast, the available evidence indicates that `reserve` and related growth paths apply `max_size()` or `_M_check_len` guards, so the supported reachability claim here is the constructor/init path rather than `_M_reallocate` as a primary trigger. Steps to reproduce: 1. Build a small test program against the affected `libstdc++` implementation with `-O0 -g -fsanitize=address,undefined`. 2. Use the following test case: ```cpp #include <vector> #include <limits> int main() { size_t n = std::numeric_limits<size_t>::max() - 31; std::vector<bool> v(n, true); v.push_back(false); } ``` 3. Run the program. On an affected build, the constructor can leave the container in an inconsistent state, and the subsequent `push_back` may trigger a sanitizer finding, crash, or other undefined-behavior symptom. The exact manifestation can vary by allocator and build configuration. Mitigation: Reject externally influenced lengths long before `max_size()` or `SIZE_MAX` boundaries, especially for `std::vector<bool>` construction and initialization. Where untrusted size inputs are involved, prefer explicit application-level limits and avoid relying on the specialization's constructor/init path until a fix is applied. Proposed Fix: A minimal fix is to add checked arithmetic in `_S_nword` and to apply constructor/init length validation before calling `_M_initialize`. ```diff diff --git a/libstdc++-v3/include/bits/stl_bvector.h b/libstdc++-v3/include/bits/stl_bvector.h @@ +#include <limits> @@ static size_t _S_nword(size_t __n) { return (__n + int(_S_word_bit) - 1) / int(_S_word_bit); } + { + constexpr size_t __wb = size_t(_S_word_bit); + if (_n > (std::numeric_limits<size_t>::max() - (_wb - 1))) + _throw_length_error(_N("vector<bool>: size overflow")); + return (_n + (_wb - 1)) / __wb; + } @@ + static size_type + _S_check_init_len(size_type __n, const allocator_type& __a) + { + const size_type __isize = + _gnu_cxx::numeric_traits<difference_type>::_max + - int(_S_word_bit) + 1; + const size_type __asize = _Bit_alloc_traits::max_size( + Bit_alloc_type(_a)); + const size_type _max = (_asize <= __isize / int(_S_word_bit) + ? __asize * int(_S_word_bit) : __isize); + if (__n > __max) + _throw_length_error(_N("cannot create std::vector larger than max_size()")); + return __n; + } @@ : Base(_a) + : Base(_a) { M_initialize(_n); + M_initialize(_S_check_init_len(_n, __a)); M_initialize_value(_value); } @@ M_initialize(_n); + M_initialize(_S_check_init_len(_n, _M_get_Bit_allocator())); std::copy(__first, __last, begin()); ``` ------ This report was generated using AI technology. Always review AI-generated content prior to use