mirror of
https://github.com/systemd/systemd.git
synced 2026-08-04 23:20:32 +00:00
shared/util: respect buffer boundary on incomplete escape sequences
cunescape_length_with_prefix() is called with the length as an argument, so it cannot rely on the buffer being NUL terminated. Move the length check before accessing the memory. When an incomplete escape sequence was given at the end of the buffer, c_l_w_p() would read past the end of the buffer. Fix this and add a test.
This commit is contained in:
@@ -1352,12 +1352,19 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
|
||||
memcpy(r, prefix, pl);
|
||||
|
||||
for (f = s, t = r + pl; f < s + length; f++) {
|
||||
size_t remaining = s + length - f;
|
||||
assert(remaining > 0);
|
||||
|
||||
if (*f != '\\') {
|
||||
if (*f != '\\') { /* a literal literal */
|
||||
*(t++) = *f;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (--remaining == 0) { /* copy trailing backslash verbatim */
|
||||
*(t++) = *f;
|
||||
break;
|
||||
}
|
||||
|
||||
f++;
|
||||
|
||||
switch (*f) {
|
||||
@@ -1400,10 +1407,12 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
|
||||
|
||||
case 'x': {
|
||||
/* hexadecimal encoding */
|
||||
int a, b;
|
||||
int a = -1, b = -1;
|
||||
|
||||
a = unhexchar(f[1]);
|
||||
b = unhexchar(f[2]);
|
||||
if (remaining >= 2) {
|
||||
a = unhexchar(f[1]);
|
||||
b = unhexchar(f[2]);
|
||||
}
|
||||
|
||||
if (a < 0 || b < 0 || (a == 0 && b == 0)) {
|
||||
/* Invalid escape code, let's take it literal then */
|
||||
@@ -1426,11 +1435,13 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
|
||||
case '6':
|
||||
case '7': {
|
||||
/* octal encoding */
|
||||
int a, b, c;
|
||||
int a = -1, b = -1, c = -1;
|
||||
|
||||
a = unoctchar(f[0]);
|
||||
b = unoctchar(f[1]);
|
||||
c = unoctchar(f[2]);
|
||||
if (remaining >= 3) {
|
||||
a = unoctchar(f[0]);
|
||||
b = unoctchar(f[1]);
|
||||
c = unoctchar(f[2]);
|
||||
}
|
||||
|
||||
if (a < 0 || b < 0 || c < 0 || (a == 0 && b == 0 && c == 0)) {
|
||||
/* Invalid escape code, let's take it literal then */
|
||||
@@ -1444,11 +1455,6 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
|
||||
break;
|
||||
}
|
||||
|
||||
case 0:
|
||||
/* premature end of string. */
|
||||
*(t++) = '\\';
|
||||
goto finish;
|
||||
|
||||
default:
|
||||
/* Invalid escape code, let's take it literal then */
|
||||
*(t++) = '\\';
|
||||
@@ -1457,7 +1463,6 @@ char *cunescape_length_with_prefix(const char *s, size_t length, const char *pre
|
||||
}
|
||||
}
|
||||
|
||||
finish:
|
||||
*t = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
@@ -416,8 +416,24 @@ static void test_cescape(void) {
|
||||
static void test_cunescape(void) {
|
||||
_cleanup_free_ char *unescaped;
|
||||
|
||||
assert_se(unescaped = cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00"));
|
||||
assert_se(streq(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00"));
|
||||
unescaped = cunescape("abc\\\\\\\"\\b\\f\\a\\n\\r\\t\\v\\003\\177\\234\\313\\000\\x00");
|
||||
assert_se(streq_ptr(unescaped, "abc\\\"\b\f\a\n\r\t\v\003\177\234\313\\000\\x00"));
|
||||
|
||||
/* incomplete sequences */
|
||||
unescaped = cunescape("\\x0");
|
||||
assert_se(streq_ptr(unescaped, "\\x0"));
|
||||
|
||||
unescaped = cunescape("\\x");
|
||||
assert_se(streq_ptr(unescaped, "\\x"));
|
||||
|
||||
unescaped = cunescape("\\");
|
||||
assert_se(streq_ptr(unescaped, "\\"));
|
||||
|
||||
unescaped = cunescape("\\11");
|
||||
assert_se(streq_ptr(unescaped, "\\11"));
|
||||
|
||||
unescaped = cunescape("\\1");
|
||||
assert_se(streq_ptr(unescaped, "\\1"));
|
||||
}
|
||||
|
||||
static void test_foreach_word(void) {
|
||||
|
||||
Reference in New Issue
Block a user