From 7b7015c36cb1b4d25c6daf83c51262e4d1185196 Mon Sep 17 00:00:00 2001 From: Urs Fleisch Date: Mon, 28 Oct 2024 16:19:06 +0100 Subject: [PATCH] Fix segfaults with String and ByteVector nullptr arguments (#1247) --- taglib/toolkit/tbytevector.cpp | 9 ++-- taglib/toolkit/tstring.cpp | 48 ++++++++++++------ tests/test_bytevector.cpp | 90 ++++++++++++++++++++++++++++++++++ tests/test_string.cpp | 78 +++++++++++++++++++++++++++++ 4 files changed, 206 insertions(+), 19 deletions(-) diff --git a/taglib/toolkit/tbytevector.cpp b/taglib/toolkit/tbytevector.cpp index 350e8498..7a9c6746 100644 --- a/taglib/toolkit/tbytevector.cpp +++ b/taglib/toolkit/tbytevector.cpp @@ -158,7 +158,7 @@ ByteVector fromNumber(T value, bool mostSignificantByteFirst) template TFloat toFloat(const ByteVector &v, size_t offset) { - if(offset > v.size() - sizeof(TInt)) { + if(offset + sizeof(TInt) > v.size()) { debug("toFloat() - offset is out of range. Returning 0."); return 0.0; } @@ -195,7 +195,7 @@ long double toFloat80(const ByteVector &v, size_t offset) { using std::swap; - if(offset > v.size() - 10) { + if(offset + 10 > v.size()) { debug("toFloat80() - offset is out of range. Returning 0."); return 0.0; } @@ -360,7 +360,7 @@ ByteVector::ByteVector(const char *data, unsigned int length) : } ByteVector::ByteVector(const char *data) : - d(std::make_unique(data, static_cast(::strlen(data)))) + d(std::make_unique(data, data ? static_cast(::strlen(data)) : 0)) { } @@ -767,6 +767,9 @@ bool ByteVector::operator!=(const ByteVector &v) const bool ByteVector::operator==(const char *s) const { + if(!s) + return isEmpty(); + if(size() != ::strlen(s)) return false; diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index f19a6755..046de208 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -195,23 +195,27 @@ String::String(const wchar_t *s) : String::String(const wchar_t *s, Type t) : d(std::make_shared()) { - if(t == UTF16 || t == UTF16BE || t == UTF16LE) { - copyFromUTF16(d->data, s, ::wcslen(s), t); - } - else { - debug("String::String() -- const wchar_t * should not contain Latin1 or UTF-8."); + if(s) { + if(t == UTF16 || t == UTF16BE || t == UTF16LE) { + copyFromUTF16(d->data, s, ::wcslen(s), t); + } + else { + debug("String::String() -- const wchar_t * should not contain Latin1 or UTF-8."); + } } } String::String(const char *s, Type t) : d(std::make_shared()) { - if(t == Latin1) - copyFromLatin1(d->data, s, ::strlen(s)); - else if(t == String::UTF8) - copyFromUTF8(d->data, s, ::strlen(s)); - else { - debug("String::String() -- const char * should not contain UTF16."); + if(s) { + if(t == Latin1) + copyFromLatin1(d->data, s, ::strlen(s)); + else if(t == String::UTF8) + copyFromUTF8(d->data, s, ::strlen(s)); + else { + debug("String::String() -- const char * should not contain UTF16."); + } } } @@ -546,6 +550,10 @@ bool String::operator!=(const String &s) const bool String::operator==(const char *s) const { + if(!s) { + return isEmpty(); + } + const wchar_t *p = toCWString(); while(*p != L'\0' || *s != '\0') { @@ -562,6 +570,10 @@ bool String::operator!=(const char *s) const bool String::operator==(const wchar_t *s) const { + if(!s) { + return isEmpty(); + } + return d->data == s; } @@ -580,18 +592,22 @@ String &String::operator+=(const String &s) String &String::operator+=(const wchar_t *s) { - detach(); + if(s) { + detach(); - d->data += s; + d->data += s; + } return *this; } String &String::operator+=(const char *s) { - detach(); + if(s) { + detach(); - for(int i = 0; s[i] != 0; i++) - d->data += static_cast(s[i]); + for(int i = 0; s[i] != 0; i++) + d->data += static_cast(s[i]); + } return *this; } diff --git a/tests/test_bytevector.cpp b/tests/test_bytevector.cpp index 41c172c9..066f01e9 100644 --- a/tests/test_bytevector.cpp +++ b/tests/test_bytevector.cpp @@ -23,6 +23,7 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#include #define _USE_MATH_DEFINES #include @@ -53,6 +54,7 @@ class TestByteVector : public CppUnit::TestFixture CPPUNIT_TEST(testAppend1); CPPUNIT_TEST(testAppend2); CPPUNIT_TEST(testBase64); + CPPUNIT_TEST(testEmpty); CPPUNIT_TEST_SUITE_END(); public: @@ -612,6 +614,94 @@ public: } + void testEmpty() + { + const ByteVector empty; + const ByteVector notEmpty("A"); + ByteVector mutEmpty; + + CPPUNIT_ASSERT_EQUAL(empty, ByteVector("")); + CPPUNIT_ASSERT_EQUAL(empty, ByteVector("", 0)); + CPPUNIT_ASSERT_EQUAL(empty, ByteVector(0U)); + CPPUNIT_ASSERT_EQUAL(empty, ByteVector(empty, 0, 0)); + CPPUNIT_ASSERT_EQUAL(empty, ByteVector(notEmpty, 1, 0)); + CPPUNIT_ASSERT_EQUAL(empty, ByteVector(static_cast(nullptr))); + CPPUNIT_ASSERT_EQUAL(empty, ByteVector(static_cast(nullptr), 0)); + CPPUNIT_ASSERT_EQUAL(mutEmpty.setData("", 0), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty.setData(""), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty.setData(nullptr, 0), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty.setData(nullptr), empty); + CPPUNIT_ASSERT(!empty.data()); + CPPUNIT_ASSERT(!mutEmpty.data()); + CPPUNIT_ASSERT_EQUAL(empty.mid(0), empty); + CPPUNIT_ASSERT_EQUAL(empty.at(0), '\0'); + // Note that the behavior of ByteVector::find() with an empty pattern is + // not consistent with String::find() and std::string::find(). + CPPUNIT_ASSERT_EQUAL(empty.find(mutEmpty), -1); + CPPUNIT_ASSERT_EQUAL(empty.find(notEmpty), -1); + CPPUNIT_ASSERT_EQUAL(notEmpty.find(empty), -1); + CPPUNIT_ASSERT_EQUAL(empty.find('\0'), -1); + CPPUNIT_ASSERT_EQUAL(empty.rfind(mutEmpty), -1); + CPPUNIT_ASSERT_EQUAL(empty.rfind(notEmpty), -1); + CPPUNIT_ASSERT_EQUAL(notEmpty.rfind(empty), -1); + CPPUNIT_ASSERT_EQUAL(empty.containsAt(mutEmpty, 0), false); + CPPUNIT_ASSERT_EQUAL(empty.startsWith(mutEmpty), false); + CPPUNIT_ASSERT_EQUAL(empty.startsWith(notEmpty), false); + CPPUNIT_ASSERT_EQUAL(notEmpty.startsWith(empty), false); + CPPUNIT_ASSERT_EQUAL(empty.endsWith(mutEmpty), false); + CPPUNIT_ASSERT_EQUAL(empty.endsWithPartialMatch(mutEmpty), -1); + CPPUNIT_ASSERT_EQUAL(mutEmpty.replace('a', 'b'), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty.replace("abc", ""), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty.append(empty), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty.append(notEmpty), notEmpty); + mutEmpty.clear(); + CPPUNIT_ASSERT_EQUAL(mutEmpty, empty); + CPPUNIT_ASSERT_EQUAL(ByteVector(notEmpty).append(empty), notEmpty); + CPPUNIT_ASSERT_EQUAL(mutEmpty.append('A'), notEmpty); + CPPUNIT_ASSERT_EQUAL(mutEmpty.resize(0), empty); + CPPUNIT_ASSERT_EQUAL(empty.size(), 0U); + CPPUNIT_ASSERT(empty.begin() == empty.end()); + CPPUNIT_ASSERT(empty.cbegin() == empty.cend()); + CPPUNIT_ASSERT(empty.rbegin() == empty.rend()); + CPPUNIT_ASSERT(mutEmpty.begin() == mutEmpty.end()); + CPPUNIT_ASSERT(mutEmpty.rbegin() == mutEmpty.rend()); + CPPUNIT_ASSERT(empty.isEmpty()); + CPPUNIT_ASSERT_EQUAL(empty.toUInt(), 0U); + CPPUNIT_ASSERT_EQUAL(empty.toUInt(0, true), 0U); + CPPUNIT_ASSERT_EQUAL(empty.toUInt(0, 0, true), 0U); + CPPUNIT_ASSERT_EQUAL(empty.toShort(), static_cast(0)); + CPPUNIT_ASSERT_EQUAL(empty.toShort(0, true), static_cast(0)); + CPPUNIT_ASSERT_EQUAL(empty.toUShort(), static_cast(0)); + CPPUNIT_ASSERT_EQUAL(empty.toUShort(0, true), static_cast(0)); + CPPUNIT_ASSERT_EQUAL(empty.toLongLong(), 0LL); + CPPUNIT_ASSERT_EQUAL(empty.toLongLong(0, true), 0LL); + CPPUNIT_ASSERT_EQUAL(empty.toULongLong(), 0ULL); + CPPUNIT_ASSERT_EQUAL(empty.toULongLong(0, true), 0ULL); + CPPUNIT_ASSERT_EQUAL(empty.toFloat32LE(0), 0.f); + CPPUNIT_ASSERT_EQUAL(empty.toFloat32BE(0), 0.f); + CPPUNIT_ASSERT_EQUAL(empty.toFloat64LE(0), 0.); + CPPUNIT_ASSERT_EQUAL(empty.toFloat64BE(0), 0.); + CPPUNIT_ASSERT_EQUAL(empty.toFloat80LE(0), 0.l); + CPPUNIT_ASSERT_EQUAL(empty.toFloat80BE(0), 0.l); + CPPUNIT_ASSERT(empty == mutEmpty); + CPPUNIT_ASSERT(empty != notEmpty); + CPPUNIT_ASSERT(empty == ""); + CPPUNIT_ASSERT(empty != " "); + CPPUNIT_ASSERT(empty == static_cast(nullptr)); + CPPUNIT_ASSERT(!(empty != static_cast(nullptr))); + CPPUNIT_ASSERT(empty < notEmpty); + CPPUNIT_ASSERT(!(empty > notEmpty)); + CPPUNIT_ASSERT_EQUAL(empty + mutEmpty, empty); + CPPUNIT_ASSERT_EQUAL(empty + notEmpty, notEmpty); + CPPUNIT_ASSERT_EQUAL(mutEmpty = static_cast(nullptr), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty = notEmpty, notEmpty); + ByteVector tmp; + mutEmpty.swap(tmp); + CPPUNIT_ASSERT_EQUAL(mutEmpty, empty); + CPPUNIT_ASSERT_EQUAL(empty.toHex(), empty); + CPPUNIT_ASSERT_EQUAL(empty.toBase64(), empty); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestByteVector); diff --git a/tests/test_string.cpp b/tests/test_string.cpp index 07caa62c..fa188380 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -23,9 +23,12 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ +#include #include #include "tstring.h" +#include "tstringlist.h" +#include "tbytevector.h" #include "tutils.h" #include @@ -54,6 +57,7 @@ class TestString : public CppUnit::TestFixture CPPUNIT_TEST(testEncodeNonBMP); CPPUNIT_TEST(testIterator); CPPUNIT_TEST(testInvalidUTF8); + CPPUNIT_TEST(testEmpty); CPPUNIT_TEST_SUITE_END(); public: @@ -370,6 +374,80 @@ public: CPPUNIT_ASSERT(String(ByteVector("\xED\xB0\x80\xED\xA0\x80"), String::UTF8).isEmpty()); } + void testEmpty() + { + const String empty; + const String notEmpty("A"); + String mutEmpty; + + CPPUNIT_ASSERT_EQUAL(empty, String("")); + CPPUNIT_ASSERT_EQUAL(empty, String(std::wstring())); + CPPUNIT_ASSERT_EQUAL(empty, String(static_cast(nullptr))); + CPPUNIT_ASSERT(empty != String('\0')); + CPPUNIT_ASSERT_EQUAL(empty, String(L'\0')); + CPPUNIT_ASSERT_EQUAL(empty, String(static_cast(nullptr))); + CPPUNIT_ASSERT_EQUAL(empty, String(ByteVector())); + CPPUNIT_ASSERT_EQUAL(empty.to8Bit(), std::string()); + CPPUNIT_ASSERT_EQUAL(empty.toWString(), std::wstring()); + CPPUNIT_ASSERT_EQUAL(::strlen(empty.toCString()), (size_t)0); + CPPUNIT_ASSERT_EQUAL(::wcslen(empty.toCWString()), (size_t)0); + CPPUNIT_ASSERT(empty.begin() == empty.end()); + CPPUNIT_ASSERT(empty.cbegin() == empty.cend()); + CPPUNIT_ASSERT(mutEmpty.begin() == mutEmpty.end()); + CPPUNIT_ASSERT_EQUAL(empty.find(mutEmpty), 0); + CPPUNIT_ASSERT_EQUAL(empty.find(notEmpty), -1); + CPPUNIT_ASSERT_EQUAL(notEmpty.find(empty), 0); + CPPUNIT_ASSERT_EQUAL(empty.rfind(mutEmpty), 0); + CPPUNIT_ASSERT_EQUAL(empty.rfind(notEmpty), -1); + CPPUNIT_ASSERT_EQUAL(notEmpty.rfind(empty), 1); + CPPUNIT_ASSERT_EQUAL(empty.split(), StringList(empty)); + CPPUNIT_ASSERT_EQUAL(empty.startsWith(mutEmpty), true); + CPPUNIT_ASSERT_EQUAL(empty.startsWith(notEmpty), false); + CPPUNIT_ASSERT_EQUAL(notEmpty.startsWith(empty), true); + CPPUNIT_ASSERT_EQUAL(empty.substr(0), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty.append(empty), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty.append(notEmpty), notEmpty); + mutEmpty.clear(); + CPPUNIT_ASSERT_EQUAL(mutEmpty, empty); + CPPUNIT_ASSERT_EQUAL(String(notEmpty).append(empty), notEmpty); + CPPUNIT_ASSERT_EQUAL(empty.upper(), empty); + CPPUNIT_ASSERT_EQUAL(empty.size(), 0U); + CPPUNIT_ASSERT_EQUAL(empty.length(), 0U); + CPPUNIT_ASSERT_EQUAL(empty.isEmpty(), true); + CPPUNIT_ASSERT_EQUAL(empty.data(String::Latin1), ByteVector()); + CPPUNIT_ASSERT_EQUAL(empty.data(String::UTF16LE), ByteVector()); + bool ok; + empty.toInt(&ok); + CPPUNIT_ASSERT(!ok); + CPPUNIT_ASSERT_EQUAL(empty.stripWhiteSpace(), empty); + CPPUNIT_ASSERT_EQUAL(empty.isLatin1(), true); + CPPUNIT_ASSERT_EQUAL(empty.isAscii(), true); + CPPUNIT_ASSERT(empty == mutEmpty); + CPPUNIT_ASSERT(empty != notEmpty); + CPPUNIT_ASSERT(empty == ""); + CPPUNIT_ASSERT(empty != " "); + CPPUNIT_ASSERT(empty == L""); + CPPUNIT_ASSERT(empty != L" "); + CPPUNIT_ASSERT(empty == static_cast(nullptr)); + CPPUNIT_ASSERT(!(empty != static_cast(nullptr))); + CPPUNIT_ASSERT(empty == static_cast(nullptr)); + CPPUNIT_ASSERT(!(empty != static_cast(nullptr))); + CPPUNIT_ASSERT_EQUAL(mutEmpty += empty, empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty += notEmpty, notEmpty); + mutEmpty.clear(); + CPPUNIT_ASSERT_EQUAL(mutEmpty += static_cast(nullptr), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty += static_cast(nullptr), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty = static_cast(nullptr), empty); + CPPUNIT_ASSERT_EQUAL(mutEmpty = static_cast(nullptr), empty); + String tmp; + mutEmpty.swap(tmp); + CPPUNIT_ASSERT_EQUAL(mutEmpty, empty); + CPPUNIT_ASSERT_EQUAL(empty < notEmpty, true); + CPPUNIT_ASSERT_EQUAL(empty + mutEmpty, empty); + CPPUNIT_ASSERT_EQUAL(empty + notEmpty, notEmpty); + CPPUNIT_ASSERT_EQUAL(empty + static_cast(nullptr), empty); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestString);