Skip to content

Commit 54240d2

Browse files
Fix String::replace() garbage at end of string (#5897)
* Fix String::replace() Fixes #5883 and supercedes #5890 The replace() function was using len() while in the middle of buffer operations. In SSO mode len() is not stored separately and is a call to strlen(), which may not be legal if you're in the middle of overwriting the SSO buffer, as was the case in ::replace when the replacement string was longer than the find string. This caused potential garbage at the end of the string when accessed. Instead, just cache the length in a local while doing the operation. Add in test cases from #5890 as well as some new ones that fail on the unmodified core. * Fix stack smashing error on 64b When pointers are 8 bytes long, the size of a String is larger than 16 chars. Increase the allocated array we're using in the test to avoid a "stack smashing" error. * Manually call destructor in test Just for clarity, manually call the destructor for the Strings() that are "placement new'd" in the String tests. It is a no-op for the existing test, since thanks to SSO there are no memory allocations, but will help in case someone adds tests later which include longer strings.
1 parent 64e30b2 commit 54240d2

File tree

2 files changed

+70
-2
lines changed

2 files changed

+70
-2
lines changed

cores/esp8266/WString.cpp

+3-2
Original file line numberDiff line numberDiff line change
@@ -759,9 +759,10 @@ void String::replace(const String& find, const String& replace) {
759759
while(index >= 0 && (index = lastIndexOf(find, index)) >= 0) {
760760
readFrom = wbuffer() + index + find.len();
761761
memmove(readFrom + diff, readFrom, len() - (readFrom - buffer()));
762-
setLen(len() + diff);
763-
wbuffer()[len()] = 0;
762+
int newLen = len() + diff;
764763
memcpy(wbuffer() + index, replace.buffer(), replace.len());
764+
setLen(newLen);
765+
wbuffer()[newLen] = 0;
765766
index--;
766767
}
767768
}

tests/host/core/test_string.cpp

+67
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,12 @@ TEST_CASE("String constructors", "[core][String]")
8383
REQUIRE(ssh == "3.14159_abcd");
8484
String flash = (F("hello from flash"));
8585
REQUIRE(flash == "hello from flash");
86+
const char textarray[6] = {'h', 'e', 'l', 'l', 'o', 0};
87+
String hello(textarray);
88+
REQUIRE(hello == "hello");
89+
String hello2;
90+
hello2 = textarray;
91+
REQUIRE(hello2 == "hello");
8692
}
8793

8894
TEST_CASE("String concantenation", "[core][String]")
@@ -360,4 +366,65 @@ TEST_CASE("String SSO works", "[core][String]")
360366
REQUIRE(s == "0123456789abcdefghijklmnopqrstu");
361367
REQUIRE(s.length() == 31);
362368
}
369+
s = "0123456789abcde";
370+
s = s.substring(s.indexOf('a'));
371+
REQUIRE(s == "abcde");
372+
REQUIRE(s.length() == 5);
373+
}
374+
375+
#include <new>
376+
void repl(const String& key, const String& val, String& s, boolean useURLencode)
377+
{
378+
s.replace(key, val);
379+
}
380+
381+
382+
TEST_CASE("String SSO handles junk in memory", "[core][String]")
383+
{
384+
// We fill the SSO space with garbage then construct an object in it and check consistency
385+
// This is NOT how you want to use Strings outside of this testing!
386+
unsigned char space[64];
387+
String *s = (String*)space;
388+
memset(space, 0xff, 64);
389+
new(s) String;
390+
REQUIRE(*s == "");
391+
s->~String();
392+
393+
// Tests from #5883
394+
bool useURLencode = false;
395+
const char euro[4] = {(char)0xe2, (char)0x82, (char)0xac, 0}; // Unicode euro symbol
396+
const char yen[3] = {(char)0xc2, (char)0xa5, 0}; // Unicode yen symbol
397+
398+
memset(space, 0xff, 64);
399+
new(s) String("%ssid%");
400+
repl(("%ssid%"), "MikroTik", *s, useURLencode);
401+
REQUIRE(*s == "MikroTik");
402+
s->~String();
403+
404+
memset(space, 0xff, 64);
405+
new(s) String("{E}");
406+
repl(("{E}"), euro, *s, useURLencode);
407+
REQUIRE(*s == "");
408+
s->~String();
409+
memset(space, 0xff, 64);
410+
new(s) String("&euro;");
411+
repl(("&euro;"), euro, *s, useURLencode);
412+
REQUIRE(*s == "");
413+
s->~String();
414+
memset(space, 0xff, 64);
415+
new(s) String("{Y}");
416+
repl(("{Y}"), yen, *s, useURLencode);
417+
REQUIRE(*s == "¥");
418+
s->~String();
419+
memset(space, 0xff, 64);
420+
new(s) String("&yen;");
421+
repl(("&yen;"), yen, *s, useURLencode);
422+
REQUIRE(*s == "¥");
423+
s->~String();
424+
425+
memset(space, 0xff, 64);
426+
new(s) String("%sysname%");
427+
repl(("%sysname%"), "CO2_defect", *s, useURLencode);
428+
REQUIRE(*s == "CO2_defect");
429+
s->~String();
363430
}

0 commit comments

Comments
 (0)