Fix scanf() %n off by one at eof

Fixes #1094
This commit is contained in:
Justine Tunney 2024-01-20 15:06:16 -08:00
parent d50064a779
commit 39b0a9c03e
No known key found for this signature in database
GPG key ID: BE714B4575D6E328
2 changed files with 103 additions and 77 deletions

View file

@ -37,18 +37,18 @@
}) })
#define FP_BUFFER_GROW 48 #define FP_BUFFER_GROW 48
#define BUFFER \ #define BUFFER \
({ \ ({ \
int c = READ; \ int c = READ; \
if (fpbufcur >= fpbufsize - 1) { \ if (fpbufcur >= fpbufsize - 1) { \
fpbufsize = fpbufsize + FP_BUFFER_GROW; \ fpbufsize = fpbufsize + FP_BUFFER_GROW; \
fpbuf = realloc(fpbuf, fpbufsize); \ fpbuf = realloc(fpbuf, fpbufsize); \
} \ } \
if (c != -1) { \ if (c != -1) { \
fpbuf[fpbufcur++] = c; \ fpbuf[fpbufcur++] = c; \
fpbuf[fpbufcur] = '\0'; \ fpbuf[fpbufcur] = '\0'; \
} \ } \
c; \ c; \
}) })
/** /**
@ -236,8 +236,9 @@ int __vcscanf(int callback(void *), //
case 'f': case 'f':
case 'F': case 'F':
case 'g': case 'g':
case 'G': // floating point number case 'G': // floating point number
if (!(charbytes == sizeof(char) || charbytes == sizeof(wchar_t))) { if (!(charbytes == sizeof(char) ||
charbytes == sizeof(wchar_t))) {
items = -1; items = -1;
goto Done; goto Done;
} }
@ -361,7 +362,8 @@ int __vcscanf(int callback(void *), //
c = BUFFER; c = BUFFER;
do { do {
bool isdigit = c >= '0' && c <= '9'; bool isdigit = c >= '0' && c <= '9';
bool isletter = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); bool isletter =
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
if (!(c == '_' || isdigit || isletter)) { if (!(c == '_' || isdigit || isletter)) {
goto Done; goto Done;
} }
@ -420,72 +422,76 @@ int __vcscanf(int callback(void *), //
goto Done; goto Done;
} }
} }
BufferFloatingPointNumber: BufferFloatingPointNumber:
enum { INTEGER, FRACTIONAL, SIGN, EXPONENT } state = INTEGER; enum { INTEGER, FRACTIONAL, SIGN, EXPONENT } state = INTEGER;
do { do {
bool isdecdigit = c >= '0' && c <= '9'; bool isdecdigit = c >= '0' && c <= '9';
bool ishexdigit = (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'); bool ishexdigit = (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
bool ispoint = c == '.' || c == ','; bool ispoint = c == '.' || c == ',';
bool isdecexp = c == 'e' || c == 'E'; bool isdecexp = c == 'e' || c == 'E';
bool ishexp = c == 'p' || c == 'P'; bool ishexp = c == 'p' || c == 'P';
bool issign = c == '+' || c == '-'; bool issign = c == '+' || c == '-';
switch (state) { switch (state) {
case INTEGER: case INTEGER:
case FRACTIONAL: case FRACTIONAL:
if (isdecdigit || (hexadecimal && ishexdigit)) { if (isdecdigit || (hexadecimal && ishexdigit)) {
goto Continue; goto Continue;
} else if (state == INTEGER && ispoint) { } else if (state == INTEGER && ispoint) {
state = FRACTIONAL; state = FRACTIONAL;
goto Continue; goto Continue;
} else if (isdecexp || (hexadecimal && ishexp)) { } else if (isdecexp || (hexadecimal && ishexp)) {
state = SIGN; state = SIGN;
goto Continue; goto Continue;
} else { } else {
goto Break;
}
case SIGN:
if (issign) {
state = EXPONENT;
goto Continue;
}
state = EXPONENT;
// fallthrough
case EXPONENT:
if (isdecdigit) {
goto Continue;
} else {
goto Break;
}
default:
goto Break; goto Break;
}
Continue:
continue;
Break:
if (c != -1 && unget) {
unget(c, arg);
} }
break; case SIGN:
} while ((c = BUFFER) != -1); if (issign) {
GotFloatingPointNumber: state = EXPONENT;
fp = strtod((char *)fpbuf, NULL); goto Continue;
if (!discard) { }
++items; state = EXPONENT;
void *out = va_arg(va, void *); // fallthrough
if (charbytes == sizeof(char)) { case EXPONENT:
*(float *)out = (float)fp; if (isdecdigit) {
} else { goto Continue;
*(double *)out = (double)fp; } else {
} goto Break;
}
default:
goto Break;
} }
Continue:
continue;
Break:
if (c != -1 && unget) {
unget(c, arg);
}
break;
} while ((c = BUFFER) != -1);
GotFloatingPointNumber:
fp = strtod((char *)fpbuf, NULL);
if (!discard) {
++items;
void *out = va_arg(va, void *);
if (charbytes == sizeof(char)) {
*(float *)out = (float)fp;
} else {
*(double *)out = (double)fp;
}
}
free(fpbuf); free(fpbuf);
fpbuf = NULL; fpbuf = NULL;
fpbufcur = fpbufsize = 0; fpbufcur = fpbufsize = 0;
continue; continue;
ReportConsumed: ReportConsumed:
n_ptr = va_arg(va, int *); n_ptr = va_arg(va, int *);
*n_ptr = consumed - 1; // minus lookahead if (c != -1) {
*n_ptr = consumed - 1; // minus lookahead
} else {
*n_ptr = consumed;
}
continue; continue;
DecodeString: DecodeString:
bufsize = !width ? 32 : rawmode ? width : width + 1; bufsize = !width ? 32 : rawmode ? width : width + 1;
@ -545,7 +551,7 @@ int __vcscanf(int callback(void *), //
} }
++items; ++items;
if (ismalloc) { if (ismalloc) {
*va_arg(va, char **) = (void *) buf; *va_arg(va, char **) = (void *)buf;
} }
buf = NULL; buf = NULL;
} else { } else {

View file

@ -383,7 +383,8 @@ TEST(sscanf, floating_point_infinity) {
TEST(sscanf, floating_point_infinity_double_precision) { TEST(sscanf, floating_point_infinity_double_precision) {
double a = 666.666, b = a, c = b, d = c, e = d, f = e, g = f; double a = 666.666, b = a, c = b, d = c, e = d, f = e, g = f;
EXPECT_EQ(4, sscanf("inf +INF -iNf InF", "%lf %lf %lf %lf", &a, &b, &c, &d)); EXPECT_EQ(4, sscanf("inf +INF -iNf InF", "%lf %lf %lf %lf", &a, &b, &c, &d));
EXPECT_EQ(3, sscanf("+infinity -INFINITY iNfInItY", "%lf %lf %lf", &e, &f, &g)); EXPECT_EQ(3,
sscanf("+infinity -INFINITY iNfInItY", "%lf %lf %lf", &e, &f, &g));
EXPECT_TRUE(isinf(a)); EXPECT_TRUE(isinf(a));
EXPECT_TRUE(isinf(b)); EXPECT_TRUE(isinf(b));
EXPECT_TRUE(isinf(c)); EXPECT_TRUE(isinf(c));
@ -394,11 +395,14 @@ TEST(sscanf, floating_point_infinity_double_precision) {
} }
TEST(sscanf, floating_point_documentation_examples) { TEST(sscanf, floating_point_documentation_examples) {
float a = 666.666f, b = a, c = b, d = c, e = d, f = e, g = f, h = g, i = h, j = i; float a = 666.666f, b = a, c = b, d = c, e = d, f = e, g = f, h = g, i = h,
j = i;
EXPECT_EQ(2, sscanf("111.11 -2.22", "%f %f", &a, &b)); EXPECT_EQ(2, sscanf("111.11 -2.22", "%f %f", &a, &b));
EXPECT_EQ(3, sscanf("Nan nan(2) inF", "%f %f %f", &c, &d, &e)); EXPECT_EQ(3, sscanf("Nan nan(2) inF", "%f %f %f", &c, &d, &e));
EXPECT_EQ(5, sscanf("0X1.BC70A3D70A3D7P+6 1.18973e+4932zzz -0.0000000123junk junk", "%f %f %f %f %f", &f, &g, &h, &i, &j)); EXPECT_EQ(
5, sscanf("0X1.BC70A3D70A3D7P+6 1.18973e+4932zzz -0.0000000123junk junk",
"%f %f %f %f %f", &f, &g, &h, &i, &j));
EXPECT_EQ(111.11f, a); EXPECT_EQ(111.11f, a);
EXPECT_EQ(-2.22f, b); EXPECT_EQ(-2.22f, b);
@ -413,11 +417,14 @@ TEST(sscanf, floating_point_documentation_examples) {
} }
TEST(sscanf, floating_point_documentation_examples_double_precision) { TEST(sscanf, floating_point_documentation_examples_double_precision) {
double a = 666.666, b = a, c = b, d = c, e = d, f = e, g = f, h = g, i = h, j = i; double a = 666.666, b = a, c = b, d = c, e = d, f = e, g = f, h = g, i = h,
j = i;
EXPECT_EQ(2, sscanf("111.11 -2.22", "%lf %lf", &a, &b)); EXPECT_EQ(2, sscanf("111.11 -2.22", "%lf %lf", &a, &b));
EXPECT_EQ(3, sscanf("Nan nan(2) inF", "%lf %lf %lf", &c, &d, &e)); EXPECT_EQ(3, sscanf("Nan nan(2) inF", "%lf %lf %lf", &c, &d, &e));
EXPECT_EQ(5, sscanf("0X1.BC70A3D70A3D7P+6 1.18973e+4932zzz -0.0000000123junk junk", "%lf %lf %lf %lf %lf", &f, &g, &h, &i, &j)); EXPECT_EQ(
5, sscanf("0X1.BC70A3D70A3D7P+6 1.18973e+4932zzz -0.0000000123junk junk",
"%lf %lf %lf %lf %lf", &f, &g, &h, &i, &j));
EXPECT_EQ(111.11, a); EXPECT_EQ(111.11, a);
EXPECT_EQ(-2.22, b); EXPECT_EQ(-2.22, b);
@ -470,3 +477,16 @@ TEST(fscanf, wantDecimalButGotLetter_returnsZeroMatches) {
EXPECT_EQ(666, x); EXPECT_EQ(666, x);
fclose(f); fclose(f);
} }
TEST(scanf, n) {
int rc;
unsigned int a, b, c, d, port, len;
rc = sscanf("1.2.3.4:1848", "%u.%u.%u.%u:%u%n", &a, &b, &c, &d, &port, &len);
ASSERT_EQ(5, rc);
ASSERT_EQ(1, a);
ASSERT_EQ(2, b);
ASSERT_EQ(3, c);
ASSERT_EQ(4, d);
ASSERT_EQ(1848, port);
ASSERT_EQ(12, len);
}