Skip to content

Commit 25a8c9a

Browse files
committed
Fix crash when using String::move on empty string (#10938)
Fixes: #10938 Keep allocated memory when rhs fits Use case: Appending to a String with pre-allocated memory (e.g. from `reserve()`) No need to move 0-termination char in String::move Simplify calls to String::copy A lot of the same checks were done before calling `copy()` which should be done in the `copy()` function itself. String::copy() Should not copy more than given length Fix potential out of range in String::concat There is no prerequisite the given array has to be a 0-terminated char array. So we should only copy the length that has been given. The `setLen()` function will make sure the internal string is 0-terminated. So no need to dangerously assume there will be 1 more byte to copy Allow String::concat(const String &s) with s.buffer() == nullptr When constructing a String object, the internal buffer is a nullptr. However concatenating this to another String would return `false` while this is perfectly fine to do.
1 parent 5ba4c21 commit 25a8c9a

File tree

1 file changed

+21
-33
lines changed

1 file changed

+21
-33
lines changed

cores/esp32/WString.cpp

+21-33
Original file line numberDiff line numberDiff line change
@@ -226,11 +226,11 @@ bool String::changeBuffer(unsigned int maxStrLen) {
226226
/*********************************************/
227227

228228
String &String::copy(const char *cstr, unsigned int length) {
229-
if (!reserve(length)) {
229+
if (cstr == nullptr || !reserve(length)) {
230230
invalidate();
231231
return *this;
232232
}
233-
memmove(wbuffer(), cstr, length + 1);
233+
memmove(wbuffer(), cstr, length);
234234
setLen(length);
235235
return *this;
236236
}
@@ -239,15 +239,18 @@ String &String::copy(const char *cstr, unsigned int length) {
239239
void String::move(String &rhs) {
240240
if (buffer()) {
241241
if (capacity() >= rhs.len()) {
242-
memmove(wbuffer(), rhs.buffer(), rhs.length() + 1);
242+
// Use case: When 'reserve()' was called and the first
243+
// assignment/append is the return value of a function.
244+
if (rhs.len() && rhs.buffer()) {
245+
memmove(wbuffer(), rhs.buffer(), rhs.length());
246+
}
243247
setLen(rhs.len());
244248
rhs.invalidate();
245249
return;
246-
} else {
247-
if (!isSSO()) {
248-
free(wbuffer());
249-
setBuffer(nullptr);
250-
}
250+
}
251+
if (!isSSO()) {
252+
free(wbuffer());
253+
setBuffer(nullptr);
251254
}
252255
}
253256
if (rhs.isSSO()) {
@@ -259,23 +262,15 @@ void String::move(String &rhs) {
259262
}
260263
setCapacity(rhs.capacity());
261264
setLen(rhs.len());
262-
rhs.setSSO(false);
263-
rhs.setCapacity(0);
264-
rhs.setBuffer(nullptr);
265-
rhs.setLen(0);
265+
rhs.init();
266266
}
267267
#endif
268268

269269
String &String::operator=(const String &rhs) {
270270
if (this == &rhs) {
271271
return *this;
272272
}
273-
if (rhs.buffer()) {
274-
copy(rhs.buffer(), rhs.len());
275-
} else {
276-
invalidate();
277-
}
278-
return *this;
273+
return copy(rhs.buffer(), rhs.len());
279274
}
280275

281276
#ifdef __GXX_EXPERIMENTAL_CXX0X__
@@ -295,12 +290,7 @@ String &String::operator=(StringSumHelper &&rval) {
295290
#endif
296291

297292
String &String::operator=(const char *cstr) {
298-
if (cstr) {
299-
copy(cstr, strlen(cstr));
300-
} else {
301-
invalidate();
302-
}
303-
return *this;
293+
return copy(cstr, strlen(cstr));
304294
}
305295

306296
/*********************************************/
@@ -311,23 +301,21 @@ bool String::concat(const String &s) {
311301
// Special case if we're concatting ourself (s += s;) since we may end up
312302
// realloc'ing the buffer and moving s.buffer in the method called
313303
if (&s == this) {
314-
unsigned int newlen = 2 * len();
315-
if (!s.buffer()) {
316-
return false;
317-
}
318304
if (s.len() == 0) {
319305
return true;
320306
}
307+
if (!s.buffer()) {
308+
return false;
309+
}
310+
unsigned int newlen = 2 * len();
321311
if (!reserve(newlen)) {
322312
return false;
323313
}
324314
memmove(wbuffer() + len(), buffer(), len());
325315
setLen(newlen);
326-
wbuffer()[len()] = 0;
327316
return true;
328-
} else {
329-
return concat(s.buffer(), s.len());
330317
}
318+
return concat(s.buffer(), s.len());
331319
}
332320

333321
bool String::concat(const char *cstr, unsigned int length) {
@@ -343,10 +331,10 @@ bool String::concat(const char *cstr, unsigned int length) {
343331
}
344332
if (cstr >= wbuffer() && cstr < wbuffer() + len()) {
345333
// compatible with SSO in ram #6155 (case "x += x.c_str()")
346-
memmove(wbuffer() + len(), cstr, length + 1);
334+
memmove(wbuffer() + len(), cstr, length);
347335
} else {
348336
// compatible with source in flash #6367
349-
memcpy_P(wbuffer() + len(), cstr, length + 1);
337+
memcpy_P(wbuffer() + len(), cstr, length);
350338
}
351339
setLen(newlen);
352340
return true;

0 commit comments

Comments
 (0)