Skip to content

Commit 5b6f9ef

Browse files
authored
Fix segfaults with String and ByteVector nullptr arguments (#1247) (#1248)
1 parent e3de035 commit 5b6f9ef

4 files changed

Lines changed: 206 additions & 19 deletions

File tree

taglib/toolkit/tbytevector.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ ByteVector fromNumber(T value, bool mostSignificantByteFirst)
158158
template <typename TFloat, typename TInt, Utils::ByteOrder ENDIAN>
159159
TFloat toFloat(const ByteVector &v, size_t offset)
160160
{
161-
if(offset > v.size() - sizeof(TInt)) {
161+
if(offset + sizeof(TInt) > v.size()) {
162162
debug("toFloat() - offset is out of range. Returning 0.");
163163
return 0.0;
164164
}
@@ -195,7 +195,7 @@ long double toFloat80(const ByteVector &v, size_t offset)
195195
{
196196
using std::swap;
197197

198-
if(offset > v.size() - 10) {
198+
if(offset + 10 > v.size()) {
199199
debug("toFloat80() - offset is out of range. Returning 0.");
200200
return 0.0;
201201
}
@@ -360,7 +360,7 @@ ByteVector::ByteVector(const char *data, unsigned int length) :
360360
}
361361

362362
ByteVector::ByteVector(const char *data) :
363-
d(std::make_unique<ByteVectorPrivate>(data, static_cast<unsigned int>(::strlen(data))))
363+
d(std::make_unique<ByteVectorPrivate>(data, data ? static_cast<unsigned int>(::strlen(data)) : 0))
364364
{
365365
}
366366

@@ -767,6 +767,9 @@ bool ByteVector::operator!=(const ByteVector &v) const
767767

768768
bool ByteVector::operator==(const char *s) const
769769
{
770+
if(!s)
771+
return isEmpty();
772+
770773
if(size() != ::strlen(s))
771774
return false;
772775

taglib/toolkit/tstring.cpp

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -195,23 +195,27 @@ String::String(const wchar_t *s) :
195195
String::String(const wchar_t *s, Type t) :
196196
d(std::make_shared<StringPrivate>())
197197
{
198-
if(t == UTF16 || t == UTF16BE || t == UTF16LE) {
199-
copyFromUTF16(d->data, s, ::wcslen(s), t);
200-
}
201-
else {
202-
debug("String::String() -- const wchar_t * should not contain Latin1 or UTF-8.");
198+
if(s) {
199+
if(t == UTF16 || t == UTF16BE || t == UTF16LE) {
200+
copyFromUTF16(d->data, s, ::wcslen(s), t);
201+
}
202+
else {
203+
debug("String::String() -- const wchar_t * should not contain Latin1 or UTF-8.");
204+
}
203205
}
204206
}
205207

206208
String::String(const char *s, Type t) :
207209
d(std::make_shared<StringPrivate>())
208210
{
209-
if(t == Latin1)
210-
copyFromLatin1(d->data, s, ::strlen(s));
211-
else if(t == String::UTF8)
212-
copyFromUTF8(d->data, s, ::strlen(s));
213-
else {
214-
debug("String::String() -- const char * should not contain UTF16.");
211+
if(s) {
212+
if(t == Latin1)
213+
copyFromLatin1(d->data, s, ::strlen(s));
214+
else if(t == String::UTF8)
215+
copyFromUTF8(d->data, s, ::strlen(s));
216+
else {
217+
debug("String::String() -- const char * should not contain UTF16.");
218+
}
215219
}
216220
}
217221

@@ -546,6 +550,10 @@ bool String::operator!=(const String &s) const
546550

547551
bool String::operator==(const char *s) const
548552
{
553+
if(!s) {
554+
return isEmpty();
555+
}
556+
549557
const wchar_t *p = toCWString();
550558

551559
while(*p != L'\0' || *s != '\0') {
@@ -562,6 +570,10 @@ bool String::operator!=(const char *s) const
562570

563571
bool String::operator==(const wchar_t *s) const
564572
{
573+
if(!s) {
574+
return isEmpty();
575+
}
576+
565577
return d->data == s;
566578
}
567579

@@ -580,18 +592,22 @@ String &String::operator+=(const String &s)
580592

581593
String &String::operator+=(const wchar_t *s)
582594
{
583-
detach();
595+
if(s) {
596+
detach();
584597

585-
d->data += s;
598+
d->data += s;
599+
}
586600
return *this;
587601
}
588602

589603
String &String::operator+=(const char *s)
590604
{
591-
detach();
605+
if(s) {
606+
detach();
592607

593-
for(int i = 0; s[i] != 0; i++)
594-
d->data += static_cast<unsigned char>(s[i]);
608+
for(int i = 0; s[i] != 0; i++)
609+
d->data += static_cast<unsigned char>(s[i]);
610+
}
595611
return *this;
596612
}
597613

tests/test_bytevector.cpp

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* http://www.mozilla.org/MPL/ *
2424
***************************************************************************/
2525

26+
#include <cstring>
2627
#define _USE_MATH_DEFINES
2728
#include <cmath>
2829

@@ -53,6 +54,7 @@ class TestByteVector : public CppUnit::TestFixture
5354
CPPUNIT_TEST(testAppend1);
5455
CPPUNIT_TEST(testAppend2);
5556
CPPUNIT_TEST(testBase64);
57+
CPPUNIT_TEST(testEmpty);
5658
CPPUNIT_TEST_SUITE_END();
5759

5860
public:
@@ -612,6 +614,94 @@ class TestByteVector : public CppUnit::TestFixture
612614

613615
}
614616

617+
void testEmpty()
618+
{
619+
const ByteVector empty;
620+
const ByteVector notEmpty("A");
621+
ByteVector mutEmpty;
622+
623+
CPPUNIT_ASSERT_EQUAL(empty, ByteVector(""));
624+
CPPUNIT_ASSERT_EQUAL(empty, ByteVector("", 0));
625+
CPPUNIT_ASSERT_EQUAL(empty, ByteVector(0U));
626+
CPPUNIT_ASSERT_EQUAL(empty, ByteVector(empty, 0, 0));
627+
CPPUNIT_ASSERT_EQUAL(empty, ByteVector(notEmpty, 1, 0));
628+
CPPUNIT_ASSERT_EQUAL(empty, ByteVector(static_cast<const char *>(nullptr)));
629+
CPPUNIT_ASSERT_EQUAL(empty, ByteVector(static_cast<const char *>(nullptr), 0));
630+
CPPUNIT_ASSERT_EQUAL(mutEmpty.setData("", 0), empty);
631+
CPPUNIT_ASSERT_EQUAL(mutEmpty.setData(""), empty);
632+
CPPUNIT_ASSERT_EQUAL(mutEmpty.setData(nullptr, 0), empty);
633+
CPPUNIT_ASSERT_EQUAL(mutEmpty.setData(nullptr), empty);
634+
CPPUNIT_ASSERT(!empty.data());
635+
CPPUNIT_ASSERT(!mutEmpty.data());
636+
CPPUNIT_ASSERT_EQUAL(empty.mid(0), empty);
637+
CPPUNIT_ASSERT_EQUAL(empty.at(0), '\0');
638+
// Note that the behavior of ByteVector::find() with an empty pattern is
639+
// not consistent with String::find() and std::string::find().
640+
CPPUNIT_ASSERT_EQUAL(empty.find(mutEmpty), -1);
641+
CPPUNIT_ASSERT_EQUAL(empty.find(notEmpty), -1);
642+
CPPUNIT_ASSERT_EQUAL(notEmpty.find(empty), -1);
643+
CPPUNIT_ASSERT_EQUAL(empty.find('\0'), -1);
644+
CPPUNIT_ASSERT_EQUAL(empty.rfind(mutEmpty), -1);
645+
CPPUNIT_ASSERT_EQUAL(empty.rfind(notEmpty), -1);
646+
CPPUNIT_ASSERT_EQUAL(notEmpty.rfind(empty), -1);
647+
CPPUNIT_ASSERT_EQUAL(empty.containsAt(mutEmpty, 0), false);
648+
CPPUNIT_ASSERT_EQUAL(empty.startsWith(mutEmpty), false);
649+
CPPUNIT_ASSERT_EQUAL(empty.startsWith(notEmpty), false);
650+
CPPUNIT_ASSERT_EQUAL(notEmpty.startsWith(empty), false);
651+
CPPUNIT_ASSERT_EQUAL(empty.endsWith(mutEmpty), false);
652+
CPPUNIT_ASSERT_EQUAL(empty.endsWithPartialMatch(mutEmpty), -1);
653+
CPPUNIT_ASSERT_EQUAL(mutEmpty.replace('a', 'b'), empty);
654+
CPPUNIT_ASSERT_EQUAL(mutEmpty.replace("abc", ""), empty);
655+
CPPUNIT_ASSERT_EQUAL(mutEmpty.append(empty), empty);
656+
CPPUNIT_ASSERT_EQUAL(mutEmpty.append(notEmpty), notEmpty);
657+
mutEmpty.clear();
658+
CPPUNIT_ASSERT_EQUAL(mutEmpty, empty);
659+
CPPUNIT_ASSERT_EQUAL(ByteVector(notEmpty).append(empty), notEmpty);
660+
CPPUNIT_ASSERT_EQUAL(mutEmpty.append('A'), notEmpty);
661+
CPPUNIT_ASSERT_EQUAL(mutEmpty.resize(0), empty);
662+
CPPUNIT_ASSERT_EQUAL(empty.size(), 0U);
663+
CPPUNIT_ASSERT(empty.begin() == empty.end());
664+
CPPUNIT_ASSERT(empty.cbegin() == empty.cend());
665+
CPPUNIT_ASSERT(empty.rbegin() == empty.rend());
666+
CPPUNIT_ASSERT(mutEmpty.begin() == mutEmpty.end());
667+
CPPUNIT_ASSERT(mutEmpty.rbegin() == mutEmpty.rend());
668+
CPPUNIT_ASSERT(empty.isEmpty());
669+
CPPUNIT_ASSERT_EQUAL(empty.toUInt(), 0U);
670+
CPPUNIT_ASSERT_EQUAL(empty.toUInt(0, true), 0U);
671+
CPPUNIT_ASSERT_EQUAL(empty.toUInt(0, 0, true), 0U);
672+
CPPUNIT_ASSERT_EQUAL(empty.toShort(), static_cast<short>(0));
673+
CPPUNIT_ASSERT_EQUAL(empty.toShort(0, true), static_cast<short>(0));
674+
CPPUNIT_ASSERT_EQUAL(empty.toUShort(), static_cast<unsigned short>(0));
675+
CPPUNIT_ASSERT_EQUAL(empty.toUShort(0, true), static_cast<unsigned short>(0));
676+
CPPUNIT_ASSERT_EQUAL(empty.toLongLong(), 0LL);
677+
CPPUNIT_ASSERT_EQUAL(empty.toLongLong(0, true), 0LL);
678+
CPPUNIT_ASSERT_EQUAL(empty.toULongLong(), 0ULL);
679+
CPPUNIT_ASSERT_EQUAL(empty.toULongLong(0, true), 0ULL);
680+
CPPUNIT_ASSERT_EQUAL(empty.toFloat32LE(0), 0.f);
681+
CPPUNIT_ASSERT_EQUAL(empty.toFloat32BE(0), 0.f);
682+
CPPUNIT_ASSERT_EQUAL(empty.toFloat64LE(0), 0.);
683+
CPPUNIT_ASSERT_EQUAL(empty.toFloat64BE(0), 0.);
684+
CPPUNIT_ASSERT_EQUAL(empty.toFloat80LE(0), 0.l);
685+
CPPUNIT_ASSERT_EQUAL(empty.toFloat80BE(0), 0.l);
686+
CPPUNIT_ASSERT(empty == mutEmpty);
687+
CPPUNIT_ASSERT(empty != notEmpty);
688+
CPPUNIT_ASSERT(empty == "");
689+
CPPUNIT_ASSERT(empty != " ");
690+
CPPUNIT_ASSERT(empty == static_cast<const char *>(nullptr));
691+
CPPUNIT_ASSERT(!(empty != static_cast<const char *>(nullptr)));
692+
CPPUNIT_ASSERT(empty < notEmpty);
693+
CPPUNIT_ASSERT(!(empty > notEmpty));
694+
CPPUNIT_ASSERT_EQUAL(empty + mutEmpty, empty);
695+
CPPUNIT_ASSERT_EQUAL(empty + notEmpty, notEmpty);
696+
CPPUNIT_ASSERT_EQUAL(mutEmpty = static_cast<const char *>(nullptr), empty);
697+
CPPUNIT_ASSERT_EQUAL(mutEmpty = notEmpty, notEmpty);
698+
ByteVector tmp;
699+
mutEmpty.swap(tmp);
700+
CPPUNIT_ASSERT_EQUAL(mutEmpty, empty);
701+
CPPUNIT_ASSERT_EQUAL(empty.toHex(), empty);
702+
CPPUNIT_ASSERT_EQUAL(empty.toBase64(), empty);
703+
}
704+
615705
};
616706

617707
CPPUNIT_TEST_SUITE_REGISTRATION(TestByteVector);

tests/test_string.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@
2323
* http://www.mozilla.org/MPL/ *
2424
***************************************************************************/
2525

26+
#include <string>
2627
#include <cstring>
2728

2829
#include "tstring.h"
30+
#include "tstringlist.h"
31+
#include "tbytevector.h"
2932
#include "tutils.h"
3033
#include <cppunit/extensions/HelperMacros.h>
3134

@@ -54,6 +57,7 @@ class TestString : public CppUnit::TestFixture
5457
CPPUNIT_TEST(testEncodeNonBMP);
5558
CPPUNIT_TEST(testIterator);
5659
CPPUNIT_TEST(testInvalidUTF8);
60+
CPPUNIT_TEST(testEmpty);
5761
CPPUNIT_TEST_SUITE_END();
5862

5963
public:
@@ -370,6 +374,80 @@ class TestString : public CppUnit::TestFixture
370374
CPPUNIT_ASSERT(String(ByteVector("\xED\xB0\x80\xED\xA0\x80"), String::UTF8).isEmpty());
371375
}
372376

377+
void testEmpty()
378+
{
379+
const String empty;
380+
const String notEmpty("A");
381+
String mutEmpty;
382+
383+
CPPUNIT_ASSERT_EQUAL(empty, String(""));
384+
CPPUNIT_ASSERT_EQUAL(empty, String(std::wstring()));
385+
CPPUNIT_ASSERT_EQUAL(empty, String(static_cast<const wchar_t *>(nullptr)));
386+
CPPUNIT_ASSERT(empty != String('\0'));
387+
CPPUNIT_ASSERT_EQUAL(empty, String(L'\0'));
388+
CPPUNIT_ASSERT_EQUAL(empty, String(static_cast<const char *>(nullptr)));
389+
CPPUNIT_ASSERT_EQUAL(empty, String(ByteVector()));
390+
CPPUNIT_ASSERT_EQUAL(empty.to8Bit(), std::string());
391+
CPPUNIT_ASSERT_EQUAL(empty.toWString(), std::wstring());
392+
CPPUNIT_ASSERT_EQUAL(::strlen(empty.toCString()), (size_t)0);
393+
CPPUNIT_ASSERT_EQUAL(::wcslen(empty.toCWString()), (size_t)0);
394+
CPPUNIT_ASSERT(empty.begin() == empty.end());
395+
CPPUNIT_ASSERT(empty.cbegin() == empty.cend());
396+
CPPUNIT_ASSERT(mutEmpty.begin() == mutEmpty.end());
397+
CPPUNIT_ASSERT_EQUAL(empty.find(mutEmpty), 0);
398+
CPPUNIT_ASSERT_EQUAL(empty.find(notEmpty), -1);
399+
CPPUNIT_ASSERT_EQUAL(notEmpty.find(empty), 0);
400+
CPPUNIT_ASSERT_EQUAL(empty.rfind(mutEmpty), 0);
401+
CPPUNIT_ASSERT_EQUAL(empty.rfind(notEmpty), -1);
402+
CPPUNIT_ASSERT_EQUAL(notEmpty.rfind(empty), 1);
403+
CPPUNIT_ASSERT_EQUAL(empty.split(), StringList(empty));
404+
CPPUNIT_ASSERT_EQUAL(empty.startsWith(mutEmpty), true);
405+
CPPUNIT_ASSERT_EQUAL(empty.startsWith(notEmpty), false);
406+
CPPUNIT_ASSERT_EQUAL(notEmpty.startsWith(empty), true);
407+
CPPUNIT_ASSERT_EQUAL(empty.substr(0), empty);
408+
CPPUNIT_ASSERT_EQUAL(mutEmpty.append(empty), empty);
409+
CPPUNIT_ASSERT_EQUAL(mutEmpty.append(notEmpty), notEmpty);
410+
mutEmpty.clear();
411+
CPPUNIT_ASSERT_EQUAL(mutEmpty, empty);
412+
CPPUNIT_ASSERT_EQUAL(String(notEmpty).append(empty), notEmpty);
413+
CPPUNIT_ASSERT_EQUAL(empty.upper(), empty);
414+
CPPUNIT_ASSERT_EQUAL(empty.size(), 0U);
415+
CPPUNIT_ASSERT_EQUAL(empty.length(), 0U);
416+
CPPUNIT_ASSERT_EQUAL(empty.isEmpty(), true);
417+
CPPUNIT_ASSERT_EQUAL(empty.data(String::Latin1), ByteVector());
418+
CPPUNIT_ASSERT_EQUAL(empty.data(String::UTF16LE), ByteVector());
419+
bool ok;
420+
empty.toInt(&ok);
421+
CPPUNIT_ASSERT(!ok);
422+
CPPUNIT_ASSERT_EQUAL(empty.stripWhiteSpace(), empty);
423+
CPPUNIT_ASSERT_EQUAL(empty.isLatin1(), true);
424+
CPPUNIT_ASSERT_EQUAL(empty.isAscii(), true);
425+
CPPUNIT_ASSERT(empty == mutEmpty);
426+
CPPUNIT_ASSERT(empty != notEmpty);
427+
CPPUNIT_ASSERT(empty == "");
428+
CPPUNIT_ASSERT(empty != " ");
429+
CPPUNIT_ASSERT(empty == L"");
430+
CPPUNIT_ASSERT(empty != L" ");
431+
CPPUNIT_ASSERT(empty == static_cast<const char *>(nullptr));
432+
CPPUNIT_ASSERT(!(empty != static_cast<const char *>(nullptr)));
433+
CPPUNIT_ASSERT(empty == static_cast<const wchar_t *>(nullptr));
434+
CPPUNIT_ASSERT(!(empty != static_cast<const wchar_t *>(nullptr)));
435+
CPPUNIT_ASSERT_EQUAL(mutEmpty += empty, empty);
436+
CPPUNIT_ASSERT_EQUAL(mutEmpty += notEmpty, notEmpty);
437+
mutEmpty.clear();
438+
CPPUNIT_ASSERT_EQUAL(mutEmpty += static_cast<const char *>(nullptr), empty);
439+
CPPUNIT_ASSERT_EQUAL(mutEmpty += static_cast<const wchar_t *>(nullptr), empty);
440+
CPPUNIT_ASSERT_EQUAL(mutEmpty = static_cast<const char *>(nullptr), empty);
441+
CPPUNIT_ASSERT_EQUAL(mutEmpty = static_cast<const wchar_t *>(nullptr), empty);
442+
String tmp;
443+
mutEmpty.swap(tmp);
444+
CPPUNIT_ASSERT_EQUAL(mutEmpty, empty);
445+
CPPUNIT_ASSERT_EQUAL(empty < notEmpty, true);
446+
CPPUNIT_ASSERT_EQUAL(empty + mutEmpty, empty);
447+
CPPUNIT_ASSERT_EQUAL(empty + notEmpty, notEmpty);
448+
CPPUNIT_ASSERT_EQUAL(empty + static_cast<const char *>(nullptr), empty);
449+
}
450+
373451
};
374452

375453
CPPUNIT_TEST_SUITE_REGISTRATION(TestString);

0 commit comments

Comments
 (0)