ww source: reject malformed UTF-8
This commit is contained in:
@@ -240,6 +240,113 @@ static const struct {
|
||||
{ "f\0\0n", sizeof "f\0\0n" - 1, "fn", 2 },
|
||||
};
|
||||
|
||||
/* Go 1.26.5 source.nextch reports each DecodeRune width-one result and
|
||||
* removes that byte before scanner recovery. */
|
||||
static const struct {
|
||||
const char *src;
|
||||
size_t len;
|
||||
const char *expect;
|
||||
int errs;
|
||||
} utf8rows[] = {
|
||||
{ "f" "\xff" "n bar", sizeof "f" "\xff" "n bar" - 1,
|
||||
"fn IDENT(bar)", 1 },
|
||||
{ "f" "\x80" "n bar", sizeof "f" "\x80" "n bar" - 1,
|
||||
"fn IDENT(bar)", 1 },
|
||||
{ "f" "\xc0\x80" "n bar", sizeof "f" "\xc0\x80" "n bar" - 1,
|
||||
"fn IDENT(bar)", 2 },
|
||||
{ "f" "\xed\xa0\x80" "n bar",
|
||||
sizeof "f" "\xed\xa0\x80" "n bar" - 1,
|
||||
"fn IDENT(bar)", 3 },
|
||||
{ "f" "\xf4\x90\x80\x80" "n bar",
|
||||
sizeof "f" "\xf4\x90\x80\x80" "n bar" - 1,
|
||||
"fn IDENT(bar)", 4 },
|
||||
{ "f" "\xe2\x82" "n bar", sizeof "f" "\xe2\x82" "n bar" - 1,
|
||||
"fn IDENT(bar)", 2 },
|
||||
{ "1" "\xff" "_0", sizeof "1" "\xff" "_0" - 1,
|
||||
"INT(10)", 1 },
|
||||
{ "=" "\xff" "=", sizeof "=" "\xff" "=" - 1,
|
||||
"==", 1 },
|
||||
{ "/" "\xff" "/ comment\nfn",
|
||||
sizeof "/" "\xff" "/ comment\nfn" - 1, "fn", 1 },
|
||||
{ "/* end *" "\xff" "/ fn",
|
||||
sizeof "/* end *" "\xff" "/ fn" - 1, "fn", 1 },
|
||||
{ "\"a" "\xff" "b\"", sizeof "\"a" "\xff" "b\"" - 1,
|
||||
"STR(ab)", 1 },
|
||||
};
|
||||
|
||||
static int
|
||||
runutf8valid(void)
|
||||
{
|
||||
static const char strsrc[] =
|
||||
"\"" "\xc3\xa9" "\xea\xb0\x80" "\xef\xbf\xbd"
|
||||
"\xf0\x9f\x98\x80" "\" fn";
|
||||
static const char commentsrc[] =
|
||||
"// " "\xc3\xa9" "\xea\xb0\x80" "\xef\xbf\xbd"
|
||||
"\xf0\x9f\x98\x80" "\nfn";
|
||||
static const unsigned char want[] = {
|
||||
0xc3, 0xa9, 0xea, 0xb0, 0x80, 0xef, 0xbf, 0xbd,
|
||||
0xf0, 0x9f, 0x98, 0x80,
|
||||
};
|
||||
Arena *a = newarena();
|
||||
Lex l;
|
||||
lexinit(&l, a, "<test>", strsrc, sizeof strsrc - 1);
|
||||
Tok s = lexnext(&l);
|
||||
Tok f = lexnext(&l);
|
||||
int ok = s.kind == TK_STR && s.tlen == sizeof want
|
||||
&& memcmp(s.text, want, sizeof want) == 0
|
||||
&& f.kind == TK_FN && f.pos.col == 16 && l.errs == 0
|
||||
&& lexnext(&l).kind == TK_EOF;
|
||||
lexinit(&l, a, "<test>", commentsrc, sizeof commentsrc - 1);
|
||||
f = lexnext(&l);
|
||||
ok = ok && f.kind == TK_FN && f.pos.line == 2 && f.pos.col == 1
|
||||
&& l.errs == 0 && lexnext(&l).kind == TK_EOF;
|
||||
if (!ok)
|
||||
fprintf(stderr, "valid UTF-8 preservation failed\n");
|
||||
freearena(a);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
runutf8cols(void)
|
||||
{
|
||||
static const char src[] =
|
||||
"\xef\xbb\xbf" "f" "\xff" "\0" "n bar";
|
||||
Arena *a = newarena();
|
||||
Lex l;
|
||||
lexinit(&l, a, "<test>", src, sizeof src - 1);
|
||||
Tok f = lexnext(&l);
|
||||
Tok b = lexnext(&l);
|
||||
int ok = f.kind == TK_FN && f.pos.line == 1 && f.pos.col == 4
|
||||
&& b.kind == TK_IDENT && b.pos.line == 1 && b.pos.col == 9
|
||||
&& strcmp(b.text, "bar") == 0 && l.errs == 2
|
||||
&& l.nulcount == 1 && lexnext(&l).kind == TK_EOF;
|
||||
if (!ok)
|
||||
fprintf(stderr, "UTF-8 raw-byte columns or BOM/NUL recovery failed\n");
|
||||
freearena(a);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
runutf8escape(void)
|
||||
{
|
||||
static const char line[] = "\"\\" "\xff" "n\"";
|
||||
static const char hex[] = "\"\\x0" "\xff" "0\"";
|
||||
Arena *a = newarena();
|
||||
Lex l;
|
||||
lexinit(&l, a, "<test>", line, sizeof line - 1);
|
||||
Tok t = lexnext(&l);
|
||||
int ok = t.kind == TK_STR && t.tlen == 1 && t.text[0] == '\n'
|
||||
&& l.errs == 1 && lexnext(&l).kind == TK_EOF;
|
||||
lexinit(&l, a, "<test>", hex, sizeof hex - 1);
|
||||
t = lexnext(&l);
|
||||
ok = ok && t.kind == TK_STR && t.tlen == 1 && t.text[0] == '\0'
|
||||
&& l.errs == 1 && lexnext(&l).kind == TK_EOF;
|
||||
if (!ok)
|
||||
fprintf(stderr, "UTF-8 escape recovery failed\n");
|
||||
freearena(a);
|
||||
return ok;
|
||||
}
|
||||
|
||||
static int
|
||||
runescapenul(void)
|
||||
{
|
||||
@@ -333,10 +440,23 @@ main(void)
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < sizeof utf8rows / sizeof utf8rows[0]; i++) {
|
||||
if (!runrown(utf8rows[i].src, utf8rows[i].len,
|
||||
utf8rows[i].expect, utf8rows[i].errs)) {
|
||||
fprintf(stderr, "UTF-8 row %zu failed\n", i);
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
if (!runescapenul())
|
||||
fail++;
|
||||
if (!runsuffixnul())
|
||||
fail++;
|
||||
if (!runutf8valid())
|
||||
fail++;
|
||||
if (!runutf8cols())
|
||||
fail++;
|
||||
if (!runutf8escape())
|
||||
fail++;
|
||||
if (!runlongdirective())
|
||||
fail++;
|
||||
if (fail) {
|
||||
|
||||
Reference in New Issue
Block a user