summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2015-01-11 17:21:17 -0500
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2015-01-11 23:41:42 -0500
commit7f76961982e03d4d5f781e7e7113fc7eff970f82 (patch)
treeb53ec520a161642ffcba434526eec371972b961d
parente01ff428993f0c126f010b5625002e6a0a8aff4a (diff)
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.
-rw-r--r--src/shared/util.c33
-rw-r--r--src/test/test-util.c20
2 files changed, 37 insertions, 16 deletions
diff --git a/src/shared/util.c b/src/shared/util.c
index 280e42b30..1210900bc 100644
--- a/src/shared/util.c
+++ b/src/shared/util.c
@@ -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;
}
diff --git a/src/test/test-util.c b/src/test/test-util.c
index 1e50a29f7..4bb51545b 100644
--- a/src/test/test-util.c
+++ b/src/test/test-util.c
@@ -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) {