Skip to content

Commit 8f548be

Browse files
Fix String::replace() and clean up SSO setLen
Fixes esp8266#5883 and supercedes esp8266#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. Also make setLength() explicitly write a 0 in the SSO buffer at the specific offset. It's probably not needed, but is safe to do and makes logical sense. Add in test cases from esp8266#5890 as well as some new ones that fail on the unmodified core.
1 parent d19fc3b commit 8f548be

File tree

3 files changed

+64
-3
lines changed

3 files changed

+64
-3
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
}

cores/esp8266/WString.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,7 @@ class String {
264264
inline unsigned int len() const { return sso() ? strlen(sso_buf) : ptr.len; }
265265
inline unsigned int capacity() const { return sso() ? SSOSIZE - 1 : ptr.cap; }
266266
inline void setSSO(bool sso) { sso_buf[SSOSIZE - 1] = sso ? 0x00 : 0xff; }
267-
inline void setLen(int len) { if (!sso()) ptr.len = len; }
267+
inline void setLen(int len) { if (!sso()) ptr.len = len; else sso_buf[len] = 0; }
268268
inline void setCapacity(int cap) { if (!sso()) ptr.cap = cap; }
269269
inline void setBuffer(char *buff) { if (!sso()) ptr.buff = buff; }
270270
// Buffer accessor functions

tests/host/core/test_string.cpp

+60
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,58 @@ 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[16];
387+
String *s = (String*)space;
388+
memset(space, 0xff, 16);
389+
new(s) String;
390+
REQUIRE(*s == "");
391+
392+
// Tests from #5883
393+
bool useURLencode = false;
394+
const char euro[4] = {(char)0xe2, (char)0x82, (char)0xac, 0}; // Unicode euro symbol
395+
const char yen[3] = {(char)0xc2, (char)0xa5, 0}; // Unicode yen symbol
396+
397+
memset(space, 0xff, 16);
398+
new(s) String("%ssid%");
399+
repl(("%ssid%"), "MikroTik", *s, useURLencode);
400+
REQUIRE(*s == "MikroTik");
401+
402+
memset(space, 0xff, 16);
403+
new(s) String("{E}");
404+
repl(("{E}"), euro, *s, useURLencode);
405+
REQUIRE(*s == "");
406+
memset(space, 0xff, 16);
407+
new(s) String("&euro;");
408+
repl(("&euro;"), euro, *s, useURLencode);
409+
REQUIRE(*s == "");
410+
memset(space, 0xff, 16);
411+
new(s) String("{Y}");
412+
repl(("{Y}"), yen, *s, useURLencode);
413+
REQUIRE(*s == "¥");
414+
memset(space, 0xff, 16);
415+
new(s) String("&yen;");
416+
repl(("&yen;"), yen, *s, useURLencode);
417+
REQUIRE(*s == "¥");
418+
419+
memset(space, 0xff, 16);
420+
new(s) String("%sysname%");
421+
repl(("%sysname%"), "CO2_defect", *s, useURLencode);
422+
REQUIRE(*s == "CO2_defect");
363423
}

0 commit comments

Comments
 (0)