1
0
mirror of https://github.com/leethomason/tinyxml2.git synced 2026-07-30 23:52:59 +04:00

Make DynArray support objects larger than 1 gigabyte

The expression ``const int newAllocated = cap * 2;`` easily causes
overflow, as soon as the input is 1.0 GiB. This goes unnoticed because
release builds of tinyxml2 do not have active assertions.

The change in commit 9.0.0-20-g8fd6cc6 did not do anything useful;
the signed multiplication overflow (and thus undefined behavior)
still occurs.

Using ``int`` in this class is really archaic, because it limits the
class to a gigabyte even on 64-bit platforms.

The multiplication overflow check also needs to include sizeof(T),
otherwise you can run into unsigned multiplication overflow (defined,
but undesirable) in the memcpy() call.

testcase:

int main()
{
        tinyxml2::XMLDocument doc;
        doc.InsertEndChild(doc.NewDeclaration());
        auto root = doc.NewElement("root");
        size_t sz = 0x80000001;
        auto blank = new char[sz];
        memset(blank, ' ', sz);
        blank[sz-1]='\0';
        root->SetText(blank);
        doc.InsertEndChild(root);
        tinyxml2::XMLPrinter printer(nullptr);
        doc.Print(&printer);
}
This commit is contained in:
Jan Engelhardt
2024-07-01 14:23:34 +02:00
parent 3a893e5757
commit eb3ab0df5d
2 changed files with 33 additions and 34 deletions
+1 -1
View File
@@ -2221,7 +2221,7 @@ void XMLDocument::MarkInUse(const XMLNode* const node)
TIXMLASSERT(node); TIXMLASSERT(node);
TIXMLASSERT(node->_parent == 0); TIXMLASSERT(node->_parent == 0);
for (int i = 0; i < _unlinked.Size(); ++i) { for (size_t i = 0; i < _unlinked.Size(); ++i) {
if (node == _unlinked[i]) { if (node == _unlinked[i]) {
_unlinked.SwapRemove(i); _unlinked.SwapRemove(i);
break; break;
+32 -33
View File
@@ -199,7 +199,7 @@ private:
Has a small initial memory pool, so that low or no usage will not Has a small initial memory pool, so that low or no usage will not
cause a call to new/delete cause a call to new/delete
*/ */
template <class T, int INITIAL_SIZE> template <class T, size_t INITIAL_SIZE>
class DynArray class DynArray
{ {
public: public:
@@ -227,9 +227,8 @@ public:
++_size; ++_size;
} }
T* PushArr( int count ) { T* PushArr( size_t count ) {
TIXMLASSERT( count >= 0 ); TIXMLASSERT( _size <= SIZE_MAX - count );
TIXMLASSERT( _size <= INT_MAX - count );
EnsureCapacity( _size+count ); EnsureCapacity( _size+count );
T* ret = &_mem[_size]; T* ret = &_mem[_size];
_size += count; _size += count;
@@ -242,7 +241,7 @@ public:
return _mem[_size]; return _mem[_size];
} }
void PopArr( int count ) { void PopArr( size_t count ) {
TIXMLASSERT( _size >= count ); TIXMLASSERT( _size >= count );
_size -= count; _size -= count;
} }
@@ -251,13 +250,13 @@ public:
return _size == 0; return _size == 0;
} }
T& operator[](int i) { T& operator[](size_t i) {
TIXMLASSERT( i>= 0 && i < _size ); TIXMLASSERT( i < _size );
return _mem[i]; return _mem[i];
} }
const T& operator[](int i) const { const T& operator[](size_t i) const {
TIXMLASSERT( i>= 0 && i < _size ); TIXMLASSERT( i < _size );
return _mem[i]; return _mem[i];
} }
@@ -266,18 +265,18 @@ public:
return _mem[ _size - 1]; return _mem[ _size - 1];
} }
int Size() const { size_t Size() const {
TIXMLASSERT( _size >= 0 ); TIXMLASSERT( _size >= 0 );
return _size; return _size;
} }
int Capacity() const { size_t Capacity() const {
TIXMLASSERT( _allocated >= INITIAL_SIZE ); TIXMLASSERT( _allocated >= INITIAL_SIZE );
return _allocated; return _allocated;
} }
void SwapRemove(int i) { void SwapRemove(size_t i) {
TIXMLASSERT(i >= 0 && i < _size); TIXMLASSERT(i < _size);
TIXMLASSERT(_size > 0); TIXMLASSERT(_size > 0);
_mem[i] = _mem[_size - 1]; _mem[i] = _mem[_size - 1];
--_size; --_size;
@@ -297,14 +296,14 @@ private:
DynArray( const DynArray& ); // not supported DynArray( const DynArray& ); // not supported
void operator=( const DynArray& ); // not supported void operator=( const DynArray& ); // not supported
void EnsureCapacity( int cap ) { void EnsureCapacity( size_t cap ) {
TIXMLASSERT( cap > 0 ); TIXMLASSERT( cap > 0 );
if ( cap > _allocated ) { if ( cap > _allocated ) {
TIXMLASSERT( cap <= INT_MAX / 2 ); TIXMLASSERT( cap <= SIZE_MAX / 2 / sizeof(T));
const int newAllocated = cap * 2; const size_t newAllocated = cap * 2;
T* newMem = new T[static_cast<unsigned int>(newAllocated)]; T* newMem = new T[newAllocated];
TIXMLASSERT( newAllocated >= _size ); TIXMLASSERT( newAllocated >= _size );
memcpy( newMem, _mem, sizeof(T)*static_cast<size_t>(_size) ); // warning: not using constructors, only works for PODs memcpy( newMem, _mem, sizeof(T) * _size ); // warning: not using constructors, only works for PODs
if ( _mem != _pool ) { if ( _mem != _pool ) {
delete [] _mem; delete [] _mem;
} }
@@ -314,9 +313,9 @@ private:
} }
T* _mem; T* _mem;
T _pool[static_cast<size_t>(INITIAL_SIZE)]; T _pool[INITIAL_SIZE];
int _allocated; // objects allocated size_t _allocated; // objects allocated
int _size; // number objects in use size_t _size; // number objects in use
}; };
@@ -330,7 +329,7 @@ public:
MemPool() {} MemPool() {}
virtual ~MemPool() {} virtual ~MemPool() {}
virtual int ItemSize() const = 0; virtual size_t ItemSize() const = 0;
virtual void* Alloc() = 0; virtual void* Alloc() = 0;
virtual void Free( void* ) = 0; virtual void Free( void* ) = 0;
virtual void SetTracked() = 0; virtual void SetTracked() = 0;
@@ -340,7 +339,7 @@ public:
/* /*
Template child class to create pools of the correct type. Template child class to create pools of the correct type.
*/ */
template< int ITEM_SIZE > template< size_t ITEM_SIZE >
class MemPoolT : public MemPool class MemPoolT : public MemPool
{ {
public: public:
@@ -362,10 +361,10 @@ public:
_nUntracked = 0; _nUntracked = 0;
} }
virtual int ItemSize() const override{ virtual size_t ItemSize() const override {
return ITEM_SIZE; return ITEM_SIZE;
} }
int CurrentAllocs() const { size_t CurrentAllocs() const {
return _currentAllocs; return _currentAllocs;
} }
@@ -376,7 +375,7 @@ public:
_blockPtrs.Push( block ); _blockPtrs.Push( block );
Item* blockItems = block->items; Item* blockItems = block->items;
for( int i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) { for( size_t i = 0; i < ITEMS_PER_BLOCK - 1; ++i ) {
blockItems[i].next = &(blockItems[i + 1]); blockItems[i].next = &(blockItems[i + 1]);
} }
blockItems[ITEMS_PER_BLOCK - 1].next = 0; blockItems[ITEMS_PER_BLOCK - 1].next = 0;
@@ -417,7 +416,7 @@ public:
--_nUntracked; --_nUntracked;
} }
int Untracked() const { size_t Untracked() const {
return _nUntracked; return _nUntracked;
} }
@@ -448,10 +447,10 @@ private:
DynArray< Block*, 10 > _blockPtrs; DynArray< Block*, 10 > _blockPtrs;
Item* _root; Item* _root;
int _currentAllocs; size_t _currentAllocs;
int _nAllocs; size_t _nAllocs;
int _maxAllocs; size_t _maxAllocs;
int _nUntracked; size_t _nUntracked;
}; };
@@ -1981,11 +1980,11 @@ private:
void PushDepth(); void PushDepth();
void PopDepth(); void PopDepth();
template<class NodeType, int PoolElementSize> template<class NodeType, size_t PoolElementSize>
NodeType* CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool ); NodeType* CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool );
}; };
template<class NodeType, int PoolElementSize> template<class NodeType, size_t PoolElementSize>
inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool ) inline NodeType* XMLDocument::CreateUnlinkedNode( MemPoolT<PoolElementSize>& pool )
{ {
TIXMLASSERT( sizeof( NodeType ) == PoolElementSize ); TIXMLASSERT( sizeof( NodeType ) == PoolElementSize );