mirror of
https://github.com/systemd/systemd.git
synced 2026-08-09 17:38:42 +00:00
escape: reject UTF-16 surrogates in \u escapes in cunescape_one() (#43079)
A `\u` escape in `cunescape_one()` was accepted for any 16-bit value except NUL. That range `0x0000`-`0xFFFF` includes the UTF-16 surrogates `0xD800`-`0xDFFF`, which are not valid Unicode scalar values and cannot be encoded as valid UTF-8.
This commit is contained in:
@@ -202,6 +202,13 @@ int cunescape_one(const char *p, size_t length, char32_t *ret, bool *eight_bit,
|
||||
if (c == 0 && !accept_nul)
|
||||
return -EINVAL;
|
||||
|
||||
/* Don't allow UTF-16 surrogates, they cannot be encoded as valid UTF-8. Note we
|
||||
* deliberately do *not* use unichar_is_valid() here (unlike the \U case below):
|
||||
* it also rejects noncharacters such as U+FFFE, which callers legitimately round-trip
|
||||
* through \u (e.g. systemd.mount-extra= parsing, see test-fstab-generator). */
|
||||
if (utf16_is_surrogate(c))
|
||||
return -EINVAL;
|
||||
|
||||
*ret = c;
|
||||
r = 5;
|
||||
break;
|
||||
|
||||
@@ -111,6 +111,21 @@ TEST(cunescape) {
|
||||
ASSERT_STREQ(unescaped, "ßßΠA");
|
||||
unescaped = mfree(unescaped);
|
||||
|
||||
/* UTF-16 surrogates cannot be encoded as valid UTF-8 and must be rejected */
|
||||
ASSERT_ERROR(cunescape("\\ud800", 0, &unescaped), EINVAL);
|
||||
ASSERT_ERROR(cunescape("\\udfff", 0, &unescaped), EINVAL);
|
||||
|
||||
/* The code points immediately outside the surrogate range must still decode */
|
||||
ASSERT_OK(cunescape("\\ud7ff", 0, &unescaped));
|
||||
unescaped = mfree(unescaped);
|
||||
ASSERT_OK(cunescape("\\ue000", 0, &unescaped));
|
||||
unescaped = mfree(unescaped);
|
||||
|
||||
/* Noncharacters (e.g. U+FFFE) are valid scalar values, encode fine as UTF-8, and are
|
||||
* relied upon by callers (systemd.mount-extra=), so unlike \U they stay accepted here */
|
||||
ASSERT_OK(cunescape("\\ufffe", 0, &unescaped));
|
||||
unescaped = mfree(unescaped);
|
||||
|
||||
assert_se(cunescape("\\073", 0, &unescaped) >= 0);
|
||||
ASSERT_STREQ(unescaped, ";");
|
||||
unescaped = mfree(unescaped);
|
||||
|
||||
Reference in New Issue
Block a user