chiark / gitweb /
shared/util: respect buffer boundary on incomplete escape sequences
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Sun, 11 Jan 2015 22:21:17 +0000 (17:21 -0500)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Mon, 12 Jan 2015 04:41:42 +0000 (23:41 -0500)
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.

src/shared/util.c
src/test/test-util.c

index 280e42b..1210900 100644 (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;
 }
index 1e50a29..4bb5154 100644 (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) {