Skip to content

Commit e8feba9

Browse files
committed
Fix stack buffer overflow in LocalizeRadix()
LocalizeRadix() uses strcpy() to copy the remainder of the input string into an 80-byte stack buffer without bounds checking. When parsing a floating-point number longer than ~78 characters in a locale where the radix character differs from '.', the strcpy overflows the buffer. The fix replaces the fixed-size stack buffer with a dynamically sized one (stack-allocated up to 256 bytes, heap-allocated for longer strings), and uses memcpy with explicit length instead of strcpy. Reproducer: LC_ALL=de_DE.UTF-8 Parse float string "1.000...001" with >80 characters -> AddressSanitizer: stack-buffer-overflow in LocalizeRadix (strtod.c:43)
1 parent 25445bc commit e8feba9

File tree

1 file changed

+19
-5
lines changed

1 file changed

+19
-5
lines changed

upb/lex/strtod.c

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,20 @@ static int GetLocaleRadix(char *data, size_t capacity) {
3131

3232
// Populates a string identical to *input except that the character pointed to
3333
// by pos (which should be '.') is replaced with the locale-specific radix.
34+
// output must have at least input_len + 8 bytes of space.
3435

35-
static void LocalizeRadix(const char *input, const char *pos, char *output) {
36-
const int len1 = pos - input;
36+
static void LocalizeRadix(const char *input, size_t input_len, const char *pos,
37+
char *output) {
38+
const size_t len1 = pos - input;
3739

3840
char radix[8];
3941
const int len2 = GetLocaleRadix(radix, sizeof(radix));
42+
const size_t len3 = input_len - len1 - 1;
4043

4144
memcpy(output, input, len1);
4245
memcpy(output + len1, radix, len2);
43-
strcpy(output + len1 + len2, input + len1 + 1);
46+
memcpy(output + len1 + len2, input + len1 + 1, len3);
47+
output[len1 + len2 + len3] = '\0';
4448
}
4549

4650
double _upb_NoLocaleStrtod(const char *str, char **endptr) {
@@ -59,8 +63,16 @@ double _upb_NoLocaleStrtod(const char *str, char **endptr) {
5963
// try to replace the '.' with a locale-specific radix character and
6064
// try again.
6165

62-
char localized[80];
63-
LocalizeRadix(str, temp_endptr, localized);
66+
const size_t str_len = strlen(str);
67+
// Extra space for locale radix which may be multi-byte.
68+
const size_t buf_size = str_len + 8;
69+
char stack_buf[256];
70+
char *localized = buf_size <= sizeof(stack_buf)
71+
? stack_buf
72+
: (char *)malloc(buf_size);
73+
if (!localized) return result;
74+
75+
LocalizeRadix(str, str_len, temp_endptr, localized);
6476
char *localized_endptr;
6577
result = strtod(localized, &localized_endptr);
6678
if ((localized_endptr - &localized[0]) > (temp_endptr - str)) {
@@ -73,5 +85,7 @@ double _upb_NoLocaleStrtod(const char *str, char **endptr) {
7385
}
7486
}
7587

88+
if (localized != stack_buf) free(localized);
89+
7690
return result;
7791
}

0 commit comments

Comments
 (0)