Skip to content

Commit 1949f7d

Browse files
authored
[scudo] Clean up string handling (#86364)
Do not abort if a vector cannot increase its own capacity. In that case, push_back calls silently fail. Modify the ScopedString implementation so that it no longer requires two passes to do the format. Move the helper functions to be private member functions so that they can use push_back directly. This allows the capacity to be increased under the hood and/or silently discards data if the capacity is exceeded and cannot be increased. Add new tests for the Vector and ScopedString for capacity increase failures. Doing this so that if a map call fails, and we are attempting to write an error string, we can still get some of the message dumped. This also avoids crashing in Scudo code, and makes the caller handle any failures.
1 parent 630283c commit 1949f7d

File tree

8 files changed

+165
-123
lines changed

8 files changed

+165
-123
lines changed

compiler-rt/lib/scudo/standalone/fuchsia.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,10 @@ static_assert(ZX_HANDLE_INVALID == 0, "");
3434

3535
static void NORETURN dieOnError(zx_status_t Status, const char *FnName,
3636
uptr Size) {
37-
char Error[128];
38-
formatString(Error, sizeof(Error),
39-
"SCUDO ERROR: %s failed with size %zuKB (%s)", FnName,
37+
ScopedString Error;
38+
Error.append("SCUDO ERROR: %s failed with size %zuKB (%s)", FnName,
4039
Size >> 10, zx_status_get_string(Status));
41-
outputRaw(Error);
40+
outputRaw(Error.data());
4241
die();
4342
}
4443

compiler-rt/lib/scudo/standalone/mem_map_fuchsia.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,10 @@ namespace scudo {
2222

2323
static void NORETURN dieOnError(zx_status_t Status, const char *FnName,
2424
uptr Size) {
25-
char Error[128];
26-
formatString(Error, sizeof(Error),
27-
"SCUDO ERROR: %s failed with size %zuKB (%s)", FnName,
25+
ScopedString Error;
26+
Error.append("SCUDO ERROR: %s failed with size %zuKB (%s)", FnName,
2827
Size >> 10, _zx_status_get_string(Status));
29-
outputRaw(Error);
28+
outputRaw(Error.data());
3029
die();
3130
}
3231

compiler-rt/lib/scudo/standalone/report_linux.cpp

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,33 +24,30 @@ namespace scudo {
2424

2525
// Fatal internal map() error (potentially OOM related).
2626
void NORETURN reportMapError(uptr SizeIfOOM) {
27-
char Error[128] = "Scudo ERROR: internal map failure\n";
27+
ScopedString Error;
28+
Error.append("Scudo ERROR: internal map failure");
2829
if (SizeIfOOM) {
29-
formatString(
30-
Error, sizeof(Error),
31-
"Scudo ERROR: internal map failure (NO MEMORY) requesting %zuKB\n",
32-
SizeIfOOM >> 10);
30+
Error.append(" (NO MEMORY) requesting %zuKB", SizeIfOOM >> 10);
3331
}
34-
reportRawError(Error);
32+
Error.append("\n");
33+
reportRawError(Error.data());
3534
}
3635

3736
void NORETURN reportUnmapError(uptr Addr, uptr Size) {
38-
char Error[128];
39-
formatString(Error, sizeof(Error),
40-
"Scudo ERROR: internal unmap failure (error desc=%s) Addr 0x%zx "
37+
ScopedString Error;
38+
Error.append("Scudo ERROR: internal unmap failure (error desc=%s) Addr 0x%zx "
4139
"Size %zu\n",
4240
strerror(errno), Addr, Size);
43-
reportRawError(Error);
41+
reportRawError(Error.data());
4442
}
4543

4644
void NORETURN reportProtectError(uptr Addr, uptr Size, int Prot) {
47-
char Error[128];
48-
formatString(
49-
Error, sizeof(Error),
45+
ScopedString Error;
46+
Error.append(
5047
"Scudo ERROR: internal protect failure (error desc=%s) Addr 0x%zx "
5148
"Size %zu Prot %x\n",
5249
strerror(errno), Addr, Size, Prot);
53-
reportRawError(Error);
50+
reportRawError(Error.data());
5451
}
5552

5653
} // namespace scudo

compiler-rt/lib/scudo/standalone/string_utils.cpp

Lines changed: 56 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -14,30 +14,21 @@
1414

1515
namespace scudo {
1616

17-
static int appendChar(char **Buffer, const char *BufferEnd, char C) {
18-
if (*Buffer < BufferEnd) {
19-
**Buffer = C;
20-
(*Buffer)++;
21-
}
22-
return 1;
23-
}
24-
2517
// Appends number in a given Base to buffer. If its length is less than
2618
// |MinNumberLength|, it is padded with leading zeroes or spaces, depending
2719
// on the value of |PadWithZero|.
28-
static int appendNumber(char **Buffer, const char *BufferEnd, u64 AbsoluteValue,
29-
u8 Base, u8 MinNumberLength, bool PadWithZero,
30-
bool Negative, bool Upper) {
20+
void ScopedString::appendNumber(u64 AbsoluteValue, u8 Base, u8 MinNumberLength,
21+
bool PadWithZero, bool Negative, bool Upper) {
3122
constexpr uptr MaxLen = 30;
3223
RAW_CHECK(Base == 10 || Base == 16);
3324
RAW_CHECK(Base == 10 || !Negative);
3425
RAW_CHECK(AbsoluteValue || !Negative);
3526
RAW_CHECK(MinNumberLength < MaxLen);
36-
int Res = 0;
3727
if (Negative && MinNumberLength)
3828
--MinNumberLength;
39-
if (Negative && PadWithZero)
40-
Res += appendChar(Buffer, BufferEnd, '-');
29+
if (Negative && PadWithZero) {
30+
String.push_back('-');
31+
}
4132
uptr NumBuffer[MaxLen];
4233
int Pos = 0;
4334
do {
@@ -55,79 +46,78 @@ static int appendNumber(char **Buffer, const char *BufferEnd, u64 AbsoluteValue,
5546
Pos--;
5647
for (; Pos >= 0 && NumBuffer[Pos] == 0; Pos--) {
5748
char c = (PadWithZero || Pos == 0) ? '0' : ' ';
58-
Res += appendChar(Buffer, BufferEnd, c);
49+
String.push_back(c);
5950
}
6051
if (Negative && !PadWithZero)
61-
Res += appendChar(Buffer, BufferEnd, '-');
52+
String.push_back('-');
6253
for (; Pos >= 0; Pos--) {
6354
char Digit = static_cast<char>(NumBuffer[Pos]);
6455
Digit = static_cast<char>((Digit < 10) ? '0' + Digit
6556
: (Upper ? 'A' : 'a') + Digit - 10);
66-
Res += appendChar(Buffer, BufferEnd, Digit);
57+
String.push_back(Digit);
6758
}
68-
return Res;
6959
}
7060

71-
static int appendUnsigned(char **Buffer, const char *BufferEnd, u64 Num,
72-
u8 Base, u8 MinNumberLength, bool PadWithZero,
73-
bool Upper) {
74-
return appendNumber(Buffer, BufferEnd, Num, Base, MinNumberLength,
75-
PadWithZero, /*Negative=*/false, Upper);
61+
void ScopedString::appendUnsigned(u64 Num, u8 Base, u8 MinNumberLength,
62+
bool PadWithZero, bool Upper) {
63+
appendNumber(Num, Base, MinNumberLength, PadWithZero, /*Negative=*/false,
64+
Upper);
7665
}
7766

78-
static int appendSignedDecimal(char **Buffer, const char *BufferEnd, s64 Num,
79-
u8 MinNumberLength, bool PadWithZero) {
67+
void ScopedString::appendSignedDecimal(s64 Num, u8 MinNumberLength,
68+
bool PadWithZero) {
8069
const bool Negative = (Num < 0);
8170
const u64 UnsignedNum = (Num == INT64_MIN)
8271
? static_cast<u64>(INT64_MAX) + 1
8372
: static_cast<u64>(Negative ? -Num : Num);
84-
return appendNumber(Buffer, BufferEnd, UnsignedNum, 10, MinNumberLength,
85-
PadWithZero, Negative, /*Upper=*/false);
73+
appendNumber(UnsignedNum, 10, MinNumberLength, PadWithZero, Negative,
74+
/*Upper=*/false);
8675
}
8776

8877
// Use the fact that explicitly requesting 0 Width (%0s) results in UB and
8978
// interpret Width == 0 as "no Width requested":
9079
// Width == 0 - no Width requested
9180
// Width < 0 - left-justify S within and pad it to -Width chars, if necessary
9281
// Width > 0 - right-justify S, not implemented yet
93-
static int appendString(char **Buffer, const char *BufferEnd, int Width,
94-
int MaxChars, const char *S) {
82+
void ScopedString::appendString(int Width, int MaxChars, const char *S) {
9583
if (!S)
9684
S = "<null>";
97-
int Res = 0;
85+
int NumChars = 0;
9886
for (; *S; S++) {
99-
if (MaxChars >= 0 && Res >= MaxChars)
87+
if (MaxChars >= 0 && NumChars >= MaxChars)
10088
break;
101-
Res += appendChar(Buffer, BufferEnd, *S);
89+
String.push_back(*S);
90+
NumChars++;
91+
}
92+
if (Width < 0) {
93+
// Only left justification supported.
94+
Width = -Width - NumChars;
95+
while (Width-- > 0)
96+
String.push_back(' ');
10297
}
103-
// Only the left justified strings are supported.
104-
while (Width < -Res)
105-
Res += appendChar(Buffer, BufferEnd, ' ');
106-
return Res;
10798
}
10899

109-
static int appendPointer(char **Buffer, const char *BufferEnd, u64 ptr_value) {
110-
int Res = 0;
111-
Res += appendString(Buffer, BufferEnd, 0, -1, "0x");
112-
Res += appendUnsigned(Buffer, BufferEnd, ptr_value, 16,
113-
SCUDO_POINTER_FORMAT_LENGTH, /*PadWithZero=*/true,
114-
/*Upper=*/false);
115-
return Res;
100+
void ScopedString::appendPointer(u64 ptr_value) {
101+
appendString(0, -1, "0x");
102+
appendUnsigned(ptr_value, 16, SCUDO_POINTER_FORMAT_LENGTH,
103+
/*PadWithZero=*/true,
104+
/*Upper=*/false);
116105
}
117106

118-
static int formatString(char *Buffer, uptr BufferLength, const char *Format,
119-
va_list Args) {
107+
void ScopedString::vappend(const char *Format, va_list &Args) {
108+
// Since the string contains the '\0' terminator, put our size before it
109+
// so that push_back calls work correctly.
110+
DCHECK(String.size() > 0);
111+
String.resize(String.size() - 1);
112+
120113
static const char *PrintfFormatsHelp =
121-
"Supported formatString formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
114+
"Supported formats: %([0-9]*)?(z|ll)?{d,u,x,X}; %p; "
122115
"%[-]([0-9]*)?(\\.\\*)?s; %c\n";
123116
RAW_CHECK(Format);
124-
RAW_CHECK(BufferLength > 0);
125-
const char *BufferEnd = &Buffer[BufferLength - 1];
126117
const char *Cur = Format;
127-
int Res = 0;
128118
for (; *Cur; Cur++) {
129119
if (*Cur != '%') {
130-
Res += appendChar(&Buffer, BufferEnd, *Cur);
120+
String.push_back(*Cur);
131121
continue;
132122
}
133123
Cur++;
@@ -162,7 +152,7 @@ static int formatString(char *Buffer, uptr BufferLength, const char *Format,
162152
DVal = HaveLL ? va_arg(Args, s64)
163153
: HaveZ ? va_arg(Args, sptr)
164154
: va_arg(Args, int);
165-
Res += appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero);
155+
appendSignedDecimal(DVal, Width, PadWithZero);
166156
break;
167157
}
168158
case 'u':
@@ -172,27 +162,25 @@ static int formatString(char *Buffer, uptr BufferLength, const char *Format,
172162
: HaveZ ? va_arg(Args, uptr)
173163
: va_arg(Args, unsigned);
174164
const bool Upper = (*Cur == 'X');
175-
Res += appendUnsigned(&Buffer, BufferEnd, UVal, (*Cur == 'u') ? 10 : 16,
176-
Width, PadWithZero, Upper);
165+
appendUnsigned(UVal, (*Cur == 'u') ? 10 : 16, Width, PadWithZero, Upper);
177166
break;
178167
}
179168
case 'p': {
180169
RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
181-
Res += appendPointer(&Buffer, BufferEnd, va_arg(Args, uptr));
170+
appendPointer(va_arg(Args, uptr));
182171
break;
183172
}
184173
case 's': {
185174
RAW_CHECK_MSG(!HaveLength, PrintfFormatsHelp);
186175
// Only left-justified Width is supported.
187176
CHECK(!HaveWidth || LeftJustified);
188-
Res += appendString(&Buffer, BufferEnd, LeftJustified ? -Width : Width,
189-
Precision, va_arg(Args, char *));
177+
appendString(LeftJustified ? -Width : Width, Precision,
178+
va_arg(Args, char *));
190179
break;
191180
}
192181
case 'c': {
193182
RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
194-
Res +=
195-
appendChar(&Buffer, BufferEnd, static_cast<char>(va_arg(Args, int)));
183+
String.push_back(static_cast<char>(va_arg(Args, int)));
196184
break;
197185
}
198186
// In Scudo, `s64`/`u64` are supposed to use `lld` and `llu` respectively.
@@ -207,55 +195,31 @@ static int formatString(char *Buffer, uptr BufferLength, const char *Format,
207195

208196
if (*Cur == 'd') {
209197
DVal = va_arg(Args, s64);
210-
Res +=
211-
appendSignedDecimal(&Buffer, BufferEnd, DVal, Width, PadWithZero);
198+
appendSignedDecimal(DVal, Width, PadWithZero);
212199
} else {
213200
UVal = va_arg(Args, u64);
214-
Res += appendUnsigned(&Buffer, BufferEnd, UVal, 10, Width, PadWithZero,
215-
false);
201+
appendUnsigned(UVal, 10, Width, PadWithZero, false);
216202
}
217203

218204
break;
219205
}
220206
case '%': {
221207
RAW_CHECK_MSG(!HaveFlags, PrintfFormatsHelp);
222-
Res += appendChar(&Buffer, BufferEnd, '%');
208+
String.push_back('%');
223209
break;
224210
}
225211
default: {
226212
RAW_CHECK_MSG(false, PrintfFormatsHelp);
227213
}
228214
}
229215
}
230-
RAW_CHECK(Buffer <= BufferEnd);
231-
appendChar(&Buffer, BufferEnd + 1, '\0');
232-
return Res;
233-
}
234-
235-
int formatString(char *Buffer, uptr BufferLength, const char *Format, ...) {
236-
va_list Args;
237-
va_start(Args, Format);
238-
int Res = formatString(Buffer, BufferLength, Format, Args);
239-
va_end(Args);
240-
return Res;
241-
}
242-
243-
void ScopedString::vappend(const char *Format, va_list Args) {
244-
va_list ArgsCopy;
245-
va_copy(ArgsCopy, Args);
246-
// formatString doesn't currently support a null buffer or zero buffer length,
247-
// so in order to get the resulting formatted string length, we use a one-char
248-
// buffer.
249-
char C[1];
250-
const uptr AdditionalLength =
251-
static_cast<uptr>(formatString(C, sizeof(C), Format, Args)) + 1;
252-
const uptr Length = length();
253-
String.resize(Length + AdditionalLength);
254-
const uptr FormattedLength = static_cast<uptr>(formatString(
255-
String.data() + Length, String.size() - Length, Format, ArgsCopy));
256-
RAW_CHECK(data()[length()] == '\0');
257-
RAW_CHECK(FormattedLength + 1 == AdditionalLength);
258-
va_end(ArgsCopy);
216+
String.push_back('\0');
217+
if (String.back() != '\0') {
218+
// String truncated, make sure the string is terminated properly.
219+
// This can happen if there is no more memory when trying to resize
220+
// the string.
221+
String.back() = '\0';
222+
}
259223
}
260224

261225
void ScopedString::append(const char *Format, ...) {

compiler-rt/lib/scudo/standalone/string_utils.h

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,17 +25,24 @@ class ScopedString {
2525
String.clear();
2626
String.push_back('\0');
2727
}
28-
void vappend(const char *Format, va_list Args);
28+
void vappend(const char *Format, va_list &Args);
2929
void append(const char *Format, ...) FORMAT(2, 3);
3030
void output() const { outputRaw(String.data()); }
3131
void reserve(size_t Size) { String.reserve(Size + 1); }
32+
uptr capacity() { return String.capacity() - 1; }
3233

3334
private:
35+
void appendNumber(u64 AbsoluteValue, u8 Base, u8 MinNumberLength,
36+
bool PadWithZero, bool Negative, bool Upper);
37+
void appendUnsigned(u64 Num, u8 Base, u8 MinNumberLength, bool PadWithZero,
38+
bool Upper);
39+
void appendSignedDecimal(s64 Num, u8 MinNumberLength, bool PadWithZero);
40+
void appendString(int Width, int MaxChars, const char *S);
41+
void appendPointer(u64 ptr_value);
42+
3443
Vector<char> String;
3544
};
3645

37-
int formatString(char *Buffer, uptr BufferLength, const char *Format, ...)
38-
FORMAT(3, 4);
3946
void Printf(const char *Format, ...) FORMAT(1, 2);
4047

4148
} // namespace scudo

0 commit comments

Comments
 (0)