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:
Zbigniew Jędrzejewski-Szmek
2015-01-11 17:21:17 -05:00
parent e01ff42899
commit 7f76961982
2 changed files with 37 additions and 16 deletions

View File

@@ -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;
}

View File

@@ -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) {