Skip to content

Commit b199233

Browse files
Fix memory corruption on x64
Because pointers are 8 bytes (and 8-bytes aligned) on x64, the structure used to store SSO needs to make sure the terminal \0 won't overwrite some of that buffer. Adjust the SSOSIZE dynamically depending on the size of the struct ptr. Refactor to add a setBuffer method, like the setConfig, setSSO, etc. Fix some issues found when using SSO strings and functions which modify len() dynamically.
1 parent f2371d9 commit b199233

File tree

3 files changed

+74
-33
lines changed

3 files changed

+74
-33
lines changed

cores/esp8266/WString.cpp

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,12 @@ inline void String::init(void) {
132132
setSSO(false);
133133
setCapacity(0);
134134
setLen(0);
135-
ptr.buf = NULL;
135+
setBuffer(nullptr);
136136
}
137137

138138
void String::invalidate(void) {
139-
if(!sso() && ptr.buf)
140-
free(ptr.buf);
139+
if(!sso() && wbuffer())
140+
free(wbuffer());
141141
init();
142142
}
143143

@@ -155,15 +155,15 @@ unsigned char String::reserve(unsigned int size) {
155155
unsigned char String::changeBuffer(unsigned int maxStrLen) {
156156
// Can we use SSO here to avoid allocation?
157157
if (maxStrLen < sizeof(sso_buf)) {
158-
if (sso() || !ptr.buf) {
158+
if (sso() || !buffer()) {
159159
// Already using SSO, nothing to do
160160
setSSO(true);
161161
return 1;
162162
} else { // if bufptr && !sso()
163163
// Using bufptr, need to shrink into sso_buff
164164
char temp[sizeof(sso_buf)];
165165
memcpy(temp, buffer(), maxStrLen);
166-
free(ptr.buf);
166+
free(wbuffer());
167167
setSSO(true);
168168
memcpy(wbuffer(), temp, maxStrLen);
169169
return 1;
@@ -176,7 +176,7 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
176176
return false;
177177
}
178178
uint16_t oldLen = len();
179-
char *newbuffer = (char *) realloc(sso() ? nullptr : ptr.buf, newSize);
179+
char *newbuffer = (char *) realloc(sso() ? nullptr : wbuffer(), newSize);
180180
if(newbuffer) {
181181
size_t oldSize = capacity() + 1; // include NULL.
182182
if (sso()) {
@@ -190,7 +190,7 @@ unsigned char String::changeBuffer(unsigned int maxStrLen) {
190190
setSSO(false);
191191
setCapacity(newSize - 1);
192192
setLen(oldLen); // Needed in case of SSO where len() never existed
193-
ptr.buf = newbuffer;
193+
setBuffer(newbuffer);
194194
return 1;
195195
}
196196
return 0;
@@ -230,8 +230,8 @@ void String::move(String &rhs) {
230230
return;
231231
} else {
232232
if (!sso()) {
233-
free(ptr.buf);
234-
ptr.buf = nullptr;
233+
free(wbuffer());
234+
setBuffer(nullptr);
235235
}
236236
}
237237
}
@@ -240,14 +240,14 @@ void String::move(String &rhs) {
240240
memcpy(sso_buf, rhs.sso_buf, sizeof(sso_buf));
241241
} else {
242242
setSSO(false);
243-
ptr.buf = rhs.ptr.buf;
243+
setBuffer(rhs.wbuffer());
244244
}
245245
setCapacity(rhs.capacity());
246246
setLen(rhs.len());
247247
rhs.setSSO(false);
248248
rhs.setCapacity(0);
249249
rhs.setLen(0);
250-
rhs.ptr.buf = nullptr;
250+
rhs.setBuffer(nullptr);
251251
}
252252
#endif
253253

@@ -785,9 +785,10 @@ void String::remove(unsigned int index, unsigned int count) {
785785
count = len() - index;
786786
}
787787
char *writeTo = wbuffer() + index;
788-
setLen(len() - count);
789-
memmove(writeTo, wbuffer() + index + count, len() - index);
790-
wbuffer()[len()] = 0;
788+
unsigned int newlen = len() - count;
789+
setLen(newlen);
790+
memmove(writeTo, wbuffer() + index + count, newlen - index);
791+
wbuffer()[newlen] = 0;
791792
}
792793

793794
void String::toLowerCase(void) {
@@ -815,10 +816,11 @@ void String::trim(void) {
815816
char *end = wbuffer() + len() - 1;
816817
while(isspace(*end) && end >= begin)
817818
end--;
818-
setLen(end + 1 - begin);
819+
unsigned int newlen = end + 1 - begin;
820+
setLen(newlen);
819821
if(begin > buffer())
820-
memmove(wbuffer(), begin, len());
821-
wbuffer()[len()] = 0;
822+
memmove(wbuffer(), begin, newlen);
823+
wbuffer()[newlen] = 0;
822824
}
823825

824826
// /*********************************************/

cores/esp8266/WString.h

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -243,17 +243,20 @@ class String {
243243
float toFloat(void) const;
244244

245245
protected:
246+
// Contains the string info when we're not in SSO mode
247+
struct _ptr {
248+
char * buff;
249+
uint16_t cap;
250+
uint16_t len;
251+
};
252+
246253
// SSO is handled by checking the last byte of sso_buff.
247254
// When not in SSO mode, that byte is set to 0xff, while when in SSO mode it is always 0x00 (so it can serve as the string terminator as well as a flag)
248255
// This allows strings up up to 12 (11 + \0 termination) without any extra space.
249-
enum { SSOSIZE = 12 }; // Characters to allocate space for SSO, must be 12 or more
256+
enum { SSOSIZE = sizeof(struct _ptr) + 4 }; // Characters to allocate space for SSO, must be 12 or more
250257
enum { CAPACITY_MAX = 65535 }; // If size of capacity changed, be sure to update this enum
251258
union {
252-
struct {
253-
uint16_t cap;
254-
uint16_t len;
255-
char * buf;
256-
} ptr;
259+
struct _ptr ptr;
257260
char sso_buf[SSOSIZE];
258261
};
259262
// Accessor functions
@@ -263,9 +266,10 @@ class String {
263266
inline void setSSO(bool sso) { sso_buf[SSOSIZE - 1] = sso ? 0x00 : 0xff; }
264267
inline void setLen(int len) { if (!sso()) ptr.len = len; }
265268
inline void setCapacity(int cap) { if (!sso()) ptr.cap = cap; }
269+
inline void setBuffer(char *buff) { if (!sso()) ptr.buff = buff; }
266270
// Buffer accessor functions
267-
inline const char *buffer() const { return (const char *)(sso() ? sso_buf : ptr.buf); }
268-
inline char *wbuffer() const { return sso() ? const_cast<char *>(sso_buf) : ptr.buf; } // Writable version of buffer
271+
inline const char *buffer() const { return (const char *)(sso() ? sso_buf : ptr.buff); }
272+
inline char *wbuffer() const { return sso() ? const_cast<char *>(sso_buf) : ptr.buff; } // Writable version of buffer
269273

270274
protected:
271275
void init(void);

tests/host/core/test_string.cpp

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -317,12 +317,47 @@ TEST_CASE("String SSO works", "[core][String]")
317317
REQUIRE(s.c_str() == savesso);
318318
REQUIRE(s == "0123456789a");
319319
REQUIRE(s.length() == 11);
320-
s += "b";
321-
REQUIRE(s.c_str() != savesso);
322-
REQUIRE(s == "0123456789ab");
323-
REQUIRE(s.length() == 12);
324-
s += "c";
325-
REQUIRE(s.c_str() != savesso);
326-
REQUIRE(s == "0123456789abc");
327-
REQUIRE(s.length() == 13);
320+
if (sizeof(savesso) == 4) {
321+
s += "b";
322+
REQUIRE(s.c_str() != savesso);
323+
REQUIRE(s == "0123456789ab");
324+
REQUIRE(s.length() == 12);
325+
s += "c";
326+
REQUIRE(s.c_str() != savesso);
327+
REQUIRE(s == "0123456789abc");
328+
REQUIRE(s.length() == 13);
329+
} else {
330+
s += "bcde";
331+
REQUIRE(s.c_str() == savesso);
332+
REQUIRE(s == "0123456789abcde");
333+
REQUIRE(s.length() == 15);
334+
s += "fghi";
335+
REQUIRE(s.c_str() == savesso);
336+
REQUIRE(s == "0123456789abcdefghi");
337+
REQUIRE(s.length() == 19);
338+
s += "j";
339+
REQUIRE(s.c_str() != savesso);
340+
REQUIRE(s == "0123456789abcdefghij");
341+
REQUIRE(s.length() == 20);
342+
s += "k";
343+
REQUIRE(s.c_str() != savesso);
344+
REQUIRE(s == "0123456789abcdefghijk");
345+
REQUIRE(s.length() == 21);
346+
s += "l";
347+
REQUIRE(s.c_str() != savesso);
348+
REQUIRE(s == "0123456789abcdefghijkl");
349+
REQUIRE(s.length() == 22);
350+
s += "m";
351+
REQUIRE(s.c_str() != savesso);
352+
REQUIRE(s == "0123456789abcdefghijklm");
353+
REQUIRE(s.length() == 23);
354+
s += "nopq";
355+
REQUIRE(s.c_str() != savesso);
356+
REQUIRE(s == "0123456789abcdefghijklmnopq");
357+
REQUIRE(s.length() == 27);
358+
s += "rstu";
359+
REQUIRE(s.c_str() != savesso);
360+
REQUIRE(s == "0123456789abcdefghijklmnopqrstu");
361+
REQUIRE(s.length() == 31);
362+
}
328363
}

0 commit comments

Comments
 (0)