36e8a3
From 26de3af817b0c5746cb61b798ae8e138e01ea17c Mon Sep 17 00:00:00 2001
36e8a3
From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= <zbyszek@in.waw.pl>
36e8a3
Date: Mon, 9 Jul 2018 07:03:01 +0200
36e8a3
Subject: [PATCH] Introduce free_and_strndup and use it in bus-message.c
36e8a3
36e8a3
v2: fix error in free_and_strndup()
36e8a3
36e8a3
When the orignal and copied message were the same, but shorter than specified
36e8a3
length l, memory read past the end of the buffer would be performed. A test
36e8a3
case is included: a string that had an embedded NUL ("q\0") is used to replace
36e8a3
"q".
36e8a3
36e8a3
v3: Fix one more bug in free_and_strndup and add tests.
36e8a3
36e8a3
v4: Some style fixed based on review, one more use of free_and_replace, and
36e8a3
make the tests more comprehensive.
36e8a3
36e8a3
(cherry picked from commit 7f546026abbdc56c453a577e52d57159458c3e9c)
36e8a3
36e8a3
Resolves: #1635428
36e8a3
---
36e8a3
 src/basic/string-util.c                       |  28 +++++++-
36e8a3
 src/basic/string-util.h                       |   1 +
36e8a3
 src/libsystemd/sd-bus/bus-message.c           |  34 ++++------
36e8a3
 src/test/test-string-util.c                   |  62 ++++++++++++++++++
36e8a3
 ...h-b88ad9ecf4aacf4a0caca5b5543953265367f084 | Bin 0 -> 32 bytes
36e8a3
 5 files changed, 103 insertions(+), 22 deletions(-)
36e8a3
 create mode 100644 test/fuzz/fuzz-bus-message/crash-b88ad9ecf4aacf4a0caca5b5543953265367f084
36e8a3
36e8a3
diff --git a/src/basic/string-util.c b/src/basic/string-util.c
36e8a3
index 0a4068349..dfa739996 100644
36e8a3
--- a/src/basic/string-util.c
36e8a3
+++ b/src/basic/string-util.c
36e8a3
@@ -1004,7 +1004,7 @@ int free_and_strdup(char **p, const char *s) {
36e8a3
 
36e8a3
         assert(p);
36e8a3
 
36e8a3
-        /* Replaces a string pointer with an strdup()ed new string,
36e8a3
+        /* Replaces a string pointer with a strdup()ed new string,
36e8a3
          * possibly freeing the old one. */
36e8a3
 
36e8a3
         if (streq_ptr(*p, s))
36e8a3
@@ -1023,6 +1023,32 @@ int free_and_strdup(char **p, const char *s) {
36e8a3
         return 1;
36e8a3
 }
36e8a3
 
36e8a3
+int free_and_strndup(char **p, const char *s, size_t l) {
36e8a3
+        char *t;
36e8a3
+
36e8a3
+        assert(p);
36e8a3
+        assert(s || l == 0);
36e8a3
+
36e8a3
+        /* Replaces a string pointer with a strndup()ed new string,
36e8a3
+         * freeing the old one. */
36e8a3
+
36e8a3
+        if (!*p && !s)
36e8a3
+                return 0;
36e8a3
+
36e8a3
+        if (*p && s && strneq(*p, s, l) && (l > strlen(*p) || (*p)[l] == '\0'))
36e8a3
+                return 0;
36e8a3
+
36e8a3
+        if (s) {
36e8a3
+                t = strndup(s, l);
36e8a3
+                if (!t)
36e8a3
+                        return -ENOMEM;
36e8a3
+        } else
36e8a3
+                t = NULL;
36e8a3
+
36e8a3
+        free_and_replace(*p, t);
36e8a3
+        return 1;
36e8a3
+}
36e8a3
+
36e8a3
 #if !HAVE_EXPLICIT_BZERO
36e8a3
 /*
36e8a3
  * Pointer to memset is volatile so that compiler must de-reference
36e8a3
diff --git a/src/basic/string-util.h b/src/basic/string-util.h
36e8a3
index c0cc4e78d..96a9260f9 100644
36e8a3
--- a/src/basic/string-util.h
36e8a3
+++ b/src/basic/string-util.h
36e8a3
@@ -176,6 +176,7 @@ char *strrep(const char *s, unsigned n);
36e8a3
 int split_pair(const char *s, const char *sep, char **l, char **r);
36e8a3
 
36e8a3
 int free_and_strdup(char **p, const char *s);
36e8a3
+int free_and_strndup(char **p, const char *s, size_t l);
36e8a3
 
36e8a3
 /* Normal memmem() requires haystack to be nonnull, which is annoying for zero-length buffers */
36e8a3
 static inline void *memmem_safe(const void *haystack, size_t haystacklen, const void *needle, size_t needlelen) {
36e8a3
diff --git a/src/libsystemd/sd-bus/bus-message.c b/src/libsystemd/sd-bus/bus-message.c
36e8a3
index 381034f5f..7c8bad2bd 100644
36e8a3
--- a/src/libsystemd/sd-bus/bus-message.c
36e8a3
+++ b/src/libsystemd/sd-bus/bus-message.c
36e8a3
@@ -4175,20 +4175,19 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
36e8a3
 
36e8a3
                 if (contents) {
36e8a3
                         size_t l;
36e8a3
-                        char *sig;
36e8a3
 
36e8a3
                         r = signature_element_length(c->signature+c->index+1, &l);
36e8a3
                         if (r < 0)
36e8a3
                                 return r;
36e8a3
 
36e8a3
-                        assert(l >= 1);
36e8a3
+                        /* signature_element_length does verification internally */
36e8a3
 
36e8a3
-                        sig = strndup(c->signature + c->index + 1, l);
36e8a3
-                        if (!sig)
36e8a3
+                        assert(l >= 1);
36e8a3
+                        if (free_and_strndup(&c->peeked_signature,
36e8a3
+                                             c->signature + c->index + 1, l) < 0)
36e8a3
                                 return -ENOMEM;
36e8a3
 
36e8a3
-                        free(c->peeked_signature);
36e8a3
-                        *contents = c->peeked_signature = sig;
36e8a3
+                        *contents = c->peeked_signature;
36e8a3
                 }
36e8a3
 
36e8a3
                 if (type)
36e8a3
@@ -4201,19 +4200,17 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
36e8a3
 
36e8a3
                 if (contents) {
36e8a3
                         size_t l;
36e8a3
-                        char *sig;
36e8a3
 
36e8a3
                         r = signature_element_length(c->signature+c->index, &l);
36e8a3
                         if (r < 0)
36e8a3
                                 return r;
36e8a3
 
36e8a3
                         assert(l >= 2);
36e8a3
-                        sig = strndup(c->signature + c->index + 1, l - 2);
36e8a3
-                        if (!sig)
36e8a3
+                        if (free_and_strndup(&c->peeked_signature,
36e8a3
+                                             c->signature + c->index + 1, l - 2) < 0)
36e8a3
                                 return -ENOMEM;
36e8a3
 
36e8a3
-                        free(c->peeked_signature);
36e8a3
-                        *contents = c->peeked_signature = sig;
36e8a3
+                        *contents = c->peeked_signature;
36e8a3
                 }
36e8a3
 
36e8a3
                 if (type)
36e8a3
@@ -4253,9 +4250,8 @@ _public_ int sd_bus_message_peek_type(sd_bus_message *m, char *type, const char
36e8a3
                                 if (k > c->item_size)
36e8a3
                                         return -EBADMSG;
36e8a3
 
36e8a3
-                                free(c->peeked_signature);
36e8a3
-                                c->peeked_signature = strndup((char*) q + 1, k - 1);
36e8a3
-                                if (!c->peeked_signature)
36e8a3
+                                if (free_and_strndup(&c->peeked_signature,
36e8a3
+                                                     (char*) q + 1, k - 1) < 0)
36e8a3
                                         return -ENOMEM;
36e8a3
 
36e8a3
                                 if (!signature_is_valid(c->peeked_signature, true))
36e8a3
@@ -5085,25 +5081,21 @@ int bus_message_parse_fields(sd_bus_message *m) {
36e8a3
 
36e8a3
                         if (*p == 0) {
36e8a3
                                 size_t l;
36e8a3
-                                char *c;
36e8a3
 
36e8a3
                                 /* We found the beginning of the signature
36e8a3
                                  * string, yay! We require the body to be a
36e8a3
                                  * structure, so verify it and then strip the
36e8a3
                                  * opening/closing brackets. */
36e8a3
 
36e8a3
-                                l = ((char*) m->footer + m->footer_accessible) - p - (1 + sz);
36e8a3
+                                l = (char*) m->footer + m->footer_accessible - p - (1 + sz);
36e8a3
                                 if (l < 2 ||
36e8a3
                                     p[1] != SD_BUS_TYPE_STRUCT_BEGIN ||
36e8a3
                                     p[1 + l - 1] != SD_BUS_TYPE_STRUCT_END)
36e8a3
                                         return -EBADMSG;
36e8a3
 
36e8a3
-                                c = strndup(p + 1 + 1, l - 2);
36e8a3
-                                if (!c)
36e8a3
+                                if (free_and_strndup(&m->root_container.signature,
36e8a3
+                                                     p + 1 + 1, l - 2) < 0)
36e8a3
                                         return -ENOMEM;
36e8a3
-
36e8a3
-                                free(m->root_container.signature);
36e8a3
-                                m->root_container.signature = c;
36e8a3
                                 break;
36e8a3
                         }
36e8a3
 
36e8a3
diff --git a/src/test/test-string-util.c b/src/test/test-string-util.c
36e8a3
index 3e72ce2c0..43a6b14c3 100644
36e8a3
--- a/src/test/test-string-util.c
36e8a3
+++ b/src/test/test-string-util.c
36e8a3
@@ -5,6 +5,7 @@
36e8a3
 #include "macro.h"
36e8a3
 #include "string-util.h"
36e8a3
 #include "strv.h"
36e8a3
+#include "tests.h"
36e8a3
 #include "utf8.h"
36e8a3
 
36e8a3
 static void test_string_erase(void) {
36e8a3
@@ -30,6 +31,64 @@ static void test_string_erase(void) {
36e8a3
         assert_se(x[9] == '\0');
36e8a3
 }
36e8a3
 
36e8a3
+static void test_free_and_strndup_one(char **t, const char *src, size_t l, const char *expected, bool change) {
36e8a3
+        int r;
36e8a3
+
36e8a3
+        log_debug("%s: \"%s\", \"%s\", %zd (expect \"%s\", %s)",
36e8a3
+                  __func__, strnull(*t), strnull(src), l, strnull(expected), yes_no(change));
36e8a3
+
36e8a3
+        r = free_and_strndup(t, src, l);
36e8a3
+        assert_se(streq_ptr(*t, expected));
36e8a3
+        assert_se(r == change); /* check that change occurs only when necessary */
36e8a3
+}
36e8a3
+
36e8a3
+static void test_free_and_strndup(void) {
36e8a3
+        static const struct test_case {
36e8a3
+                const char *src;
36e8a3
+                size_t len;
36e8a3
+                const char *expected;
36e8a3
+        } cases[] = {
36e8a3
+                     {"abc", 0, ""},
36e8a3
+                     {"abc", 0, ""},
36e8a3
+                     {"abc", 1, "a"},
36e8a3
+                     {"abc", 2, "ab"},
36e8a3
+                     {"abc", 3, "abc"},
36e8a3
+                     {"abc", 4, "abc"},
36e8a3
+                     {"abc", 5, "abc"},
36e8a3
+                     {"abc", 5, "abc"},
36e8a3
+                     {"abc", 4, "abc"},
36e8a3
+                     {"abc", 3, "abc"},
36e8a3
+                     {"abc", 2, "ab"},
36e8a3
+                     {"abc", 1, "a"},
36e8a3
+                     {"abc", 0, ""},
36e8a3
+
36e8a3
+                     {"", 0, ""},
36e8a3
+                     {"", 1, ""},
36e8a3
+                     {"", 2, ""},
36e8a3
+                     {"", 0, ""},
36e8a3
+                     {"", 1, ""},
36e8a3
+                     {"", 2, ""},
36e8a3
+                     {"", 2, ""},
36e8a3
+                     {"", 1, ""},
36e8a3
+                     {"", 0, ""},
36e8a3
+
36e8a3
+                     {NULL, 0, NULL},
36e8a3
+
36e8a3
+                     {"foo", 3, "foo"},
36e8a3
+                     {"foobar", 6, "foobar"},
36e8a3
+        };
36e8a3
+
36e8a3
+        _cleanup_free_ char *t = NULL;
36e8a3
+        const char *prev_expected = t;
36e8a3
+
36e8a3
+        for (unsigned i = 0; i < ELEMENTSOF(cases); i++) {
36e8a3
+                test_free_and_strndup_one(&t,
36e8a3
+                                          cases[i].src, cases[i].len, cases[i].expected,
36e8a3
+                                          !streq_ptr(cases[i].expected, prev_expected));
36e8a3
+                prev_expected = t;
36e8a3
+        }
36e8a3
+}
36e8a3
+
36e8a3
 static void test_ascii_strcasecmp_n(void) {
36e8a3
 
36e8a3
         assert_se(ascii_strcasecmp_n("", "", 0) == 0);
36e8a3
@@ -497,7 +556,10 @@ static void test_memory_startswith(void) {
36e8a3
 }
36e8a3
 
36e8a3
 int main(int argc, char *argv[]) {
36e8a3
+        test_setup_logging(LOG_DEBUG);
36e8a3
+
36e8a3
         test_string_erase();
36e8a3
+        test_free_and_strndup();
36e8a3
         test_ascii_strcasecmp_n();
36e8a3
         test_ascii_strcasecmp_nn();
36e8a3
         test_cellescape();
36e8a3
diff --git a/test/fuzz/fuzz-bus-message/crash-b88ad9ecf4aacf4a0caca5b5543953265367f084 b/test/fuzz/fuzz-bus-message/crash-b88ad9ecf4aacf4a0caca5b5543953265367f084
36e8a3
new file mode 100644
36e8a3
index 0000000000000000000000000000000000000000..52469650b5498a45d5d95bd9d933c989cfb47ca7
36e8a3
GIT binary patch
36e8a3
literal 32
36e8a3
ccmd1#|DTBg0(2Mzp)7_%AVVXuuuM|`09r!?!~g&Q
36e8a3
36e8a3
literal 0
36e8a3
HcmV?d00001
36e8a3