libstdc++: Avoid overflow when appending to std::filesystem::path
This prevents a std::filesystem::path from exceeding INT_MAX/4 components (which is unlikely to ever be a problem except on 16-bit targets). That limit ensures that the capacity*1.5 calculation doesn't overflow. We should also check that we don't exceed SIZE_MAX when calculating how many bytes to allocate. That only needs to be checked when int is at least as large as size_t, because otherwise we know that the product INT_MAX/4 * sizeof(value_type) will fit in SIZE_MAX. For targets where size_t is twice as wide as int this obviously holds. For msp430-elf we have 16-bit int and 20-bit size_t, so the condition holds as long as sizeof(value_type) fits in 7 bits, which it does. We can also remove some floating-point arithmetic in operator/= which ensures exponential growth of the buffer. That's redundant because path::_List::reserve does that anyway (and does so more efficiently since the commit immediately before this one). libstdc++-v3/ChangeLog: * src/c++17/fs_path.cc (path::_List::reserve): Limit maximum size and check for overflows in arithmetic. (path::operator/=(const path&)): Remove redundant exponential growth calculation.
This commit is contained in:
parent
a3fee5ef89
commit
d4cd871d15
1 changed files with 24 additions and 11 deletions
|
@ -35,6 +35,7 @@
|
|||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <bits/stl_uninitialized.h>
|
||||
#include <ext/numeric_traits.h> // __gnu_cxx::__int_traits
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using fs::path;
|
||||
|
@ -447,11 +448,30 @@ path::_List::reserve(int newcap, bool exact = false)
|
|||
|
||||
if (curcap < newcap)
|
||||
{
|
||||
const int nextcap = curcap + curcap / 2;
|
||||
if (!exact && newcap < nextcap)
|
||||
newcap = nextcap;
|
||||
if (!exact)
|
||||
{
|
||||
const int nextcap = curcap + curcap / 2;
|
||||
if (newcap < nextcap)
|
||||
newcap = nextcap;
|
||||
}
|
||||
|
||||
void* p = ::operator new(sizeof(_Impl) + newcap * sizeof(value_type));
|
||||
using __gnu_cxx::__int_traits;
|
||||
// Nobody should need paths with this many components.
|
||||
if (newcap >= __int_traits<int>::__max / 4)
|
||||
std::__throw_bad_alloc();
|
||||
|
||||
size_t bytes;
|
||||
if constexpr (__int_traits<int>::__max >= __int_traits<size_t>::__max)
|
||||
{
|
||||
size_t components;
|
||||
if (__builtin_mul_overflow(newcap, sizeof(value_type), &components)
|
||||
|| __builtin_add_overflow(sizeof(_Impl), components, &bytes))
|
||||
std::__throw_bad_alloc();
|
||||
}
|
||||
else // This won't overflow, even for 20-bit size_t on msp430.
|
||||
bytes = sizeof(_Impl) + newcap * sizeof(value_type);
|
||||
|
||||
void* p = ::operator new(bytes);
|
||||
std::unique_ptr<_Impl, _Impl_deleter> newptr(::new(p) _Impl{newcap});
|
||||
const int cursize = curptr ? curptr->size() : 0;
|
||||
if (cursize)
|
||||
|
@ -588,13 +608,6 @@ path::operator/=(const path& __p)
|
|||
++capacity; // Need to insert root-directory after root-name
|
||||
#endif
|
||||
|
||||
if (orig_type == _Type::_Multi)
|
||||
{
|
||||
const int curcap = _M_cmpts._M_impl->capacity();
|
||||
if (capacity > curcap)
|
||||
capacity = std::max(capacity, (int) (curcap * 1.5));
|
||||
}
|
||||
|
||||
_M_pathname.reserve(_M_pathname.length() + sep.length()
|
||||
+ __p._M_pathname.length());
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue