chiark / gitweb /
bus: send memfds as payload only on directed messages and for large parts
[elogind.git] / src / libsystemd-bus / bus-message.c
index 747b44ac942944711c28879670a8ac30baf5fc9e..ceac15601e1edf35e8c0e48a70256143529f72f4 100644 (file)
@@ -59,6 +59,8 @@ static void message_free_part(sd_bus_message *m, struct bus_body_part *part) {
         assert(part);
 
         if (part->memfd >= 0) {
+                /* If we can reuse the memfd, try that. For that it
+                 * can't be sealed yet. */
 
                 if (!part->sealed)
                         bus_kernel_push_memfd(m->bus, part->memfd, part->data, part->mapped);
@@ -2184,7 +2186,7 @@ int sd_bus_message_append_array_memfd(sd_bus_message *m, char type, sd_memfd *me
         return sd_bus_message_close_container(m);
 }
 
-static int body_part_map_for_read(struct bus_body_part *part) {
+int bus_body_part_map(struct bus_body_part *part) {
         void *p;
         size_t psz;
 
@@ -2196,6 +2198,13 @@ static int body_part_map_for_read(struct bus_body_part *part) {
         if (part->size <= 0)
                 return 0;
 
+        /* For smaller zero parts (as used for padding) we don't need to map anything... */
+        if (part->memfd < 0 && part->is_zero && part->size < 8) {
+                static const uint8_t zeroes[7] = { };
+                part->data = (void*) zeroes;
+                return 0;
+        }
+
         psz = PAGE_ALIGN(part->size);
 
         if (part->memfd >= 0)
@@ -2210,9 +2219,33 @@ static int body_part_map_for_read(struct bus_body_part *part) {
 
         part->mapped = psz;
         part->data = p;
+        part->munmap_this = true;
+
         return 0;
 }
 
+void bus_body_part_unmap(struct bus_body_part *part) {
+
+        assert_se(part);
+
+        if (part->memfd < 0)
+                return;
+
+        if (!part->data)
+                return;
+
+        if (!part->munmap_this)
+                return;
+
+        assert_se(munmap(part->data, part->mapped) == 0);
+
+        part->data = NULL;
+        part->mapped = 0;
+        part->munmap_this = false;
+
+        return;
+}
+
 static int buffer_peek(const void *p, uint32_t sz, size_t *rindex, size_t align, size_t nbytes, void **r) {
         size_t k, start, end;
 
@@ -2271,7 +2304,7 @@ static struct bus_body_part* find_part(sd_bus_message *m, size_t index, size_t s
 
                 if (index + sz <= begin + part->size) {
 
-                        r = body_part_map_for_read(part);
+                        r = bus_body_part_map(part);
                         if (r < 0)
                                 return NULL;
 
@@ -3208,7 +3241,7 @@ int sd_bus_message_read_array(sd_bus_message *m, char type, const void **ptr, si
                 return align;
 
         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, CHAR_TO_STR(type));
-        if (r < 0)
+        if (r <= 0)
                 return r;
 
         c = message_get_container(m);
@@ -3699,19 +3732,27 @@ int bus_message_seal(sd_bus_message *m, uint64_t serial) {
                         return r;
         }
 
-        /* Add padding at the end, since we know the body
-         * needs to start at an 8 byte alignment. */
-
+        /* Add padding at the end of the fields part, since we know
+         * the body needs to start at an 8 byte alignment. We made
+         * sure we allocated enough space for this, so all we need to
+         * do here is to zero it out. */
         l = BUS_MESSAGE_FIELDS_SIZE(m);
         a = ALIGN8(l) - l;
         if (a > 0)
                 memset((uint8_t*) BUS_MESSAGE_FIELDS(m) + l, 0, a);
 
-        MESSAGE_FOREACH_PART(part, i, m)
-                if (part->memfd >= 0 && !part->sealed) {
-                        ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1);
-                        part->sealed = true;
-                }
+        /* If this is something we can send as memfd, then let's seal
+        the memfd now. Note that we can send memfds as payload only
+        for directed messages, and not for broadcasts. */
+        if (m->destination) {
+                MESSAGE_FOREACH_PART(part, i, m)
+                        if (part->memfd >= 0 && !part->sealed && part->size > MEMFD_MIN_SIZE) {
+                                bus_body_part_unmap(part);
+
+                                if (ioctl(part->memfd, KDBUS_CMD_MEMFD_SEAL_SET, 1) >= 0)
+                                        part->sealed = true;
+                        }
+        }
 
         m->header->serial = serial;
         m->sealed = true;