CVE-2026-114691: Gcc: integer wrap in `std
A flaw was found in the libstdc++ component of GCC. An attacker who can provide an excessively large element count to a boolean vector (std::vector<bool>) constructor can trigger an integer wrap during internal memory calculations. This issue leaves the container in an inconsistent state, leading to out-of-bounds memory access and resulting in an application crash or a Denial of Service (DoS) during subsequent operations.
Other sources
AIONLYREPORT 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-SIZEMAX 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 pushback 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/stlbvector.h (Snword, constructor/init paths such as vector(sizetype, ...) and Minitialize) 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/stlbvector.h: cpp static sizet Snword(sizet n) { return (n + int(Swordbit) - 1) / int(Swordbit); } ... vector(sizetype n, const bool& value, const allocatortype& a = allocatortype()) : Base(a) { Minitialize(n); Minitializevalue(value); } ... Bitpointer q = this->Mallocate(n); this->Mimpl.Mendofstorage = q + Snword(n); iterator start = iterator(std::addressof(q), 0); this->Mimpl.Mstart = start; this->Mimpl.Mfinish = start + differencetype(n); For very large sizet inputs, the addition inside Snword can wrap before division. In the affected constructor/init path, allocation and Mendofstorage then follow the wrapped word count, while Mfinish 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 maxsize() or Mchecklen guards, so the supported reachability claim here is the constructor/init path rather than Mreallocate 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() { sizet n = std::numericlimits<sizet>::max() - 31; std::vector<bool> v(n, true); v.pushback(false); } 3. Run the program. On an affected build, the constructor can leave the container in an inconsistent state, and the subsequent pushback 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 maxsize() or SIZEMAX 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 Snword and to apply constructor/init length validation before calling Minitialize. diff diff --git a/libstdc++-v3/include/bits/stlbvector.h b/libstdc++-v3/include/bits/stlbvector.h @@ +#include <limits> @@ static sizet Snword(sizet n) { return (n + int(Swordbit) - 1) / int(Swordbit); } + { + constexpr sizet wb = sizet(Swordbit); + if (n > (std::numericlimits<sizet>::max() - (wb - 1))) + throwlengtherror(N("vector<bool>: size overflow")); + return (n + (wb - 1)) / wb; + } @@ + static sizetype + Scheckinitlen(sizetype n, const allocatortype& a) + { + const sizetype isize = + gnucxx::numerictraits<differencetype>::max + - int(Swordbit) + 1; + const sizetype asize = Bitalloctraits::maxsize( + Bitalloctype(a)); + const sizetype max = (asize <= isize / int(Swordbit) + ? asize int(Swordbit) : isize); + if (n > max) + throwlengtherror(N("cannot create std::vector larger than maxsize()")); + return n; + } @@
: Base(a) + : Base(a) {
Minitialize(n); + Minitialize(Scheckinitlen(n, a)); Minitializevalue(value); } @@
Minitialize(n); + Minitialize(Scheckinitlen(n, MgetBitallocator())); std::copy(first, last, begin());
------ This report was generated using AI technology. Always review AI-generated content prior to use
— Red Hat
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Compensating control
For applications using the affected libstdc++ std::vector<bool> implementation, reject externally influenced element counts and enforce explicit application-level length limits well before max_size() or SIZE_MAX boundaries; do not rely solely on the specialization's internal length checks.