Skip to content

Commit fde38a4

Browse files
committed
Fix parsing of escape sequences in strings
1 parent 84c7dc7 commit fde38a4

File tree

5 files changed

+292
-119
lines changed

5 files changed

+292
-119
lines changed

src/fallback/text.h

+38-63
Original file line numberDiff line numberDiff line change
@@ -27,77 +27,52 @@ static really_inline uint32_t unescape(const char *text, uint8_t *wire)
2727
}
2828

2929
nonnull_all
30-
static really_inline int32_t parse_text_inner(
31-
parser_t *parser,
32-
const type_info_t *type,
33-
const rdata_info_t *field,
34-
rdata_t *rdata,
35-
const token_t *token)
30+
static really_inline int32_t scan_string(
31+
const char *data,
32+
size_t length,
33+
uint8_t *octets,
34+
const uint8_t *limit)
3635
{
37-
uint32_t skip;
38-
const char *data = token->data, *limit = token->data + token->length;
36+
const char *text = data, *text_limit = data + length;
37+
uint8_t *wire = octets;
3938

40-
if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets >= token->length) {
41-
while (data < limit) {
42-
*rdata->octets = (uint8_t)*data;
43-
if (likely(*data != '\\'))
44-
(void)(rdata->octets += 1), data += 1;
45-
else if (!(skip = unescape(data, rdata->octets)))
46-
SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
47-
else
48-
(void)(rdata->octets += 1), data += skip;
39+
if (likely((uintptr_t)limit - (uintptr_t)wire >= length)) {
40+
while (text < text_limit) {
41+
*wire = (uint8_t)*text;
42+
if (likely(*text != '\\')) {
43+
text += 1;
44+
wire += 1;
45+
} else {
46+
const uint32_t octet = unescape(text, wire);
47+
if (!octet)
48+
return -1;
49+
text += octet;
50+
wire += 1;
51+
}
4952
}
5053

51-
if (data != limit)
52-
SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
53-
return 0;
54+
if (text != text_limit)
55+
return -1;
56+
return (int32_t)(wire - octets);
5457
} else {
55-
while (data < limit && rdata->octets < rdata->limit) {
56-
*rdata->octets = (uint8_t)*data;
57-
if (likely(*data != '\\'))
58-
(void)(rdata->octets += 1), data += 1;
59-
else if (!(skip = unescape(data, rdata->octets)))
60-
SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
61-
else
62-
(void)(rdata->octets += 1), data += skip;
58+
while (text < text_limit && wire < limit) {
59+
*wire = (uint8_t)*text;
60+
if (likely(*text != '\\')) {
61+
text += 1;
62+
wire += 1;
63+
} else {
64+
const uint32_t octet = unescape(text, wire);
65+
if (!octet)
66+
return -1;
67+
text += octet;
68+
wire += 1;
69+
}
6370
}
6471

65-
if (data != limit || rdata->octets >= rdata->limit)
66-
SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
67-
return 0;
72+
if (text != text_limit || wire > limit)
73+
return -1;
74+
return (int32_t)(wire - octets);
6875
}
6976
}
7077

71-
nonnull_all
72-
static really_inline int32_t parse_string(
73-
parser_t *parser,
74-
const type_info_t *type,
75-
const rdata_info_t *field,
76-
rdata_t *rdata,
77-
const token_t *token)
78-
{
79-
int32_t code;
80-
uint8_t *octets = rdata->octets;
81-
uint8_t *limit = rdata->limit;
82-
if ((uintptr_t)rdata->limit - (uintptr_t)rdata->octets > 1 + 255)
83-
rdata->limit = rdata->octets + 1 + 255;
84-
rdata->octets += 1;
85-
86-
code = parse_text_inner(parser, type, field, rdata, token);
87-
*octets = (uint8_t)((uintptr_t)rdata->octets - (uintptr_t)octets) - 1;
88-
rdata->limit = limit;
89-
return code;
90-
}
91-
92-
nonnull_all
93-
static really_inline int32_t parse_text(
94-
parser_t *parser,
95-
const type_info_t *type,
96-
const rdata_info_t *field,
97-
rdata_t *rdata,
98-
const token_t *token)
99-
{
100-
return parse_text_inner(parser, type, field, rdata, token);
101-
}
102-
10378
#endif // TEXT_H

src/generic/format.h

+41
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,47 @@ static really_inline int32_t parse_owner(
105105
return 0;
106106
}
107107

108+
nonnull_all
109+
static really_inline int32_t parse_string(
110+
parser_t *parser,
111+
const type_info_t *type,
112+
const rdata_info_t *field,
113+
rdata_t *rdata,
114+
const token_t *token)
115+
{
116+
if (rdata->limit == rdata->octets)
117+
SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(type), NAME(field));
118+
assert(rdata->limit > rdata->octets);
119+
120+
int32_t length;
121+
uint8_t *octets = rdata->octets + 1;
122+
const uint8_t *limit = rdata->limit;
123+
124+
if (rdata->limit - rdata->octets > (1 + 255))
125+
limit = rdata->octets + 1 + 255;
126+
if ((length = scan_string(token->data, token->length, octets, limit)) == -1)
127+
SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(type), NAME(field));
128+
*rdata->octets = (uint8_t)length;
129+
rdata->octets += 1u + (uint32_t)length;
130+
return 0;
131+
}
132+
133+
nonnull_all
134+
static really_inline int32_t parse_text(
135+
parser_t *parser,
136+
const type_info_t *type,
137+
const rdata_info_t *field,
138+
rdata_t *rdata,
139+
const token_t *token)
140+
{
141+
int32_t length;
142+
143+
if ((length = scan_string(token->data, token->length, rdata->octets, rdata->limit)) == -1)
144+
SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(type), NAME(field));
145+
rdata->octets += (uint32_t)length;
146+
return 0;
147+
}
148+
108149
nonnull_all
109150
static really_inline int32_t parse_rr(
110151
parser_t *parser, token_t *token)

src/generic/text.h

+51-56
Original file line numberDiff line numberDiff line change
@@ -44,69 +44,64 @@ static really_inline void copy_string_block(
4444
}
4545

4646
nonnull_all
47-
static really_inline int32_t parse_text_inner(
48-
parser_t *parser,
49-
const type_info_t *type,
50-
const rdata_info_t *field,
51-
rdata_t *rdata,
52-
const token_t *token)
47+
static really_inline int32_t scan_string(
48+
const char *data,
49+
size_t length,
50+
uint8_t *octets,
51+
const uint8_t *limit)
5352
{
54-
string_block_t b;
55-
const char *t = token->data, *te = t + token->length;
56-
uint64_t left = token->length;
53+
const char *text = data;
54+
uint8_t *wire = octets;
55+
string_block_t block;
5756

58-
while ((t < te) & (rdata->octets < rdata->limit)) {
59-
copy_string_block(&b, t, rdata->octets);
60-
uint64_t n = 32;
61-
if (left < 32)
62-
n = left;
63-
uint64_t mask = (1llu << n) - 1;
57+
copy_string_block(&block, text, octets);
6458

65-
if (unlikely(b.backslashes & mask)) {
66-
n = trailing_zeroes(b.backslashes);
67-
rdata->octets += n; t += n;
68-
if (!(n = unescape(t, rdata->octets)))
69-
SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
70-
rdata->octets += 1; t += n;
71-
} else {
72-
rdata->octets += n; t += n;
73-
}
74-
}
59+
uint64_t count = 32;
60+
if (length < 32)
61+
count = length;
62+
uint64_t mask = (1llu << count) - 1u;
7563

76-
if (rdata->octets >= rdata->limit)
77-
SYNTAX_ERROR(parser, "Invalid %s in %s", NAME(field), NAME(type));
78-
return 0;
79-
}
64+
// check for escape sequences
65+
if (unlikely(block.backslashes & mask))
66+
goto escaped;
8067

81-
nonnull_all
82-
static really_inline int32_t parse_string(
83-
parser_t *parser,
84-
const type_info_t *type,
85-
const rdata_info_t *field,
86-
rdata_t *rdata,
87-
const token_t *token)
88-
{
89-
int32_t code;
90-
uint8_t *octets = rdata->octets, *limit = rdata->limit;
91-
if (rdata->limit - rdata->octets > 1 + 255)
92-
rdata->limit = rdata->octets + 1 + 255;
93-
rdata->octets += 1;
68+
if (length < 32)
69+
return (int32_t)count;
9470

95-
code = parse_text_inner(parser, type, field, rdata, token);
96-
*octets = (uint8_t)((rdata->octets - octets) - 1);
97-
rdata->limit = limit;
98-
return code;
99-
}
71+
text += count;
72+
wire += count;
73+
length -= count;
10074

101-
nonnull_all
102-
static really_inline int32_t parse_text(
103-
zone_parser_t *parser,
104-
const type_info_t *type,
105-
const rdata_info_t *field,
106-
rdata_t *rdata,
107-
const token_t *token)
108-
{
109-
return parse_text_inner(parser, type, field, rdata, token);
75+
do {
76+
copy_string_block(&block, text, wire);
77+
count = 32;
78+
if (length < 32)
79+
count = length;
80+
mask = (1llu << count) - 1u;
81+
82+
// check for escape sequences
83+
if (unlikely(block.backslashes & mask)) {
84+
escaped:
85+
block.backslashes &= -block.backslashes;
86+
mask = block.backslashes - 1;
87+
count = count_ones(mask);
88+
const uint32_t octet = unescape(text+count, wire+count);
89+
if (!octet)
90+
return -1;
91+
text += count + octet;
92+
wire += count + 1;
93+
length -= count + octet;
94+
} else {
95+
text += count;
96+
wire += count;
97+
length -= count;
98+
}
99+
} while (length && wire < limit);
100+
101+
if (length || (wire > limit))
102+
return -1;
103+
assert(!length);
104+
return (int32_t)(wire - octets);
110105
}
111106

112107
#endif // TEXT_H

src/generic/types.h

+16
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,22 @@ static really_inline int32_t parse_name(
3232
rdata_t *rdata,
3333
const token_t *token);
3434

35+
nonnull_all
36+
static really_inline int32_t parse_string(
37+
parser_t *parser,
38+
const type_info_t *type,
39+
const rdata_info_t *field,
40+
rdata_t *rdata,
41+
const token_t *token);
42+
43+
nonnull_all
44+
static really_inline int32_t parse_text(
45+
parser_t *parser,
46+
const type_info_t *type,
47+
const rdata_info_t *field,
48+
rdata_t *rdata,
49+
const token_t *token);
50+
3551
#define FIELDS(fields) \
3652
{ (sizeof(fields)/sizeof(fields[0])), fields }
3753

0 commit comments

Comments
 (0)