mirror of
https://github.com/taglib/taglib.git
synced 2026-11-06 22:48:48 -05:00
ByteVector, List: Check indices if the standard library does (#1467)
If the standard library checks the index in std::vector::operator[] (_GLIBCXX_ASSERTIONS, libc++ hardening modes), ByteVector::operator[] and List::operator[] now check the index against their own size and abort if it is out of range, as std::vector does. The std::vector check alone is not enough: a ByteVector created by mid() shares the buffer of the original byte vector, so an index past the end of the smaller vector still passes it. List::operator[] walks a std::list, which these checks do not cover. Builds without these macros are unchanged; a GCC Release build of libtag produces identical code. The Ubuntu CI job now builds with -D_GLIBCXX_ASSERTIONS so that the tests run with the checks enabled.
This commit is contained in:
@@ -11,6 +11,8 @@ jobs:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest, windows-2025, ubuntu-26.04]
|
||||
include:
|
||||
- os: ubuntu-latest
|
||||
cmake_extra_args: '-DCMAKE_CXX_FLAGS="-D_GLIBCXX_ASSERTIONS"'
|
||||
- os: windows-latest
|
||||
cmake_extra_args: '-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake"'
|
||||
- os: windows-2025
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
@@ -749,13 +750,35 @@ long double ByteVector::toFloat80BE(size_t offset) const
|
||||
return toFloat80<Utils::BigEndian>(*this, offset);
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
// If the standard library checks indices in std::vector::operator[], e.g.
|
||||
// with _GLIBCXX_ASSERTIONS or a libc++ hardening mode, ByteVector::operator[]
|
||||
// is checked too. This cannot be left to std::vector, because a ByteVector
|
||||
// created by mid() shares the buffer of the original byte vector.
|
||||
void checkIndex(const ByteVector &v, int index)
|
||||
{
|
||||
#if defined(_GLIBCXX_ASSERTIONS) || \
|
||||
(defined(_LIBCPP_HARDENING_MODE) && \
|
||||
_LIBCPP_HARDENING_MODE != _LIBCPP_HARDENING_MODE_NONE)
|
||||
if(index < 0 || static_cast<unsigned int>(index) >= v.size())
|
||||
std::abort();
|
||||
#else
|
||||
static_cast<void>(v);
|
||||
static_cast<void>(index);
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const char &ByteVector::operator[](int index) const
|
||||
{
|
||||
checkIndex(*this, index);
|
||||
return (*d->data)[d->offset + index];
|
||||
}
|
||||
|
||||
char &ByteVector::operator[](int index)
|
||||
{
|
||||
checkIndex(*this, index);
|
||||
detach();
|
||||
return (*d->data)[d->offset + index];
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
***************************************************************************/
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
|
||||
namespace TagLib {
|
||||
@@ -43,6 +44,22 @@ class ListPrivateBase
|
||||
{
|
||||
public:
|
||||
bool autoDelete{};
|
||||
|
||||
// If the standard library checks indices in std::vector::operator[], e.g.
|
||||
// with _GLIBCXX_ASSERTIONS or a libc++ hardening mode, List::operator[]
|
||||
// is checked too.
|
||||
static void checkIndex(unsigned int i, size_t size)
|
||||
{
|
||||
#if defined(_GLIBCXX_ASSERTIONS) || \
|
||||
(defined(_LIBCPP_HARDENING_MODE) && \
|
||||
_LIBCPP_HARDENING_MODE != _LIBCPP_HARDENING_MODE_NONE)
|
||||
if(i >= size)
|
||||
std::abort();
|
||||
#else
|
||||
static_cast<void>(i);
|
||||
static_cast<void>(size);
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
// A generic implementation
|
||||
@@ -289,6 +306,7 @@ T &List<T>::back()
|
||||
template <class T>
|
||||
T &List<T>::operator[](unsigned int i)
|
||||
{
|
||||
ListPrivateBase::checkIndex(i, d->list.size());
|
||||
auto it = d->list.begin();
|
||||
std::advance(it, i);
|
||||
|
||||
@@ -298,6 +316,7 @@ T &List<T>::operator[](unsigned int i)
|
||||
template <class T>
|
||||
const T &List<T>::operator[](unsigned int i) const
|
||||
{
|
||||
ListPrivateBase::checkIndex(i, d->list.size());
|
||||
auto it = d->list.begin();
|
||||
std::advance(it, i);
|
||||
|
||||
|
||||
@@ -50,6 +50,7 @@ class TestByteVector : public CppUnit::TestFixture
|
||||
CPPUNIT_TEST(testReplace);
|
||||
CPPUNIT_TEST(testReplaceAndDetach);
|
||||
CPPUNIT_TEST(testIterator);
|
||||
CPPUNIT_TEST(testIndex);
|
||||
CPPUNIT_TEST(testResize);
|
||||
CPPUNIT_TEST(testAppend1);
|
||||
CPPUNIT_TEST(testAppend2);
|
||||
@@ -420,6 +421,21 @@ public:
|
||||
CPPUNIT_ASSERT_EQUAL('3', *it4);
|
||||
}
|
||||
|
||||
void testIndex()
|
||||
{
|
||||
const ByteVector a("0123456789");
|
||||
const ByteVector b = a.mid(3, 4);
|
||||
CPPUNIT_ASSERT_EQUAL('3', b[0]);
|
||||
CPPUNIT_ASSERT_EQUAL('6', b[3]);
|
||||
|
||||
ByteVector c = a.mid(7);
|
||||
CPPUNIT_ASSERT_EQUAL('7', c[0]);
|
||||
CPPUNIT_ASSERT_EQUAL('9', c[2]);
|
||||
c[2] = 'X';
|
||||
CPPUNIT_ASSERT_EQUAL(ByteVector("78X"), c);
|
||||
CPPUNIT_ASSERT_EQUAL('9', a[9]);
|
||||
}
|
||||
|
||||
void testResize()
|
||||
{
|
||||
auto a = ByteVector("0123456789");
|
||||
|
||||
Reference in New Issue
Block a user