Skip to contentSkip to conversation

Add kdbus implementation

PATCH NAK Greg Kroah-Hartman 11 participants 52 messages Mar 9, 2015 – Apr 8, 2015

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

v4 cover Add kdbus implementation

kdbus is a kernel-level IPC implementation that aims for resemblance to the the protocol layer with the existing userspace D-Bus daemon while enabling some features that couldn't be implemented before in userspace.

The documentation in the first patch in this series explains the protocol and the API details.

This is v4 of the kdbus series for inclusion into the mainline kernel. Changes since v3 are:

* Drop KDBUS_FLAG_KERNEL and the 'kernel_flags' member from all struct kdbus_cmd_*, and introduce a new KDBUS_FLAGS_NEGOTIATE instead. Requested by Michael Kerrisk.

* Transform kdbus.txt into DocBook man-pages for better readablity, and extend the documentation significantly. Requested by Michael Kerrisk and Christoph Hellwig.

* Add a walk-through example for using the low-level ioctl API from userspace.

* Consolidate some 'struct kdbus_cmd_*' types to make the API interface easier to grasp.

* Drop 'struct kdbus_item_list'. The information stored in this struct was redundant as all ioctls report the returned size in the command struct already.

* KDBUS_CMD_NAME_ACQUIRE now returns the KDBUS_NAME_IN_QUEUE flag in cmd->return_flags rather than modifying cmd->flags.

* Get rid of the need for a 2nd pool slice at install time. This avoids pool fragmentation, message memory footprint and complexity.

* Separate flags from attach_flags in struct kdbus_cmd_info.

* Fix handling of messages with file descriptors with regard to monitor connections that don't accept file descriptors.

* Revisited and reimplemented the quota logic. 50% are now always kept reserved for the connection to receive notification etc, and the rest is accounted per remote peer to avoid denial of service attacks.

* Make use of new functions introduced with 4.0-rc1 (vfs_iter_write(), {kstrdup,kfree}_const())

* Some internal restructuring and cleanups.

Reasons why this should be done in the kernel, instead of userspace as it is currently done today include the following:

* Performance: Fewer process context switches, fewer copies, fewer syscalls, larger memory chunks via memfd. This is really important for a whole class of userspace programs that are ported from other operating systems that are run on tiny ARM systems that rely on hundreds of thousands of messages passed at boot time, and at "critical" times in their user interaction loops. DBus is not used for performance sensitive applications because DBus is slow. We want to make it fast so we can finally use it for low-latency, high-throughput applications. A simple DBus method-call+reply takes 200us on an up-to-date test machine, with kdbus it takes 8us (with UDS about 2us). If the packet size is increased from 8k to 128k, kdbus even beats UDS due to single-copy transfers.

* Security: The peers which communicate do not have to trust each other, as the only trustworthy component in the game is the kernel which adds metadata and ensures that all data passed as payload is either copied or sealed, so that the receiver can parse the data without having to protect against changing memory while parsing buffers. Also, all the data transfer is controlled by the kernel, so that LSMs can track and control what is going on, without involving userspace. Because of the LSM issue, security people are much happier with this model than the current scheme of having to hook into dbus to mediate things.

* More types of metadata can be attached to messages than in userspace

* Semantics for apps with heavy data payloads (media apps, for instance) with optinal priority message dequeuing, and global message ordering. Some "crazy" people are playing with using kdbus for audio data in the system. I'm not saying that this is the best model for this, but until now, there wasn't any other way to do this without having to create custom "buses", one for each application library.

* Being in the kernel closes a lot of races which can't be fixed with the current userspace solutions. For example, with kdbus, there is a way a client can disconnect from a bus, but do so only if no further messages present in its queue, which is crucial for implementing race-free "exit-on-idle" services

* Eavesdropping on the kernel level, so privileged users can hook into the message stream without hacking support for that into their userspace processes

* A number of smaller benefits: for example kdbus learned a way to peek full messages without dequeing them, which is really useful for logging metadata when handling bus-activation requests.

* dbus-daemon is not available during early-boot or shutdown.

DBus marshaling is the de-facto standard in all major(!) Linux desktop systems. It is well established and accepted by many DEs. It also solves many other problems, including: policy, authentication / authorization, well-known name registry, efficient broadcasts / multicasts, peer discovery, bus discovery, metadata transmission, and more.

It is a shame that we cannot use this well-established protocol for low-latency applications. We, effectively, have to duplicate all this code on custom UDS and other transports just because DBus is too slow. kdbus tries to unify those efforts, so that we don't need multiple policy implementations, name registries and peer discovery mechanisms. Furthermore, kdbus implements comprehensive, yet optional, metadata transmission that allows to identify and authenticate peers in a race-free manner (which is *not* possible with UDS).

Also, kdbus provides a single transport bus with sequential message numbering. If you use multiple channels, you cannot give any ordering guarantees across peers (for instance, regarding parallel name-registry changes).

Of course, some of the bits above could be implemented in userspace alone, for example with more sophisticated memory management APIs, but this is usually done by losing out on the other details. For example, for many of the memory management APIs, it's hard to not require the communicating peers to fully trust each other. And we _really_ don't want peers to have to trust each other.

Another benefit of having this in the kernel, rather than as a userspace daemon, is that you can now easily use the bus from the initrd, or up to the very end when the system shuts down. On current userspace D-Bus, this is not really possible, as this requires passing the bus instance around between initrd and the "real" system. Such a transition of all fds also requires keeping full state of what has already been read from the connection fds. kdbus makes this much simpler, as we can change the ownership of the bus, just by passing one fd over from one part to the other.

Given the theoretical advantages above, here are some real-world examples:

* The Tizen developers have been complaining about the high latency of DBus for polkit'ish policy queries. That's why their authentication framework uses custom UDS sockets (called 'Cynara'). If a UI-interaction needs multiple authentication-queries, you don't want it to take multiple milliseconds, given that you usually want to render the result in the same frame.

* PulseAudio doesn't use DBus for data transmission. They had to implement their own marshaling code, transport layer and so on, just because DBus1-latency is horrible. With kdbus, we can basically drop this code-duplication and unify the IPC layer. Same is true for Wayland, btw.

* By moving broadcast-transmission into the kernel, we can use the time-slices of the sender to perform heavy operations. This is also true for policy decisions, etc. With a userspace daemon, we cannot perform operations in a time-slice of the caller. This makes DoS attacks much harder.

* With priority-inheritance, we can do synchronous calls into trusted peers and let them optionally use our time-slice to perform the action. This allows syscall-like/binder-like method-calls into other processes. Without priority-inheritance, this is not possible in a secure manner (see 'priority-inheritance').

* Logging-daemons often want to attach metadata to log-messages so debugging/filtering gets easier. If short-lived programs send log-messages, the destination peer might not be able to read such metadata from /proc, as the process might no longer be available at that time. Same is true for policy-decisions like polkit does. You cannot send off method-calls and exit. You have to wait for a reply, even though you might not even care for it. If you don't wait, the other side might not be able to verify your identity and as such reject the request.

* Even though the dbus traffic on idle-systems might be low, this doesn't mean it's not significant at boot-times or under high-load. If you run a dbus-monitor of your choice, you will see there is an significant number of messages exchanged during VT-switches, startup, shutdown, suspend, wakeup, hotplugging and similar situations where lots of control-messages are exchanged. We don't want to spend hundreds of ms just to transmit those messages.

These patches can also be found in a git tree, the kdbus branch of char-misc.git at:
https://git.kernel.org/cgit/linux/kernel/git/gregkh/char-misc.git/

Daniel Mack (14):
kdbus: add documentation
kdbus: add uapi header file
kdbus: add driver skeleton, ioctl entry points and utility functions
kdbus: add connection pool implementation
kdbus: add connection, queue handling and message validation code
kdbus: add node and filesystem implementation
kdbus: add code to gather metadata
kdbus: add code for notifications and matches
kdbus: add code for buses, domains and endpoints
kdbus: add name registry implementation
kdbus: add policy database implementation
kdbus: add Makefile, Kconfig and MAINTAINERS entry
kdbus: add walk-through user space example
kdbus: add selftests

 Documentation/Makefile                            |    2 +-
 Documentation/ioctl/ioctl-number.txt              |    1 +
 Documentation/kdbus/Makefile                      |   30 +
 Documentation/kdbus/kdbus.bus.xml                 |  360 ++++
 Documentation/kdbus/kdbus.connection.xml          | 1252 ++++++++++++
 Documentation/kdbus/kdbus.endpoint.xml            |  436 ++++
 Documentation/kdbus/kdbus.fs.xml                  |  124 ++
 Documentation/kdbus/kdbus.item.xml                |  840 ++++++++
 Documentation/kdbus/kdbus.match.xml               |  553 +++++
 Documentation/kdbus/kdbus.message.xml             | 1277 ++++++++++++
 Documentation/kdbus/kdbus.name.xml                |  711 +++++++
 Documentation/kdbus/kdbus.policy.xml              |  406 ++++
 Documentation/kdbus/kdbus.pool.xml                |  320 +++
 Documentation/kdbus/kdbus.xml                     | 1012 ++++++++++
 Documentation/kdbus/stylesheet.xsl                |   16 +
 MAINTAINERS                                       |   13 +
 Makefile                                          |    1 +
 include/uapi/linux/Kbuild                         |    1 +
 include/uapi/linux/kdbus.h                        |  979 +++++++++
 include/uapi/linux/magic.h                        |    2 +
 init/Kconfig                                      |   12 +
 ipc/Makefile                                      |    2 +-
 ipc/kdbus/Makefile                                |   22 +
 ipc/kdbus/bus.c                                   |  560 ++++++
 ipc/kdbus/bus.h                                   |  101 +
 ipc/kdbus/connection.c                            | 2215 +++++++++++++++++++++
 ipc/kdbus/connection.h                            |  257 +++
 ipc/kdbus/domain.c                                |  296 +++
 ipc/kdbus/domain.h                                |   77 +
 ipc/kdbus/endpoint.c                              |  275 +++
 ipc/kdbus/endpoint.h                              |   67 +
 ipc/kdbus/fs.c                                    |  510 +++++
 ipc/kdbus/fs.h                                    |   28 +
 ipc/kdbus/handle.c                                |  617 ++++++
 ipc/kdbus/handle.h                                |   85 +
 ipc/kdbus/item.c                                  |  339 ++++
 ipc/kdbus/item.h                                  |   64 +
 ipc/kdbus/limits.h                                |   64 +
 ipc/kdbus/main.c                                  |  125 ++
 ipc/kdbus/match.c                                 |  559 ++++++
 ipc/kdbus/match.h                                 |   35 +
 ipc/kdbus/message.c                               |  616 ++++++
 ipc/kdbus/message.h                               |  133 ++
 ipc/kdbus/metadata.c                              | 1164 +++++++++++
 ipc/kdbus/metadata.h                              |   57 +
 ipc/kdbus/names.c                                 |  772 +++++++
 ipc/kdbus/names.h                                 |   74 +
 ipc/kdbus/node.c                                  |  910 +++++++++
 ipc/kdbus/node.h                                  |   84 +
 ipc/kdbus/notify.c                                |  248 +++
 ipc/kdbus/notify.h                                |   30 +
 ipc/kdbus/policy.c                                |  489 +++++
 ipc/kdbus/policy.h                                |   51 +
 ipc/kdbus/pool.c                                  |  728 +++++++
 ipc/kdbus/pool.h                                  |   46 +
 ipc/kdbus/queue.c                                 |  678 +++++++
 ipc/kdbus/queue.h                                 |   92 +
 ipc/kdbus/reply.c                                 |  259 +++
 ipc/kdbus/reply.h                                 |   68 +
 ipc/kdbus/util.c                                  |  201 ++
 ipc/kdbus/util.h                                  |   74 +
 samples/Makefile                                  |    3 +-
 samples/kdbus/.gitignore                          |    1 +
 samples/kdbus/Makefile                            |   10 +
 samples/kdbus/kdbus-api.h                         |  114 ++
 samples/kdbus/kdbus-workers.c                     | 1327 ++++++++++++
 tools/testing/selftests/Makefile                  |    1 +
 tools/testing/selftests/kdbus/.gitignore          |    3 +
 tools/testing/selftests/kdbus/Makefile            |   46 +
 tools/testing/selftests/kdbus/kdbus-enum.c        |   94 +
 tools/testing/selftests/kdbus/kdbus-enum.h        |   14 +
 tools/testing/selftests/kdbus/kdbus-test.c        |  923 +++++++++
 tools/testing/selftests/kdbus/kdbus-test.h        |   85 +
 tools/testing/selftests/kdbus/kdbus-util.c        | 1615 +++++++++++++++
 tools/testing/selftests/kdbus/kdbus-util.h        |  222 +++
 tools/testing/selftests/kdbus/test-activator.c    |  318 +++
 tools/testing/selftests/kdbus/test-attach-flags.c |  750 +++++++
 tools/testing/selftests/kdbus/test-benchmark.c    |  451 +++++
 tools/testing/selftests/kdbus/test-bus.c          |  175 ++
 tools/testing/selftests/kdbus/test-chat.c         |  122 ++
 tools/testing/selftests/kdbus/test-connection.c   |  616 ++++++
 tools/testing/selftests/kdbus/test-daemon.c       |   65 +
 tools/testing/selftests/kdbus/test-endpoint.c     |  341 ++++
 tools/testing/selftests/kdbus/test-fd.c           |  789 ++++++++
 tools/testing/selftests/kdbus/test-free.c         |   64 +
 tools/testing/selftests/kdbus/test-match.c        |  441 ++++
 tools/testing/selftests/kdbus/test-message.c      |  731 +++++++
 tools/testing/selftests/kdbus/test-metadata-ns.c  |  506 +++++
 tools/testing/selftests/kdbus/test-monitor.c      |  176 ++
 tools/testing/selftests/kdbus/test-names.c        |  194 ++
 tools/testing/selftests/kdbus/test-policy-ns.c    |  632 ++++++
 tools/testing/selftests/kdbus/test-policy-priv.c  | 1269 ++++++++++++
 tools/testing/selftests/kdbus/test-policy.c       |   80 +
 tools/testing/selftests/kdbus/test-sync.c         |  369 ++++
 tools/testing/selftests/kdbus/test-timeout.c      |   99 +
 95 files changed, 34063 insertions(+), 3 deletions(-)
 create mode 100644 Documentation/kdbus/Makefile
 create mode 100644 Documentation/kdbus/kdbus.bus.xml
 create mode 100644 Documentation/kdbus/kdbus.connection.xml
 create mode 100644 Documentation/kdbus/kdbus.endpoint.xml
 create mode 100644 Documentation/kdbus/kdbus.fs.xml
 create mode 100644 Documentation/kdbus/kdbus.item.xml
 create mode 100644 Documentation/kdbus/kdbus.match.xml
 create mode 100644 Documentation/kdbus/kdbus.message.xml
 create mode 100644 Documentation/kdbus/kdbus.name.xml
 create mode 100644 Documentation/kdbus/kdbus.policy.xml
 create mode 100644 Documentation/kdbus/kdbus.pool.xml
 create mode 100644 Documentation/kdbus/kdbus.xml
 create mode 100644 Documentation/kdbus/stylesheet.xsl
 create mode 100644 include/uapi/linux/kdbus.h
 create mode 100644 ipc/kdbus/Makefile
 create mode 100644 ipc/kdbus/bus.c
 create mode 100644 ipc/kdbus/bus.h
 create mode 100644 ipc/kdbus/connection.c
 create mode 100644 ipc/kdbus/connection.h
 create mode 100644 ipc/kdbus/domain.c
 create mode 100644 ipc/kdbus/domain.h
 create mode 100644 ipc/kdbus/endpoint.c
 create mode 100644 ipc/kdbus/endpoint.h
 create mode 100644 ipc/kdbus/fs.c
 create mode 100644 ipc/kdbus/fs.h
 create mode 100644 ipc/kdbus/handle.c
 create mode 100644 ipc/kdbus/handle.h
 create mode 100644 ipc/kdbus/item.c
 create mode 100644 ipc/kdbus/item.h
 create mode 100644 ipc/kdbus/limits.h
 create mode 100644 ipc/kdbus/main.c
 create mode 100644 ipc/kdbus/match.c
 create mode 100644 ipc/kdbus/match.h
 create mode 100644 ipc/kdbus/message.c
 create mode 100644 ipc/kdbus/message.h
 create mode 100644 ipc/kdbus/metadata.c
 create mode 100644 ipc/kdbus/metadata.h
 create mode 100644 ipc/kdbus/names.c
 create mode 100644 ipc/kdbus/names.h
 create mode 100644 ipc/kdbus/node.c
 create mode 100644 ipc/kdbus/node.h
 create mode 100644 ipc/kdbus/notify.c
 create mode 100644 ipc/kdbus/notify.h
 create mode 100644 ipc/kdbus/policy.c
 create mode 100644 ipc/kdbus/policy.h
 create mode 100644 ipc/kdbus/pool.c
 create mode 100644 ipc/kdbus/pool.h
 create mode 100644 ipc/kdbus/queue.c
 create mode 100644 ipc/kdbus/queue.h
 create mode 100644 ipc/kdbus/reply.c
 create mode 100644 ipc/kdbus/reply.h
 create mode 100644 ipc/kdbus/util.c
 create mode 100644 ipc/kdbus/util.h
 create mode 100644 samples/kdbus/.gitignore
 create mode 100644 samples/kdbus/Makefile
 create mode 100644 samples/kdbus/kdbus-api.h
 create mode 100644 samples/kdbus/kdbus-workers.c
 create mode 100644 tools/testing/selftests/kdbus/.gitignore
 create mode 100644 tools/testing/selftests/kdbus/Makefile
 create mode 100644 tools/testing/selftests/kdbus/kdbus-enum.c
 create mode 100644 tools/testing/selftests/kdbus/kdbus-enum.h
 create mode 100644 tools/testing/selftests/kdbus/kdbus-test.c
 create mode 100644 tools/testing/selftests/kdbus/kdbus-test.h
 create mode 100644 tools/testing/selftests/kdbus/kdbus-util.c
 create mode 100644 tools/testing/selftests/kdbus/kdbus-util.h
 create mode 100644 tools/testing/selftests/kdbus/test-activator.c
 create mode 100644 tools/testing/selftests/kdbus/test-attach-flags.c
 create mode 100644 tools/testing/selftests/kdbus/test-benchmark.c
 create mode 100644 tools/testing/selftests/kdbus/test-bus.c
 create mode 100644 tools/testing/selftests/kdbus/test-chat.c
 create mode 100644 tools/testing/selftests/kdbus/test-connection.c
 create mode 100644 tools/testing/selftests/kdbus/test-daemon.c
 create mode 100644 tools/testing/selftests/kdbus/test-endpoint.c
 create mode 100644 tools/testing/selftests/kdbus/test-fd.c
 create mode 100644 tools/testing/selftests/kdbus/test-free.c
 create mode 100644 tools/testing/selftests/kdbus/test-match.c
 create mode 100644 tools/testing/selftests/kdbus/test-message.c
 create mode 100644 tools/testing/selftests/kdbus/test-metadata-ns.c
 create mode 100644 tools/testing/selftests/kdbus/test-monitor.c
 create mode 100644 tools/testing/selftests/kdbus/test-names.c
 create mode 100644 tools/testing/selftests/kdbus/test-policy-ns.c
 create mode 100644 tools/testing/selftests/kdbus/test-policy-priv.c
 create mode 100644 tools/testing/selftests/kdbus/test-policy.c
 create mode 100644 tools/testing/selftests/kdbus/test-sync.c
 create mode 100644 tools/testing/selftests/kdbus/test-timeout.c
#1 lore raw

PATCH v4 1/14 kdbus: add documentation

From: Daniel Mack <[email protected]>

kdbus is a system for low-latency, low-overhead, easy to use interprocess communication (IPC).

The interface to all functions in this driver is implemented via ioctls on files exposed through a filesystem called 'kdbusfs'. The default mount point of kdbusfs is /sys/fs/kdbus. This patch adds detailed documentation about the kernel level API design.

This patch adds a set of comprehensive set of DocBook files which can be turned into man-pages using 'make mandocs', or into HTML files with 'make htmldocs'.

---
 Documentation/Makefile                   |    2 +-
 Documentation/kdbus/Makefile             |   30 +
 Documentation/kdbus/kdbus.bus.xml        |  360 +++++++++
 Documentation/kdbus/kdbus.connection.xml | 1252 +++++++++++++++++++++++++++++
 Documentation/kdbus/kdbus.endpoint.xml   |  436 ++++++++++
 Documentation/kdbus/kdbus.fs.xml         |  124 +++
 Documentation/kdbus/kdbus.item.xml       |  840 ++++++++++++++++++++
 Documentation/kdbus/kdbus.match.xml      |  553 +++++++++++++
 Documentation/kdbus/kdbus.message.xml    | 1277 ++++++++++++++++++++++++++++++
 Documentation/kdbus/kdbus.name.xml       |  711 +++++++++++++++++
 Documentation/kdbus/kdbus.policy.xml     |  406 ++++++++++
 Documentation/kdbus/kdbus.pool.xml       |  320 ++++++++
 Documentation/kdbus/kdbus.xml            | 1012 +++++++++++++++++++++++
 Documentation/kdbus/stylesheet.xsl       |   16 +
 Makefile                                 |    1 +
 15 files changed, 7339 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/kdbus/Makefile
 create mode 100644 Documentation/kdbus/kdbus.bus.xml
 create mode 100644 Documentation/kdbus/kdbus.connection.xml
 create mode 100644 Documentation/kdbus/kdbus.endpoint.xml
 create mode 100644 Documentation/kdbus/kdbus.fs.xml
 create mode 100644 Documentation/kdbus/kdbus.item.xml
 create mode 100644 Documentation/kdbus/kdbus.match.xml
 create mode 100644 Documentation/kdbus/kdbus.message.xml
 create mode 100644 Documentation/kdbus/kdbus.name.xml
 create mode 100644 Documentation/kdbus/kdbus.policy.xml
 create mode 100644 Documentation/kdbus/kdbus.pool.xml
 create mode 100644 Documentation/kdbus/kdbus.xml
 create mode 100644 Documentation/kdbus/stylesheet.xsl
Documentation/Makefile +1 -1
index 6883a1b9b351..5e3fde632d03 100644
@@ -1,4 +1,4 @@
 subdir-y := accounting arm auxdisplay blackfin connector \
-	filesystems filesystems ia64 laptops mic misc-devices \
+	filesystems filesystems ia64 kdbus laptops mic misc-devices \
 	networking pcmcia prctl ptp spi timers vDSO video4linux \
 	watchdog
Documentation/kdbus/Makefile new file +30 -0
new file mode 100644
index 000000000000..cd6b48ee41bf
@@ -0,0 +1,30 @@
+DOCS :=	\
+	kdbus.xml		\
+	kdbus.bus.xml		\
+	kdbus.connection.xml	\
+	kdbus.endpoint.xml	\
+	kdbus.fs.xml		\
+	kdbus.item.xml		\
+	kdbus.match.xml		\
+	kdbus.message.xml	\
+	kdbus.name.xml		\
+	kdbus.policy.xml	\
+	kdbus.pool.xml
+
+XMLFILES := $(addprefix $(obj)/,$(DOCS))
+MANFILES := $(patsubst %.xml, %.7, $(XMLFILES))
+HTMLFILES := $(patsubst %.xml, %.html, $(XMLFILES))
+
+XMLTO_ARGS := -m $(obj)/stylesheet.xsl
+
+%.7: %.xml
+	xmlto man $(XMLTO_ARGS) -o . $<
+
+%.html: %.xml
+	xmlto html-nochunks $(XMLTO_ARGS) -o . $<
+
+mandocs: $(MANFILES)
+
+htmldocs: $(HTMLFILES)
+
+clean-files := $(MANFILES) $(HTMLFILES)
Documentation/kdbus/kdbus.bus.xml new file +360 -0

360 lines not on this page · raw message

Documentation/kdbus/kdbus.connection.xml new file +1252 -0

1,252 lines not on this page · raw message

Documentation/kdbus/kdbus.endpoint.xml new file +436 -0

436 lines not on this page · raw message

Documentation/kdbus/kdbus.fs.xml new file +124 -0

124 lines not on this page · raw message

Documentation/kdbus/kdbus.item.xml new file +840 -0

793 lines not on this page · raw message

Documentation/kdbus/kdbus.match.xml new file +553 -0

553 lines not on this page · raw message

Documentation/kdbus/kdbus.message.xml new file +1277 -0

1,277 lines not on this page · raw message

Documentation/kdbus/kdbus.name.xml new file +711 -0

711 lines not on this page · raw message

Documentation/kdbus/kdbus.policy.xml new file +406 -0

406 lines not on this page · raw message

Documentation/kdbus/kdbus.pool.xml new file +320 -0

320 lines not on this page · raw message

Documentation/kdbus/kdbus.xml new file +1012 -0

1,012 lines not on this page · raw message

Documentation/kdbus/stylesheet.xsl new file +16 -0
new file mode 100644
index 000000000000..52565eac7d0d

Lines of this file are not shown. View the raw message.

Makefile +1 -0
index 1100ff3c77e3..08c98188a37a 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.3.1

Part of the diff is not shown here. View the raw message.

#2 lore raw

PATCH v4 2/14 kdbus: add uapi header file

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

This patch adds the header file which describes the low-level transport protocol used by various ioctls. The header file is located in include/uapi/linux/ as it is shared between kernel and userspace, and it only contains data structure definitions, enums and defines for constants.

The low-level kernel API of kdbus is exposed through ioctls, employed on nodes exposed by kdbusfs. We've chosen a ioctl-based implementation over syscalls for various reaons:

* The ioctls kdbus offers are completely specific to nodes exposed by kdbusfs and can not be applied to any other file descriptor in a system.

* The file descriptors derived from opening nodes in kdbusfs can only be used for poll(), close() and the ioctls described in kdbus.h.

* Not all systems will make use of kdbus eventually, and we want to make as many parts of the kernel optional at build time.

* We want to build the kdbus code as module, which is impossible to do when implemented with syscalls.

* The ioctl dispatching logic does not show up in our performance graphs; its overhead is negligible.

* For development, being able to build, load and unload a separate module with a versioned name suffix is essential.

---
 include/uapi/linux/Kbuild  |   1 +
 include/uapi/linux/kdbus.h | 979 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 980 insertions(+)
 create mode 100644 include/uapi/linux/kdbus.h
include/uapi/linux/Kbuild +1 -0
index 68ceb97c458c..ddc413e1959f 100644
@@ -214,6 +214,7 @@ header-y += ixjuser.h
 header-y += jffs2.h
 header-y += joystick.h
 header-y += kcmp.h
+header-y += kdbus.h
 header-y += kdev_t.h
 header-y += kd.h
 header-y += kernelcapi.h
include/uapi/linux/kdbus.h new file +979 -0

979 lines not on this page · raw message

Signature · 1 line
2.3.1
#3 lore raw

PATCH v4 3/14 kdbus: add driver skeleton, ioctl entry points and utility functions

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

Add the basic driver structure.

handle.c is the main ioctl command dispatcher that calls into other parts of the driver.

main.c contains the code that creates the initial domain at startup, and util.c has utility functions such as item iterators that are shared with other files.

limits.h describes limits on things like maximum data structure sizes, number of messages per users and suchlike. Some of the numbers currently picked are rough ideas of what what might be sufficient and are probably rather conservative.

---
 Documentation/ioctl/ioctl-number.txt |   1 +
 ipc/kdbus/handle.c                   | 617 +++++++++++++++++++++++++++++++++++
 ipc/kdbus/handle.h                   |  85 +++++
 ipc/kdbus/limits.h                   |  64 ++++
 ipc/kdbus/main.c                     | 125 +++++++
 ipc/kdbus/util.c                     | 201 ++++++++++++
 ipc/kdbus/util.h                     |  74 +++++
 7 files changed, 1167 insertions(+)
 create mode 100644 ipc/kdbus/handle.c
 create mode 100644 ipc/kdbus/handle.h
 create mode 100644 ipc/kdbus/limits.h
 create mode 100644 ipc/kdbus/main.c
 create mode 100644 ipc/kdbus/util.c
 create mode 100644 ipc/kdbus/util.h
Documentation/ioctl/ioctl-number.txt +1 -0
index 8136e1fd30fd..54e091ebb862 100644
@@ -292,6 +292,7 @@ Code  Seq#(hex)	Include File		Comments
 0x92	00-0F	drivers/usb/mon/mon_bin.c
 0x93	60-7F	linux/auto_fs.h
 0x94	all	fs/btrfs/ioctl.h
+0x95	all	uapi/linux/kdbus.h	kdbus IPC driver
 0x97	00-7F	fs/ceph/ioctl.h		Ceph file system
 0x99	00-0F				537-Addinboard driver
 					<mailto:[email protected]>
ipc/kdbus/handle.c new file +617 -0

617 lines not on this page · raw message

ipc/kdbus/handle.h new file +85 -0

85 lines not on this page · raw message

ipc/kdbus/limits.h new file +64 -0

64 lines not on this page · raw message

ipc/kdbus/main.c new file +125 -0

125 lines not on this page · raw message

ipc/kdbus/util.c new file +201 -0

201 lines not on this page · raw message

ipc/kdbus/util.h new file +74 -0

74 lines not on this page · raw message

Signature · 1 line
2.3.1
#4 lore raw

PATCH v4 4/14 kdbus: add connection pool implementation

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

A pool for data received from the kernel is installed for every connection of the bus, and it is used to copy data from the kernel to userspace clients, for messages and other information.

It is accessed when one of the following ioctls is issued:

* KDBUS_CMD_MSG_RECV, to receive a message
* KDBUS_CMD_NAME_LIST, to dump the name registry
* KDBUS_CMD_CONN_INFO, to retrieve information on a connection

The offsets returned by either one of the aforementioned ioctls describe offsets inside the pool. Internally, the pool is organized in slices, that are dynamically allocated on demand. The overall size of the pool is chosen by the connection when it connects to the bus with KDBUS_CMD_HELLO.

In order to make the slice available for subsequent calls, KDBUS_CMD_FREE has to be called on the offset.

To access the memory, the caller is expected to mmap() it to its task.

---
 ipc/kdbus/pool.c | 728 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ipc/kdbus/pool.h |  46 ++++
 2 files changed, 774 insertions(+)
 create mode 100644 ipc/kdbus/pool.c
 create mode 100644 ipc/kdbus/pool.h
ipc/kdbus/pool.c new file +728 -0
new file mode 100644
index 000000000000..139bb77056b3
@@ -0,0 +1,728 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2013-2015 Daniel Mack <[email protected]>
+ * Copyright (C) 2013-2015 David Herrmann <[email protected]>
+ * Copyright (C) 2013-2015 Linux Foundation
+ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz-Umm1ozX2/[email protected]>
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/aio.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/highmem.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/pagemap.h>
+#include <linux/rbtree.h>
+#include <linux/sched.h>
+#include <linux/shmem_fs.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+
+#include "pool.h"
+#include "util.h"
+
+/**
+ * struct kdbus_pool - the receiver's buffer
+ * @f:			The backing shmem file
+ * @size:		The size of the file
+ * @accounted_size:	Currently accounted memory in bytes
+ * @lock:		Pool data lock
+ * @slices:		All slices sorted by address
+ * @slices_busy:	Tree of allocated slices
+ * @slices_free:	Tree of free slices
+ *
+ * The receiver's buffer, managed as a pool of allocated and free
+ * slices containing the queued messages.
+ *
+ * Messages sent with KDBUS_CMD_SEND are copied direcly by the
+ * sending process into the receiver's pool.
+ *
+ * Messages received with KDBUS_CMD_RECV just return the offset
+ * to the data placed in the pool.
+ *
+ * The internally allocated memory needs to be returned by the receiver
+ * with KDBUS_CMD_FREE.
+ */
+struct kdbus_pool {
+	struct file *f;
+	size_t size;
+	size_t accounted_size;
+	struct mutex lock;
+
+	struct list_head slices;
+	struct rb_root slices_busy;
+	struct rb_root slices_free;
+};
+
+/**
+ * struct kdbus_pool_slice - allocated element in kdbus_pool
+ * @pool:		Pool this slice belongs to
+ * @off:		Offset of slice in the shmem file
+ * @size:		Size of slice
+ * @entry:		Entry in "all slices" list
+ * @rb_node:		Entry in free or busy list
+ * @free:		Unused slice
+ * @accounted:		Accounted as queue slice
+ * @ref_kernel:		Kernel holds a reference
+ * @ref_user:		Userspace holds a reference
+ *
+ * The pool has one or more slices, always spanning the entire size of the
+ * pool.
+ *
+ * Every slice is an element in a list sorted by the buffer address, to
+ * provide access to the next neighbor slice.
+ *
+ * Every slice is member in either the busy or the free tree. The free
+ * tree is organized by slice size, the busy tree organized by buffer
+ * offset.
+ */
+struct kdbus_pool_slice {
+	struct kdbus_pool *pool;
+	size_t off;
+	size_t size;
+
+	struct list_head entry;
+	struct rb_node rb_node;
+
+	bool free:1;
+	bool accounted:1;
+	bool ref_kernel:1;
+	bool ref_user:1;
+};
+
+static struct kdbus_pool_slice *kdbus_pool_slice_new(struct kdbus_pool *pool,
+						     size_t off, size_t size)
+{
+	struct kdbus_pool_slice *slice;
+
+	slice = kzalloc(sizeof(*slice), GFP_KERNEL);
+	if (!slice)
+		return NULL;
+
+	slice->pool = pool;
+	slice->off = off;
+	slice->size = size;
+	slice->free = true;
+	return slice;
+}
+
+/* insert a slice into the free tree */
+static void kdbus_pool_add_free_slice(struct kdbus_pool *pool,
+				      struct kdbus_pool_slice *slice)
+{
+	struct rb_node **n;
+	struct rb_node *pn = NULL;
+
+	n = &pool->slices_free.rb_node;
+	while (*n) {
+		struct kdbus_pool_slice *pslice;
+
+		pn = *n;
+		pslice = rb_entry(pn, struct kdbus_pool_slice, rb_node);
+		if (slice->size < pslice->size)
+			n = &pn->rb_left;
+		else
+			n = &pn->rb_right;
+	}
+
+	rb_link_node(&slice->rb_node, pn, n);
+	rb_insert_color(&slice->rb_node, &pool->slices_free);
+}
+
+/* insert a slice into the busy tree */
+static void kdbus_pool_add_busy_slice(struct kdbus_pool *pool,
+				      struct kdbus_pool_slice *slice)
+{
+	struct rb_node **n;
+	struct rb_node *pn = NULL;
+
+	n = &pool->slices_busy.rb_node;
+	while (*n) {
+		struct kdbus_pool_slice *pslice;
+
+		pn = *n;
+		pslice = rb_entry(pn, struct kdbus_pool_slice, rb_node);
+		if (slice->off < pslice->off)
+			n = &pn->rb_left;
+		else if (slice->off > pslice->off)
+			n = &pn->rb_right;
+		else
+			BUG();
+	}
+
+	rb_link_node(&slice->rb_node, pn, n);
+	rb_insert_color(&slice->rb_node, &pool->slices_busy);
+}
+
+static struct kdbus_pool_slice *kdbus_pool_find_slice(struct kdbus_pool *pool,
+						      size_t off)
+{
+	struct rb_node *n;
+
+	n = pool->slices_busy.rb_node;
+	while (n) {
+		struct kdbus_pool_slice *s;
+
+		s = rb_entry(n, struct kdbus_pool_slice, rb_node);
+		if (off < s->off)
+			n = n->rb_left;
+		else if (off > s->off)
+			n = n->rb_right;
+		else
+			return s;
+	}
+
+	return NULL;
+}
+
+/**
+ * kdbus_pool_slice_alloc() - allocate memory from a pool
+ * @pool:	The receiver's pool
+ * @size:	The number of bytes to allocate
+ * @accounted:	Whether this slice should be accounted for
+ *
+ * The returned slice is used for kdbus_pool_slice_release() to
+ * free the allocated memory. If either @kvec or @iovec is non-NULL, the data
+ * will be copied from kernel or userspace memory into the new slice at
+ * offset 0.
+ *
+ * Return: the allocated slice on success, ERR_PTR on failure.
+ */
+struct kdbus_pool_slice *kdbus_pool_slice_alloc(struct kdbus_pool *pool,
+						size_t size, bool accounted)
+{
+	size_t slice_size = KDBUS_ALIGN8(size);
+	struct rb_node *n, *found = NULL;
+	struct kdbus_pool_slice *s;
+	int ret = 0;
+
+	if (WARN_ON(!size))
+		return ERR_PTR(-EINVAL);
+
+	/* search a free slice with the closest matching size */
+	mutex_lock(&pool->lock);
+	n = pool->slices_free.rb_node;
+	while (n) {
+		s = rb_entry(n, struct kdbus_pool_slice, rb_node);
+		if (slice_size < s->size) {
+			found = n;
+			n = n->rb_left;
+		} else if (slice_size > s->size) {
+			n = n->rb_right;
+		} else {
+			found = n;
+			break;
+		}
+	}
+
+	/* no slice with the minimum size found in the pool */
+	if (!found) {
+		ret = -EXFULL;
+		goto exit_unlock;
+	}
+
+	/* no exact match, use the closest one */
+	if (!n) {
+		struct kdbus_pool_slice *s_new;
+
+		s = rb_entry(found, struct kdbus_pool_slice, rb_node);
+
+		/* split-off the remainder of the size to its own slice */
+		s_new = kdbus_pool_slice_new(pool, s->off + slice_size,
+					     s->size - slice_size);
+		if (!s_new) {
+			ret = -ENOMEM;
+			goto exit_unlock;
+		}
+
+		list_add(&s_new->entry, &s->entry);
+		kdbus_pool_add_free_slice(pool, s_new);
+
+		/* adjust our size now that we split-off another slice */
+		s->size = slice_size;
+	}
+
+	/* move slice from free to the busy tree */
+	rb_erase(found, &pool->slices_free);
+	kdbus_pool_add_busy_slice(pool, s);
+
+	WARN_ON(s->ref_kernel || s->ref_user);
+
+	s->ref_kernel = true;
+	s->free = false;
+	s->accounted = accounted;
+	if (accounted)
+		pool->accounted_size += s->size;
+	mutex_unlock(&pool->lock);
+
+	return s;
+
+exit_unlock:
+	mutex_unlock(&pool->lock);
+	return ERR_PTR(ret);
+}
+
+static void __kdbus_pool_slice_release(struct kdbus_pool_slice *slice)
+{
+	struct kdbus_pool *pool = slice->pool;
+
+	/* don't free the slice if either has a reference */
+	if (slice->ref_kernel || slice->ref_user)
+		return;
+
+	if (WARN_ON(slice->free))
+		return;
+
+	rb_erase(&slice->rb_node, &pool->slices_busy);
+
+	/* merge with the next free slice */
+	if (!list_is_last(&slice->entry, &pool->slices)) {
+		struct kdbus_pool_slice *s;
+
+		s = list_entry(slice->entry.next,
+			       struct kdbus_pool_slice, entry);
+		if (s->free) {
+			rb_erase(&s->rb_node, &pool->slices_free);
+			list_del(&s->entry);
+			slice->size += s->size;
+			kfree(s);
+		}
+	}
+
+	/* merge with previous free slice */
+	if (pool->slices.next != &slice->entry) {
+		struct kdbus_pool_slice *s;
+
+		s = list_entry(slice->entry.prev,
+			       struct kdbus_pool_slice, entry);
+		if (s->free) {
+			rb_erase(&s->rb_node, &pool->slices_free);
+			list_del(&slice->entry);
+			s->size += slice->size;
+			kfree(slice);
+			slice = s;
+		}
+	}
+
+	slice->free = true;
+	kdbus_pool_add_free_slice(pool, slice);
+}
+
+/**
+ * kdbus_pool_slice_release() - drop kernel-reference on allocated slice
+ * @slice:		Slice allocated from the pool
+ *
+ * This releases the kernel-reference on the given slice. If the
+ * kernel-reference and the user-reference on a slice are dropped, the slice is
+ * returned to the pool.
+ *
+ * So far, we do not implement full ref-counting on slices. Each, kernel and
+ * user-space can have exactly one reference to a slice. If both are dropped at
+ * the same time, the slice is released.
+ */
+void kdbus_pool_slice_release(struct kdbus_pool_slice *slice)
+{
+	struct kdbus_pool *pool;
+
+	if (!slice)
+		return;
+
+	/* @slice may be freed, so keep local ptr to @pool */
+	pool = slice->pool;
+
+	mutex_lock(&pool->lock);
+	/* kernel must own a ref to @slice to drop it */
+	WARN_ON(!slice->ref_kernel);
+	slice->ref_kernel = false;
+	/* no longer kernel-owned, de-account slice */
+	if (slice->accounted && !WARN_ON(pool->accounted_size < slice->size))
+		pool->accounted_size -= slice->size;
+	__kdbus_pool_slice_release(slice);
+	mutex_unlock(&pool->lock);
+}
+
+/**
+ * kdbus_pool_release_offset() - release a public offset
+ * @pool:		pool to operate on
+ * @off:		offset to release
+ *
+ * This should be called whenever user-space frees a slice given to them. It
+ * verifies the slice is available and public, and then drops it. It ensures
+ * correct locking and barriers against queues.
+ *
+ * Return: 0 on success, ENXIO if the offset is invalid or not public.
+ */
+int kdbus_pool_release_offset(struct kdbus_pool *pool, size_t off)
+{
+	struct kdbus_pool_slice *slice;
+	int ret = 0;
+
+	/* 'pool->size' is used as dummy offset for empty slices */
+	if (off == pool->size)
+		return 0;
+
+	mutex_lock(&pool->lock);
+	slice = kdbus_pool_find_slice(pool, off);
+	if (slice && slice->ref_user) {
+		slice->ref_user = false;
+		__kdbus_pool_slice_release(slice);
+	} else {
+		ret = -ENXIO;
+	}
+	mutex_unlock(&pool->lock);
+
+	return ret;
+}
+
+/**
+ * kdbus_pool_publish_empty() - publish empty slice to user-space
+ * @pool:		pool to operate on
+ * @off:		output storage for offset, or NULL
+ * @size:		output storage for size, or NULL
+ *
+ * This is the same as kdbus_pool_slice_publish(), but uses a dummy slice with
+ * size 0. The returned offset points to the end of the pool and is never
+ * returned on real slices.
+ */
+void kdbus_pool_publish_empty(struct kdbus_pool *pool, u64 *off, u64 *size)
+{
+	if (off)
+		*off = pool->size;
+	if (size)
+		*size = 0;
+}
+
+/**
+ * kdbus_pool_slice_publish() - publish slice to user-space
+ * @slice:		The slice
+ * @out_offset:		Output storage for offset, or NULL
+ * @out_size:		Output storage for size, or NULL
+ *
+ * This prepares a slice to be published to user-space.
+ *
+ * This call combines the following operations:
+ *   * the memory region is flushed so the user's memory view is consistent
+ *   * the slice is marked as referenced by user-space, so user-space has to
+ *     call KDBUS_CMD_FREE to release it
+ *   * the offset and size of the slice are written to the given output
+ *     arguments, if non-NULL
+ */
+void kdbus_pool_slice_publish(struct kdbus_pool_slice *slice,
+			      u64 *out_offset, u64 *out_size)
+{
+	mutex_lock(&slice->pool->lock);
+	/* kernel must own a ref to @slice to gain a user-space ref */
+	WARN_ON(!slice->ref_kernel);
+	slice->ref_user = true;
+	mutex_unlock(&slice->pool->lock);
+
+	if (out_offset)
+		*out_offset = slice->off;
+	if (out_size)
+		*out_size = slice->size;
+}
+
+/**
+ * kdbus_pool_slice_offset() - Get a slice's offset inside the pool
+ * @slice:	Slice to return the offset of
+ *
+ * Return: The internal offset @slice inside the pool.
+ */
+off_t kdbus_pool_slice_offset(const struct kdbus_pool_slice *slice)
+{
+	return slice->off;
+}
+
+/**
+ * kdbus_pool_slice_size() - get size of a pool slice
+ * @slice:	slice to query
+ *
+ * Return: size of the given slice
+ */
+size_t kdbus_pool_slice_size(const struct kdbus_pool_slice *slice)
+{
+	return slice->size;
+}
+
+/**
+ * kdbus_pool_new() - create a new pool
+ * @name:		Name of the (deleted) file which shows up in
+ *			/proc, used for debugging
+ * @size:		Maximum size of the pool
+ *
+ * Return: a new kdbus_pool on success, ERR_PTR on failure.
+ */
+struct kdbus_pool *kdbus_pool_new(const char *name, size_t size)
+{
+	struct kdbus_pool_slice *s;
+	struct kdbus_pool *p;
+	struct file *f;
+	char *n = NULL;
+	int ret;
+
+	p = kzalloc(sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return ERR_PTR(-ENOMEM);
+
+	if (name) {
+		n = kasprintf(GFP_KERNEL, KBUILD_MODNAME "-conn:%s", name);
+		if (!n) {
+			ret = -ENOMEM;
+			goto exit_free;
+		}
+	}
+
+	f = shmem_file_setup(n ?: KBUILD_MODNAME "-conn", size, 0);
+	kfree(n);
+
+	if (IS_ERR(f)) {
+		ret = PTR_ERR(f);
+		goto exit_free;
+	}
+
+	ret = get_write_access(file_inode(f));
+	if (ret < 0)
+		goto exit_put_shmem;
+
+	/* allocate first slice spanning the entire pool */
+	s = kdbus_pool_slice_new(p, 0, size);
+	if (!s) {
+		ret = -ENOMEM;
+		goto exit_put_write;
+	}
+
+	p->f = f;
+	p->size = size;
+	p->slices_free = RB_ROOT;
+	p->slices_busy = RB_ROOT;
+	mutex_init(&p->lock);
+
+	INIT_LIST_HEAD(&p->slices);
+	list_add(&s->entry, &p->slices);
+
+	kdbus_pool_add_free_slice(p, s);
+	return p;
+
+exit_put_write:
+	put_write_access(file_inode(f));
+exit_put_shmem:
+	fput(f);
+exit_free:
+	kfree(p);
+	return ERR_PTR(ret);
+}
+
+/**
+ * kdbus_pool_free() - destroy pool
+ * @pool:		The receiver's pool
+ */
+void kdbus_pool_free(struct kdbus_pool *pool)
+{
+	struct kdbus_pool_slice *s, *tmp;
+
+	if (!pool)
+		return;
+
+	list_for_each_entry_safe(s, tmp, &pool->slices, entry) {
+		list_del(&s->entry);
+		kfree(s);
+	}
+
+	put_write_access(file_inode(pool->f));
+	fput(pool->f);
+	kfree(pool);
+}
+
+/**
+ * kdbus_pool_accounted() - retrieve accounting information
+ * @pool:		pool to query
+ * @size:		output for overall pool size
+ * @acc:		output for currently accounted size
+ *
+ * This returns accounting information of the pool. Note that the data might
+ * change after the function returns, as the pool lock is dropped. You need to
+ * protect the data via other means, if you need reliable accounting.
+ */
+void kdbus_pool_accounted(struct kdbus_pool *pool, size_t *size, size_t *acc)
+{
+	mutex_lock(&pool->lock);
+	if (size)
+		*size = pool->size;
+	if (acc)
+		*acc = pool->accounted_size;
+	mutex_unlock(&pool->lock);
+}
+
+/**
+ * kdbus_pool_slice_copy_iovec() - copy user memory to a slice
+ * @slice:		The slice to write to
+ * @off:		Offset in the slice to write to
+ * @iov:		iovec array, pointing to data to copy
+ * @iov_len:		Number of elements in @iov
+ * @total_len:		Total number of bytes described in members of @iov
+ *
+ * User memory referenced by @iov will be copied into @slice at offset @off.
+ *
+ * Return: the numbers of bytes copied, negative errno on failure.
+ */
+ssize_t
+kdbus_pool_slice_copy_iovec(const struct kdbus_pool_slice *slice, loff_t off,
+			    struct iovec *iov, size_t iov_len, size_t total_len)
+{
+	struct iov_iter iter;
+	ssize_t len;
+
+	if (WARN_ON(off + total_len > slice->size))
+		return -EFAULT;
+
+	off += slice->off;
+	iov_iter_init(&iter, WRITE, iov, iov_len, total_len);
+	len = vfs_iter_write(slice->pool->f, &iter, &off);
+
+	return (len >= 0 && len != total_len) ? -EFAULT : len;
+}
+
+/**
+ * kdbus_pool_slice_copy_kvec() - copy kernel memory to a slice
+ * @slice:		The slice to write to
+ * @off:		Offset in the slice to write to
+ * @kvec:		kvec array, pointing to data to copy
+ * @kvec_len:		Number of elements in @kvec
+ * @total_len:		Total number of bytes described in members of @kvec
+ *
+ * Kernel memory referenced by @kvec will be copied into @slice at offset @off.
+ *
+ * Return: the numbers of bytes copied, negative errno on failure.
+ */
+ssize_t kdbus_pool_slice_copy_kvec(const struct kdbus_pool_slice *slice,
+				   loff_t off, struct kvec *kvec,
+				   size_t kvec_len, size_t total_len)
+{
+	struct iov_iter iter;
+	mm_segment_t old_fs;
+	ssize_t len;
+
+	if (WARN_ON(off + total_len > slice->size))
+		return -EFAULT;
+
+	off += slice->off;
+	iov_iter_kvec(&iter, WRITE | ITER_KVEC, kvec, kvec_len, total_len);
+
+	old_fs = get_fs();
+	set_fs(get_ds());
+	len = vfs_iter_write(slice->pool->f, &iter, &off);
+	set_fs(old_fs);
+
+	return (len >= 0 && len != total_len) ? -EFAULT : len;
+}
+
+/**
+ * kdbus_pool_slice_copy() - copy data from one slice into another
+ * @slice_dst:		destination slice
+ * @slice_src:		source slice
+ *
+ * Return: 0 on success, negative error number on failure.
+ */
+int kdbus_pool_slice_copy(const struct kdbus_pool_slice *slice_dst,
+			  const struct kdbus_pool_slice *slice_src)
+{
+	struct file *f_src = slice_src->pool->f;
+	struct file *f_dst = slice_dst->pool->f;
+	struct inode *i_dst = file_inode(f_dst);
+	struct address_space *mapping_dst = f_dst->f_mapping;
+	const struct address_space_operations *aops = mapping_dst->a_ops;
+	unsigned long len = slice_src->size;
+	loff_t off_src = slice_src->off;
+	loff_t off_dst = slice_dst->off;
+	mm_segment_t old_fs;
+	int ret = 0;
+
+	if (WARN_ON(slice_src->size != slice_dst->size) ||
+	    WARN_ON(slice_src->free || slice_dst->free))
+		return -EINVAL;
+
+	mutex_lock(&i_dst->i_mutex);
+	old_fs = get_fs();
+	set_fs(get_ds());
+	while (len > 0) {
+		unsigned long page_off;
+		unsigned long copy_len;
+		char __user *kaddr;
+		struct page *page;
+		ssize_t n_read;
+		void *fsdata;
+		long status;
+
+		page_off = off_dst & (PAGE_CACHE_SIZE - 1);
+		copy_len = min_t(unsigned long,
+				 PAGE_CACHE_SIZE - page_off, len);
+
+		status = aops->write_begin(f_dst, mapping_dst, off_dst,
+					   copy_len, 0, &page, &fsdata);
+		if (unlikely(status < 0)) {
+			ret = status;
+			break;
+		}
+
+		kaddr = (char __force __user *)kmap(page) + page_off;
+		n_read = f_src->f_op->read(f_src, kaddr, copy_len, &off_src);
+		kunmap(page);
+		mark_page_accessed(page);
+		flush_dcache_page(page);
+
+		if (unlikely(n_read != copy_len)) {
+			ret = -EFAULT;
+			break;
+		}
+
+		status = aops->write_end(f_dst, mapping_dst, off_dst,
+					 copy_len, copy_len, page, fsdata);
+		if (unlikely(status != copy_len)) {
+			ret = -EFAULT;
+			break;
+		}
+
+		off_dst += copy_len;
+		len -= copy_len;
+	}
+	set_fs(old_fs);
+	mutex_unlock(&i_dst->i_mutex);
+
+	return ret;
+}
+
+/**
+ * kdbus_pool_mmap() -  map the pool into the process
+ * @pool:		The receiver's pool
+ * @vma:		passed by mmap() syscall
+ *
+ * Return: the result of the mmap() call, negative errno on failure.
+ */
+int kdbus_pool_mmap(const struct kdbus_pool *pool, struct vm_area_struct *vma)
+{
+	/* deny write access to the pool */
+	if (vma->vm_flags & VM_WRITE)
+		return -EPERM;
+	vma->vm_flags &= ~VM_MAYWRITE;
+
+	/* do not allow to map more than the size of the file */
+	if ((vma->vm_end - vma->vm_start) > pool->size)
+		return -EFAULT;
+
+	/* replace the connection file with our shmem file */
+	if (vma->vm_file)
+		fput(vma->vm_file);
+	vma->vm_file = get_file(pool->f);
+
+	return pool->f->f_op->mmap(pool->f, vma);
+}
ipc/kdbus/pool.h new file +46 -0

46 lines not on this page · raw message

Signature · 1 line
2.3.1
#5 lore raw

PATCH v4 5/14 kdbus: add connection, queue handling and message validation code

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

This patch adds code to create and destroy connections, to validate incoming messages and to maintain the queue of messages that are associated with a connection.

Note that connection and queue have a 1:1 relation, the code is only split in two parts for cleaner separation and better readability.

---
 ipc/kdbus/connection.c | 2215 ++++++++++++++++++++++++++++++++++++++++++++++++
 ipc/kdbus/connection.h |  257 ++++++
 ipc/kdbus/item.c       |  339 ++++++++
 ipc/kdbus/item.h       |   64 ++
 ipc/kdbus/message.c    |  616 ++++++++++++++
 ipc/kdbus/message.h    |  133 +++
 ipc/kdbus/queue.c      |  678 +++++++++++++++
 ipc/kdbus/queue.h      |   92 ++
 ipc/kdbus/reply.c      |  259 ++++++
 ipc/kdbus/reply.h      |   68 ++
 ipc/kdbus/util.h       |    2 +-
 11 files changed, 4722 insertions(+), 1 deletion(-)
 create mode 100644 ipc/kdbus/connection.c
 create mode 100644 ipc/kdbus/connection.h
 create mode 100644 ipc/kdbus/item.c
 create mode 100644 ipc/kdbus/item.h
 create mode 100644 ipc/kdbus/message.c
 create mode 100644 ipc/kdbus/message.h
 create mode 100644 ipc/kdbus/queue.c
 create mode 100644 ipc/kdbus/queue.h
 create mode 100644 ipc/kdbus/reply.c
 create mode 100644 ipc/kdbus/reply.h
ipc/kdbus/connection.c new file +2215 -0
new file mode 100644
index 000000000000..e554f1a71aa1
@@ -0,0 +1,2215 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2013-2015 Daniel Mack <[email protected]>
+ * Copyright (C) 2013-2015 David Herrmann <[email protected]>
+ * Copyright (C) 2013-2015 Linux Foundation
+ * Copyright (C) 2014-2015 Djalal Harouni <[email protected]>
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/audit.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/fs_struct.h>
+#include <linux/hashtable.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/math64.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/path.h>
+#include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/shmem_fs.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/syscalls.h>
+#include <linux/uio.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "endpoint.h"
+#include "handle.h"
+#include "match.h"
+#include "message.h"
+#include "metadata.h"
+#include "names.h"
+#include "domain.h"
+#include "item.h"
+#include "notify.h"
+#include "policy.h"
+#include "pool.h"
+#include "reply.h"
+#include "util.h"
+#include "queue.h"
+
+#define KDBUS_CONN_ACTIVE_BIAS	(INT_MIN + 2)
+#define KDBUS_CONN_ACTIVE_NEW	(INT_MIN + 1)
+
+static struct kdbus_conn *kdbus_conn_new(struct kdbus_ep *ep, bool privileged,
+					 struct kdbus_cmd_hello *hello,
+					 const char *name,
+					 const struct kdbus_creds *creds,
+					 const struct kdbus_pids *pids,
+					 const char *seclabel,
+					 const char *conn_description)
+{
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+	static struct lock_class_key __key;
+#endif
+	struct kdbus_pool_slice *slice = NULL;
+	struct kdbus_bus *bus = ep->bus;
+	struct kdbus_conn *conn;
+	u64 attach_flags_send;
+	u64 attach_flags_recv;
+	u64 items_size = 0;
+	bool is_policy_holder;
+	bool is_activator;
+	bool is_monitor;
+	struct kvec kvec;
+	int ret;
+
+	struct {
+		u64 size;
+		u64 type;
+		struct kdbus_bloom_parameter bloom;
+	} bloom_item;
+
+	is_monitor = hello->flags & KDBUS_HELLO_MONITOR;
+	is_activator = hello->flags & KDBUS_HELLO_ACTIVATOR;
+	is_policy_holder = hello->flags & KDBUS_HELLO_POLICY_HOLDER;
+
+	if (!hello->pool_size || !IS_ALIGNED(hello->pool_size, PAGE_SIZE))
+		return ERR_PTR(-EINVAL);
+	if (is_monitor + is_activator + is_policy_holder > 1)
+		return ERR_PTR(-EINVAL);
+	if (name && !is_activator && !is_policy_holder)
+		return ERR_PTR(-EINVAL);
+	if (!name && (is_activator || is_policy_holder))
+		return ERR_PTR(-EINVAL);
+	if (name && !kdbus_name_is_valid(name, true))
+		return ERR_PTR(-EINVAL);
+	if (is_monitor && ep->user)
+		return ERR_PTR(-EOPNOTSUPP);
+	if (!privileged && (is_activator || is_policy_holder || is_monitor))
+		return ERR_PTR(-EPERM);
+	if ((creds || pids || seclabel) && !privileged)
+		return ERR_PTR(-EPERM);
+
+	ret = kdbus_sanitize_attach_flags(hello->attach_flags_send,
+					  &attach_flags_send);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	ret = kdbus_sanitize_attach_flags(hello->attach_flags_recv,
+					  &attach_flags_recv);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	/* The attach flags must always satisfy the bus requirements. */
+	if (bus->attach_flags_req & ~attach_flags_send)
+		return ERR_PTR(-ECONNREFUSED);
+
+	conn = kzalloc(sizeof(*conn), GFP_KERNEL);
+	if (!conn)
+		return ERR_PTR(-ENOMEM);
+
+	kref_init(&conn->kref);
+	atomic_set(&conn->active, KDBUS_CONN_ACTIVE_NEW);
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+	lockdep_init_map(&conn->dep_map, "s_active", &__key, 0);
+#endif
+	mutex_init(&conn->lock);
+	INIT_LIST_HEAD(&conn->names_list);
+	INIT_LIST_HEAD(&conn->names_queue_list);
+	INIT_LIST_HEAD(&conn->reply_list);
+	atomic_set(&conn->name_count, 0);
+	atomic_set(&conn->request_count, 0);
+	atomic_set(&conn->lost_count, 0);
+	INIT_DELAYED_WORK(&conn->work, kdbus_reply_list_scan_work);
+	conn->cred = get_current_cred();
+	init_waitqueue_head(&conn->wait);
+	kdbus_queue_init(&conn->queue);
+	conn->privileged = privileged;
+	conn->ep = kdbus_ep_ref(ep);
+	conn->id = atomic64_inc_return(&bus->domain->last_id);
+	conn->flags = hello->flags;
+	atomic64_set(&conn->attach_flags_send, attach_flags_send);
+	atomic64_set(&conn->attach_flags_recv, attach_flags_recv);
+	INIT_LIST_HEAD(&conn->monitor_entry);
+
+	if (conn_description) {
+		conn->description = kstrdup(conn_description, GFP_KERNEL);
+		if (!conn->description) {
+			ret = -ENOMEM;
+			goto exit_unref;
+		}
+	}
+
+	conn->pool = kdbus_pool_new(conn->description, hello->pool_size);
+	if (IS_ERR(conn->pool)) {
+		ret = PTR_ERR(conn->pool);
+		conn->pool = NULL;
+		goto exit_unref;
+	}
+
+	conn->match_db = kdbus_match_db_new();
+	if (IS_ERR(conn->match_db)) {
+		ret = PTR_ERR(conn->match_db);
+		conn->match_db = NULL;
+		goto exit_unref;
+	}
+
+	/* return properties of this connection to the caller */
+	hello->bus_flags = bus->bus_flags;
+	hello->id = conn->id;
+
+	BUILD_BUG_ON(sizeof(bus->id128) != sizeof(hello->id128));
+	memcpy(hello->id128, bus->id128, sizeof(hello->id128));
+
+	conn->meta = kdbus_meta_proc_new();
+	if (IS_ERR(conn->meta)) {
+		ret = PTR_ERR(conn->meta);
+		conn->meta = NULL;
+		goto exit_unref;
+	}
+
+	/* privileged processes can impersonate somebody else */
+	if (creds || pids || seclabel) {
+		ret = kdbus_meta_proc_fake(conn->meta, creds, pids, seclabel);
+		if (ret < 0)
+			goto exit_unref;
+
+		conn->faked_meta = true;
+	} else {
+		ret = kdbus_meta_proc_collect(conn->meta,
+					      KDBUS_ATTACH_CREDS |
+					      KDBUS_ATTACH_PIDS |
+					      KDBUS_ATTACH_AUXGROUPS |
+					      KDBUS_ATTACH_TID_COMM |
+					      KDBUS_ATTACH_PID_COMM |
+					      KDBUS_ATTACH_EXE |
+					      KDBUS_ATTACH_CMDLINE |
+					      KDBUS_ATTACH_CGROUP |
+					      KDBUS_ATTACH_CAPS |
+					      KDBUS_ATTACH_SECLABEL |
+					      KDBUS_ATTACH_AUDIT);
+		if (ret < 0)
+			goto exit_unref;
+	}
+
+	/*
+	 * Account the connection against the current user (UID), or for
+	 * custom endpoints use the anonymous user assigned to the endpoint.
+	 * Note that limits are always accounted against the real UID, not
+	 * the effective UID (cred->user always points to the accounting of
+	 * cred->uid, not cred->euid).
+	 */
+	if (ep->user) {
+		conn->user = kdbus_user_ref(ep->user);
+	} else {
+		conn->user = kdbus_user_lookup(ep->bus->domain, current_uid());
+		if (IS_ERR(conn->user)) {
+			ret = PTR_ERR(conn->user);
+			conn->user = NULL;
+			goto exit_unref;
+		}
+	}
+
+	if (atomic_inc_return(&conn->user->connections) > KDBUS_USER_MAX_CONN) {
+		/* decremented by destructor as conn->user is valid */
+		ret = -EMFILE;
+		goto exit_unref;
+	}
+
+	bloom_item.size = sizeof(bloom_item);
+	bloom_item.type = KDBUS_ITEM_BLOOM_PARAMETER;
+	bloom_item.bloom = bus->bloom;
+	kdbus_kvec_set(&kvec, &bloom_item, bloom_item.size, &items_size);
+
+	slice = kdbus_pool_slice_alloc(conn->pool, items_size, false);
+	if (IS_ERR(slice)) {
+		ret = PTR_ERR(slice);
+		slice = NULL;
+		goto exit_unref;
+	}
+
+	ret = kdbus_pool_slice_copy_kvec(slice, 0, &kvec, 1, items_size);
+	if (ret < 0)
+		goto exit_unref;
+
+	kdbus_pool_slice_publish(slice, &hello->offset, &hello->items_size);
+	kdbus_pool_slice_release(slice);
+
+	return conn;
+
+exit_unref:
+	kdbus_pool_slice_release(slice);
+	kdbus_conn_unref(conn);
+	return ERR_PTR(ret);
+}
+
+static void __kdbus_conn_free(struct kref *kref)
+{
+	struct kdbus_conn *conn = container_of(kref, struct kdbus_conn, kref);
+
+	WARN_ON(kdbus_conn_active(conn));
+	WARN_ON(delayed_work_pending(&conn->work));
+	WARN_ON(!list_empty(&conn->queue.msg_list));
+	WARN_ON(!list_empty(&conn->names_list));
+	WARN_ON(!list_empty(&conn->names_queue_list));
+	WARN_ON(!list_empty(&conn->reply_list));
+
+	if (conn->user) {
+		atomic_dec(&conn->user->connections);
+		kdbus_user_unref(conn->user);
+	}
+
+	kdbus_meta_proc_unref(conn->meta);
+	kdbus_match_db_free(conn->match_db);
+	kdbus_pool_free(conn->pool);
+	kdbus_ep_unref(conn->ep);
+	put_cred(conn->cred);
+	kfree(conn->description);
+	kfree(conn->quota);
+	kfree(conn);
+}
+
+/**
+ * kdbus_conn_ref() - take a connection reference
+ * @conn:		Connection, may be %NULL
+ *
+ * Return: the connection itself
+ */
+struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn)
+{
+	if (conn)
+		kref_get(&conn->kref);
+	return conn;
+}
+
+/**
+ * kdbus_conn_unref() - drop a connection reference
+ * @conn:		Connection (may be NULL)
+ *
+ * When the last reference is dropped, the connection's internal structure
+ * is freed.
+ *
+ * Return: NULL
+ */
+struct kdbus_conn *kdbus_conn_unref(struct kdbus_conn *conn)
+{
+	if (conn)
+		kref_put(&conn->kref, __kdbus_conn_free);
+	return NULL;
+}
+
+/**
+ * kdbus_conn_active() - connection is not disconnected
+ * @conn:		Connection to check
+ *
+ * Return true if the connection was not disconnected, yet. Note that a
+ * connection might be disconnected asynchronously, unless you hold the
+ * connection lock. If that's not suitable for you, see kdbus_conn_acquire() to
+ * suppress connection shutdown for a short period.
+ *
+ * Return: true if the connection is still active
+ */
+bool kdbus_conn_active(const struct kdbus_conn *conn)
+{
+	return atomic_read(&conn->active) >= 0;
+}
+
+/**
+ * kdbus_conn_acquire() - acquire an active connection reference
+ * @conn:		Connection
+ *
+ * Users can close a connection via KDBUS_BYEBYE (or by destroying the
+ * endpoint/bus/...) at any time. Whenever this happens, we should deny any
+ * user-visible action on this connection and signal ECONNRESET instead.
+ * To avoid testing for connection availability everytime you take the
+ * connection-lock, you can acquire a connection for short periods.
+ *
+ * By calling kdbus_conn_acquire(), you gain an "active reference" to the
+ * connection. You must also hold a regular reference at any time! As long as
+ * you hold the active-ref, the connection will not be shut down. However, if
+ * the connection was shut down, you can never acquire an active-ref again.
+ *
+ * kdbus_conn_disconnect() disables the connection and then waits for all active
+ * references to be dropped. It will also wake up any pending operation.
+ * However, you must not sleep for an indefinite period while holding an
+ * active-reference. Otherwise, kdbus_conn_disconnect() might stall. If you need
+ * to sleep for an indefinite period, either release the reference and try to
+ * acquire it again after waking up, or make kdbus_conn_disconnect() wake up
+ * your wait-queue.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_conn_acquire(struct kdbus_conn *conn)
+{
+	if (!atomic_inc_unless_negative(&conn->active))
+		return -ECONNRESET;
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+	rwsem_acquire_read(&conn->dep_map, 0, 1, _RET_IP_);
+#endif
+
+	return 0;
+}
+
+/**
+ * kdbus_conn_release() - release an active connection reference
+ * @conn:		Connection
+ *
+ * This releases an active reference that has been acquired via
+ * kdbus_conn_acquire(). If the connection was already disabled and this is the
+ * last active-ref that is dropped, the disconnect-waiter will be woken up and
+ * properly close the connection.
+ */
+void kdbus_conn_release(struct kdbus_conn *conn)
+{
+	int v;
+
+	if (!conn)
+		return;
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+	rwsem_release(&conn->dep_map, 1, _RET_IP_);
+#endif
+
+	v = atomic_dec_return(&conn->active);
+	if (v != KDBUS_CONN_ACTIVE_BIAS)
+		return;
+
+	wake_up_all(&conn->wait);
+}
+
+static int kdbus_conn_connect(struct kdbus_conn *conn, const char *name)
+{
+	struct kdbus_ep *ep = conn->ep;
+	struct kdbus_bus *bus = ep->bus;
+	int ret;
+
+	if (WARN_ON(atomic_read(&conn->active) != KDBUS_CONN_ACTIVE_NEW))
+		return -EALREADY;
+
+	/* make sure the ep-node is active while we add our connection */
+	if (!kdbus_node_acquire(&ep->node))
+		return -ESHUTDOWN;
+
+	/* lock order: domain -> bus -> ep -> names -> conn */
+	mutex_lock(&ep->lock);
+	down_write(&bus->conn_rwlock);
+
+	/* link into monitor list */
+	if (kdbus_conn_is_monitor(conn))
+		list_add_tail(&conn->monitor_entry, &bus->monitors_list);
+
+	/* link into bus and endpoint */
+	list_add_tail(&conn->ep_entry, &ep->conn_list);
+	hash_add(bus->conn_hash, &conn->hentry, conn->id);
+
+	/* enable lookups and acquire active ref */
+	atomic_set(&conn->active, 1);
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+	rwsem_acquire_read(&conn->dep_map, 0, 1, _RET_IP_);
+#endif
+
+	up_write(&bus->conn_rwlock);
+	mutex_unlock(&ep->lock);
+
+	kdbus_node_release(&ep->node);
+
+	/*
+	 * Notify subscribers about the new active connection, unless it is
+	 * a monitor. Monitors are invisible on the bus, can't be addressed
+	 * directly, and won't cause any notifications.
+	 */
+	if (!kdbus_conn_is_monitor(conn)) {
+		ret = kdbus_notify_id_change(conn->ep->bus, KDBUS_ITEM_ID_ADD,
+					     conn->id, conn->flags);
+		if (ret < 0)
+			goto exit_disconnect;
+	}
+
+	if (kdbus_conn_is_activator(conn)) {
+		u64 flags = KDBUS_NAME_ACTIVATOR;
+
+		if (WARN_ON(!name)) {
+			ret = -EINVAL;
+			goto exit_disconnect;
+		}
+
+		ret = kdbus_name_acquire(bus->name_registry, conn, name,
+					 flags, NULL);
+		if (ret < 0)
+			goto exit_disconnect;
+	}
+
+	kdbus_conn_release(conn);
+	kdbus_notify_flush(bus);
+	return 0;
+
+exit_disconnect:
+	kdbus_conn_release(conn);
+	kdbus_conn_disconnect(conn, false);
+	return ret;
+}
+
+/**
+ * kdbus_conn_disconnect() - disconnect a connection
+ * @conn:		The connection to disconnect
+ * @ensure_queue_empty:	Flag to indicate if the call should fail in
+ *			case the connection's message list is not
+ *			empty
+ *
+ * If @ensure_msg_list_empty is true, and the connection has pending messages,
+ * -EBUSY is returned.
+ *
+ * Return: 0 on success, negative errno on failure
+ */
+int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_queue_empty)
+{
+	struct kdbus_queue_entry *entry, *tmp;
+	struct kdbus_bus *bus = conn->ep->bus;
+	struct kdbus_reply *r, *r_tmp;
+	struct kdbus_conn *c;
+	int i, v;
+
+	mutex_lock(&conn->lock);
+	v = atomic_read(&conn->active);
+	if (v == KDBUS_CONN_ACTIVE_NEW) {
+		/* was never connected */
+		mutex_unlock(&conn->lock);
+		return 0;
+	}
+	if (v < 0) {
+		/* already dead */
+		mutex_unlock(&conn->lock);
+		return -ECONNRESET;
+	}
+	if (ensure_queue_empty && !list_empty(&conn->queue.msg_list)) {
+		/* still busy */
+		mutex_unlock(&conn->lock);
+		return -EBUSY;
+	}
+
+	atomic_add(KDBUS_CONN_ACTIVE_BIAS, &conn->active);
+	mutex_unlock(&conn->lock);
+
+	wake_up_interruptible(&conn->wait);
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+	rwsem_acquire(&conn->dep_map, 0, 0, _RET_IP_);
+	if (atomic_read(&conn->active) != KDBUS_CONN_ACTIVE_BIAS)
+		lock_contended(&conn->dep_map, _RET_IP_);
+#endif
+
+	wait_event(conn->wait,
+		   atomic_read(&conn->active) == KDBUS_CONN_ACTIVE_BIAS);
+
+#ifdef CONFIG_DEBUG_LOCK_ALLOC
+	lock_acquired(&conn->dep_map, _RET_IP_);
+	rwsem_release(&conn->dep_map, 1, _RET_IP_);
+#endif
+
+	cancel_delayed_work_sync(&conn->work);
+	kdbus_policy_remove_owner(&conn->ep->bus->policy_db, conn);
+
+	/* lock order: domain -> bus -> ep -> names -> conn */
+	mutex_lock(&conn->ep->lock);
+	down_write(&bus->conn_rwlock);
+
+	/* remove from bus and endpoint */
+	hash_del(&conn->hentry);
+	list_del(&conn->monitor_entry);
+	list_del(&conn->ep_entry);
+
+	up_write(&bus->conn_rwlock);
+	mutex_unlock(&conn->ep->lock);
+
+	/*
+	 * Remove all names associated with this connection; this possibly
+	 * moves queued messages back to the activator connection.
+	 */
+	kdbus_name_release_all(bus->name_registry, conn);
+
+	/* if we die while other connections wait for our reply, notify them */
+	mutex_lock(&conn->lock);
+	list_for_each_entry_safe(entry, tmp, &conn->queue.msg_list, entry) {
+		if (entry->reply)
+			kdbus_notify_reply_dead(bus,
+						entry->reply->reply_dst->id,
+						entry->reply->cookie);
+		kdbus_queue_entry_free(entry);
+	}
+
+	list_for_each_entry_safe(r, r_tmp, &conn->reply_list, entry)
+		kdbus_reply_unlink(r);
+	mutex_unlock(&conn->lock);
+
+	/* lock order: domain -> bus -> ep -> names -> conn */
+	down_read(&bus->conn_rwlock);
+	hash_for_each(bus->conn_hash, i, c, hentry) {
+		mutex_lock(&c->lock);
+		list_for_each_entry_safe(r, r_tmp, &c->reply_list, entry) {
+			if (r->reply_src == conn) {
+				if (r->sync) {
+					kdbus_sync_reply_wakeup(r, -EPIPE);
+					kdbus_reply_unlink(r);
+					continue;
+				}
+
+				/* send a 'connection dead' notification */
+				kdbus_notify_reply_dead(bus, c->id, r->cookie);
+				kdbus_reply_unlink(r);
+			}
+		}
+		mutex_unlock(&c->lock);
+	}
+	up_read(&bus->conn_rwlock);
+
+	if (!kdbus_conn_is_monitor(conn))
+		kdbus_notify_id_change(bus, KDBUS_ITEM_ID_REMOVE,
+				       conn->id, conn->flags);
+
+	kdbus_notify_flush(bus);
+
+	return 0;
+}
+
+/**
+ * kdbus_conn_has_name() - check if a connection owns a name
+ * @conn:		Connection
+ * @name:		Well-know name to check for
+ *
+ * The caller must hold the registry lock of conn->ep->bus.
+ *
+ * Return: true if the name is currently owned by the connection
+ */
+bool kdbus_conn_has_name(struct kdbus_conn *conn, const char *name)
+{
+	struct kdbus_name_entry *e;
+
+	lockdep_assert_held(&conn->ep->bus->name_registry->rwlock);
+
+	list_for_each_entry(e, &conn->names_list, conn_entry)
+		if (strcmp(e->name, name) == 0)
+			return true;
+
+	return false;
+}
+
+struct kdbus_quota {
+	uint32_t memory;
+	uint16_t msgs;
+	uint8_t fds;
+};
+
+/**
+ * kdbus_conn_quota_inc() - increase quota accounting
+ * @c:		connection owning the quota tracking
+ * @u:		user to account for (or NULL for kernel accounting)
+ * @memory:	size of memory to account for
+ * @fds:	number of FDs to account for
+ *
+ * This call manages the quotas on resource @c. That is, it's used if other
+ * users want to use the resources of connection @c, which so far only concerns
+ * the receive queue of the destination.
+ *
+ * This increases the quota-accounting for user @u by @memory bytes and @fds
+ * file descriptors. If the user has already reached the quota limits, this call
+ * will not do any accounting but return a negative error code indicating the
+ * failure.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_conn_quota_inc(struct kdbus_conn *c, struct kdbus_user *u,
+			 size_t memory, size_t fds)
+{
+	struct kdbus_quota *quota;
+	size_t available, accounted;
+	unsigned int id;
+
+	/*
+	 * Pool Layout:
+	 * 50% of a pool is always owned by the connection. It is reserved for
+	 * kernel queries, handling received messages and other tasks that are
+	 * under control of the pool owner. The other 50% of the pool are used
+	 * as incoming queue.
+	 * As we optionally support user-space based policies, we need fair
+	 * allocation schemes. Furthermore, resource utilization should be
+	 * maximized, so only minimal resources stay reserved. However, we need
+	 * to adapt to a dynamic number of users, as we cannot know how many
+	 * users will talk to a connection. Therefore, the current allocations
+	 * works like this:
+	 * We limit the number of bytes in a destination's pool per sending
+	 * user. The space available for a user is 33% of the unused pool space
+	 * (whereas the space used by the user itself is also treated as
+	 * 'unused'). This way, we favor users coming first, but keep enough
+	 * pool space available for any following users. Given that messages are
+	 * dequeued in FIFO order, this should balance nicely if the number of
+	 * users grows. At the same time, this algorithm guarantees that the
+	 * space available to a connection is reduced dynamically, the more
+	 * concurrent users talk to a connection.
+	 */
+
+	/* per user-accounting is expensive, so we keep state small */
+	BUILD_BUG_ON(sizeof(quota->memory) != 4);
+	BUILD_BUG_ON(sizeof(quota->msgs) != 2);
+	BUILD_BUG_ON(sizeof(quota->fds) != 1);
+	BUILD_BUG_ON(KDBUS_CONN_MAX_MSGS > U16_MAX);
+	BUILD_BUG_ON(KDBUS_CONN_MAX_FDS_PER_USER > U8_MAX);
+
+	id = u ? u->id : KDBUS_USER_KERNEL_ID;
+	if (id >= c->n_quota) {
+		unsigned int users;
+
+		users = max(KDBUS_ALIGN8(id) + 8, id);
+		quota = krealloc(c->quota, users * sizeof(*quota),
+				 GFP_KERNEL | __GFP_ZERO);
+		if (!quota)
+			return -ENOMEM;
+
+		c->n_quota = users;
+		c->quota = quota;
+	}
+
+	quota = &c->quota[id];
+	kdbus_pool_accounted(c->pool, &available, &accounted);
+
+	/* half the pool is _always_ reserved for the pool owner */
+	available /= 2;
+
+	/*
+	 * Pool owner slices are un-accounted slices; they can claim more
+	 * than 50% of the queue. However, the slice we're dealing with here
+	 * belong to the incoming queue, hence they are 'accounted' slices
+	 * to which the 50%-limit applies.
+	 */
+	if (available < accounted)
+		return -ENOBUFS;
+
+	/* 1/3 of the remaining space (including your own memory) */
+	available = (available - accounted + quota->memory) / 3;
+
+	if (available < quota->memory ||
+	    available - quota->memory < memory ||
+	    quota->memory + memory > U32_MAX)
+		return -ENOBUFS;
+	if (quota->msgs >= KDBUS_CONN_MAX_MSGS)
+		return -ENOBUFS;
+	if (quota->fds + fds < quota->fds ||
+	    quota->fds + fds > KDBUS_CONN_MAX_FDS_PER_USER)
+		return -EMFILE;
+
+	quota->memory += memory;
+	quota->fds += fds;
+	++quota->msgs;
+	return 0;
+}
+
+/**
+ * kdbus_conn_quota_dec() - decrease quota accounting
+ * @c:		connection owning the quota tracking
+ * @u:		user which was accounted for (or NULL for kernel accounting)
+ * @memory:	size of memory which was accounted for
+ * @fds:	number of FDs which were accounted for
+ *
+ * This does the reverse of kdbus_conn_quota_inc(). You have to release any
+ * accounted resources that you called kdbus_conn_quota_inc() for. However, you
+ * must not call kdbus_conn_quota_dec() if the accounting failed (that is,
+ * kdbus_conn_quota_inc() failed).
+ */
+void kdbus_conn_quota_dec(struct kdbus_conn *c, struct kdbus_user *u,
+			  size_t memory, size_t fds)
+{
+	struct kdbus_quota *quota;
+	unsigned int id;
+
+	id = u ? u->id : KDBUS_USER_KERNEL_ID;
+	if (WARN_ON(id >= c->n_quota))
+		return;
+
+	quota = &c->quota[id];
+
+	if (!WARN_ON(quota->msgs == 0))
+		--quota->msgs;
+	if (!WARN_ON(quota->memory < memory))
+		quota->memory -= memory;
+	if (!WARN_ON(quota->fds < fds))
+		quota->fds -= fds;
+}
+
+/**
+ * kdbus_conn_lost_message() - handle lost messages
+ * @c:		connection that lost a message
+ *
+ * kdbus is reliable. That means, we try hard to never lose messages. However,
+ * memory is limited, so we cannot rely on transmissions to never fail.
+ * Therefore, we use quota-limits to let callers know if there unicast message
+ * cannot be transmitted to a peer. This works fine for unicasts, but for
+ * broadcasts we cannot make the caller handle the transmission failure.
+ * Instead, we must let the destination know that it couldn't receive a
+ * broadcast.
+ * As this is an unlikely scenario, we keep it simple. A single lost-counter
+ * remembers the number of lost messages since the last call to RECV. The next
+ * message retrieval will notify the connection that it lost messages since the
+ * last message retrieval and thus should resync its state.
+ */
+void kdbus_conn_lost_message(struct kdbus_conn *c)
+{
+	if (atomic_inc_return(&c->lost_count) == 1)
+		wake_up_interruptible(&c->wait);
+}
+
+/* Callers should take the conn_dst lock */
+static struct kdbus_queue_entry *
+kdbus_conn_entry_make(struct kdbus_conn *conn_dst,
+		      const struct kdbus_kmsg *kmsg,
+		      struct kdbus_user *user)
+{
+	struct kdbus_queue_entry *entry;
+
+	/* The remote connection was disconnected */
+	if (!kdbus_conn_active(conn_dst))
+		return ERR_PTR(-ECONNRESET);
+
+	/*
+	 * If the connection does not accept file descriptors but the message
+	 * has some attached, refuse it.
+	 *
+	 * If this is a monitor connection, accept the message. In that
+	 * case, all file descriptors will be set to -1 at receive time.
+	 */
+	if (!kdbus_conn_is_monitor(conn_dst) &&
+	    !(conn_dst->flags & KDBUS_HELLO_ACCEPT_FD) &&
+	    kmsg->res && kmsg->res->fds_count > 0)
+		return ERR_PTR(-ECOMM);
+
+	entry = kdbus_queue_entry_new(conn_dst, kmsg, user);
+	if (IS_ERR(entry))
+		return entry;
+
+	return entry;
+}
+
+/*
+ * Synchronously responding to a message, allocate a queue entry
+ * and attach it to the reply tracking object.
+ * The connection's queue will never get to see it.
+ */
+static int kdbus_conn_entry_sync_attach(struct kdbus_conn *conn_dst,
+					const struct kdbus_kmsg *kmsg,
+					struct kdbus_reply *reply_wake)
+{
+	struct kdbus_queue_entry *entry;
+	int remote_ret;
+	int ret = 0;
+
+	mutex_lock(&reply_wake->reply_dst->lock);
+
+	/*
+	 * If we are still waiting then proceed, allocate a queue
+	 * entry and attach it to the reply object
+	 */
+	if (reply_wake->waiting) {
+		entry = kdbus_conn_entry_make(conn_dst, kmsg,
+					      reply_wake->reply_src->user);
+		if (IS_ERR(entry))
+			ret = PTR_ERR(entry);
+		else
+			/* Attach the entry to the reply object */
+			reply_wake->queue_entry = entry;
+	} else {
+		ret = -ECONNRESET;
+	}
+
+	/*
+	 * Update the reply object and wake up remote peer only
+	 * on appropriate return codes
+	 *
+	 * * -ECOMM: if the replying connection failed with -ECOMM
+	 *           then wakeup remote peer with -EREMOTEIO
+	 *
+	 *           We do this to differenciate between -ECOMM errors
+	 *           from the original sender perspective:
+	 *           -ECOMM error during the sync send and
+	 *           -ECOMM error during the sync reply, this last
+	 *           one is rewritten to -EREMOTEIO
+	 *
+	 * * Wake up on all other return codes.
+	 */
+	remote_ret = ret;
+
+	if (ret == -ECOMM)
+		remote_ret = -EREMOTEIO;
+
+	kdbus_sync_reply_wakeup(reply_wake, remote_ret);
+	kdbus_reply_unlink(reply_wake);
+	mutex_unlock(&reply_wake->reply_dst->lock);
+
+	return ret;
+}
+
+/**
+ * kdbus_conn_entry_insert() - enqueue a message into the receiver's pool
+ * @conn_src:		The sending connection
+ * @conn_dst:		The connection to queue into
+ * @kmsg:		The kmsg to queue
+ * @reply:		The reply tracker to attach to the queue entry
+ *
+ * Return: 0 on success. negative error otherwise.
+ */
+int kdbus_conn_entry_insert(struct kdbus_conn *conn_src,
+			    struct kdbus_conn *conn_dst,
+			    const struct kdbus_kmsg *kmsg,
+			    struct kdbus_reply *reply)
+{
+	struct kdbus_queue_entry *entry;
+	int ret;
+
+	kdbus_conn_lock2(conn_src, conn_dst);
+
+	entry = kdbus_conn_entry_make(conn_dst, kmsg,
+				      conn_src ? conn_src->user : NULL);
+	if (IS_ERR(entry)) {
+		ret = PTR_ERR(entry);
+		goto exit_unlock;
+	}
+
+	if (reply) {
+		kdbus_reply_link(reply);
+		if (!reply->sync)
+			schedule_delayed_work(&conn_src->work, 0);
+	}
+
+	kdbus_queue_entry_enqueue(entry, reply);
+	wake_up_interruptible(&conn_dst->wait);
+
+	ret = 0;
+
+exit_unlock:
+	kdbus_conn_unlock2(conn_src, conn_dst);
+	return ret;
+}
+
+static int kdbus_conn_wait_reply(struct kdbus_conn *conn_src,
+				 struct kdbus_cmd_send *cmd_send,
+				 struct file *ioctl_file,
+				 struct file *cancel_fd,
+				 struct kdbus_reply *reply_wait,
+				 ktime_t expire)
+{
+	struct kdbus_queue_entry *entry;
+	struct poll_wqueues pwq = {};
+	int ret;
+
+	if (WARN_ON(!reply_wait))
+		return -EIO;
+
+	/*
+	 * Block until the reply arrives. reply_wait is left untouched
+	 * by the timeout scans that might be conducted for other,
+	 * asynchronous replies of conn_src.
+	 */
+
+	poll_initwait(&pwq);
+	poll_wait(ioctl_file, &conn_src->wait, &pwq.pt);
+
+	for (;;) {
+		/*
+		 * Any of the following conditions will stop our synchronously
+		 * blocking SEND command:
+		 *
+		 * a) The origin sender closed its connection
+		 * b) The remote peer answered, setting reply_wait->waiting = 0
+		 * c) The cancel FD was written to
+		 * d) A signal was received
+		 * e) The specified timeout was reached, and none of the above
+		 *    conditions kicked in.
+		 */
+
+		/*
+		 * We have already acquired an active reference when
+		 * entering here, but another thread may call
+		 * KDBUS_CMD_BYEBYE which does not acquire an active
+		 * reference, therefore kdbus_conn_disconnect() will
+		 * not wait for us.
+		 */
+		if (!kdbus_conn_active(conn_src)) {
+			ret = -ECONNRESET;
+			break;
+		}
+
+		/*
+		 * After the replying peer unset the waiting variable
+		 * it will wake up us.
+		 */
+		if (!reply_wait->waiting) {
+			ret = reply_wait->err;
+			break;
+		}
+
+		if (cancel_fd) {
+			unsigned int r;
+
+			r = cancel_fd->f_op->poll(cancel_fd, &pwq.pt);
+			if (r & POLLIN) {
+				ret = -ECANCELED;
+				break;
+			}
+		}
+
+		if (signal_pending(current)) {
+			ret = -EINTR;
+			break;
+		}
+
+		if (!poll_schedule_timeout(&pwq, TASK_INTERRUPTIBLE,
+					   &expire, 0)) {
+			ret = -ETIMEDOUT;
+			break;
+		}
+
+		/*
+		 * Reset the poll worker func, so the waitqueues are not
+		 * added to the poll table again. We just reuse what we've
+		 * collected earlier for further iterations.
+		 */
+		init_poll_funcptr(&pwq.pt, NULL);
+	}
+
+	poll_freewait(&pwq);
+
+	if (ret == -EINTR) {
+		/*
+		 * Interrupted system call. Unref the reply object, and pass
+		 * the return value down the chain. Mark the reply as
+		 * interrupted, so the cleanup work can remove it, but do not
+		 * unlink it from the list. Once the syscall restarts, we'll
+		 * pick it up and wait on it again.
+		 */
+		mutex_lock(&conn_src->lock);
+		reply_wait->interrupted = true;
+		schedule_delayed_work(&conn_src->work, 0);
+		mutex_unlock(&conn_src->lock);
+
+		return -ERESTARTSYS;
+	}
+
+	mutex_lock(&conn_src->lock);
+	reply_wait->waiting = false;
+	entry = reply_wait->queue_entry;
+	if (entry) {
+		ret = kdbus_queue_entry_install(entry,
+						&cmd_send->reply.return_flags,
+						true);
+		kdbus_pool_slice_publish(entry->slice, &cmd_send->reply.offset,
+					 &cmd_send->reply.msg_size);
+		kdbus_queue_entry_free(entry);
+	}
+	kdbus_reply_unlink(reply_wait);
+	mutex_unlock(&conn_src->lock);
+
+	return ret;
+}
+
+static int kdbus_pin_dst(struct kdbus_bus *bus,
+			 struct kdbus_kmsg *kmsg,
+			 struct kdbus_name_entry **out_name,
+			 struct kdbus_conn **out_dst)
+{
+	struct kdbus_msg_resources *res = kmsg->res;
+	struct kdbus_name_entry *name = NULL;
+	struct kdbus_conn *dst = NULL;
+	struct kdbus_msg *msg = &kmsg->msg;
+	int ret;
+
+	if (WARN_ON(!res))
+		return -EINVAL;
+
+	lockdep_assert_held(&bus->name_registry->rwlock);
+
+	if (!res->dst_name) {
+		dst = kdbus_bus_find_conn_by_id(bus, msg->dst_id);
+		if (!dst)
+			return -ENXIO;
+
+		if (!kdbus_conn_is_ordinary(dst)) {
+			ret = -ENXIO;
+			goto error;
+		}
+	} else {
+		name = kdbus_name_lookup_unlocked(bus->name_registry,
+						  res->dst_name);
+		if (!name)
+			return -ESRCH;
+
+		/*
+		 * If both a name and a connection ID are given as destination
+		 * of a message, check that the currently owning connection of
+		 * the name matches the specified ID.
+		 * This way, we allow userspace to send the message to a
+		 * specific connection by ID only if the connection currently
+		 * owns the given name.
+		 */
+		if (msg->dst_id != KDBUS_DST_ID_NAME &&
+		    msg->dst_id != name->conn->id)
+			return -EREMCHG;
+
+		if (!name->conn && name->activator)
+			dst = kdbus_conn_ref(name->activator);
+		else
+			dst = kdbus_conn_ref(name->conn);
+
+		if ((msg->flags & KDBUS_MSG_NO_AUTO_START) &&
+		    kdbus_conn_is_activator(dst)) {
+			ret = -EADDRNOTAVAIL;
+			goto error;
+		}
+
+		/*
+		 * Record the sequence number of the registered name; it will
+		 * be passed on to the queue, in case messages addressed to a
+		 * name need to be moved from or to an activator.
+		 */
+		kmsg->dst_name_id = name->name_id;
+	}
+
+	*out_name = name;
+	*out_dst = dst;
+	return 0;
+
+error:
+	kdbus_conn_unref(dst);
+	return ret;
+}
+
+static int kdbus_conn_reply(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
+{
+	struct kdbus_name_entry *name = NULL;
+	struct kdbus_reply *reply, *wake = NULL;
+	struct kdbus_conn *dst = NULL;
+	struct kdbus_bus *bus = src->ep->bus;
+	u64 attach;
+	int ret;
+
+	if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
+	    WARN_ON(kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) ||
+	    WARN_ON(kmsg->msg.flags & KDBUS_MSG_SIGNAL))
+		return -EINVAL;
+
+	/* name-registry must be locked for lookup *and* collecting data */
+	down_read(&bus->name_registry->rwlock);
+
+	/* find and pin destination */
+
+	ret = kdbus_pin_dst(bus, kmsg, &name, &dst);
+	if (ret < 0)
+		goto exit;
+
+	mutex_lock(&dst->lock);
+	reply = kdbus_reply_find(src, dst, kmsg->msg.cookie_reply);
+	if (reply) {
+		if (reply->sync)
+			wake = kdbus_reply_ref(reply);
+		kdbus_reply_unlink(reply);
+	}
+	mutex_unlock(&dst->lock);
+
+	if (!reply) {
+		ret = -EPERM;
+		goto exit;
+	}
+
+	/* attach metadata */
+
+	attach = kdbus_meta_calc_attach_flags(src, dst);
+
+	if (!src->faked_meta) {
+		ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
+		if (ret < 0)
+			goto exit;
+	}
+
+	ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach);
+	if (ret < 0)
+		goto exit;
+
+	/* send message */
+
+	kdbus_bus_eavesdrop(bus, src, kmsg);
+
+	if (wake)
+		ret = kdbus_conn_entry_sync_attach(dst, kmsg, wake);
+	else
+		ret = kdbus_conn_entry_insert(src, dst, kmsg, NULL);
+
+exit:
+	up_read(&bus->name_registry->rwlock);
+	kdbus_reply_unref(wake);
+	kdbus_conn_unref(dst);
+	return ret;
+}
+
+static struct kdbus_reply *kdbus_conn_call(struct kdbus_conn *src,
+					   struct kdbus_kmsg *kmsg,
+					   ktime_t exp)
+{
+	struct kdbus_name_entry *name = NULL;
+	struct kdbus_reply *wait = NULL;
+	struct kdbus_conn *dst = NULL;
+	struct kdbus_bus *bus = src->ep->bus;
+	u64 attach;
+	int ret;
+
+	if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
+	    WARN_ON(kmsg->msg.flags & KDBUS_MSG_SIGNAL) ||
+	    WARN_ON(!(kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY)))
+		return ERR_PTR(-EINVAL);
+
+	/* resume previous wait-context, if available */
+
+	mutex_lock(&src->lock);
+	wait = kdbus_reply_find(NULL, src, kmsg->msg.cookie);
+	if (wait) {
+		if (wait->interrupted) {
+			kdbus_reply_ref(wait);
+			wait->interrupted = false;
+		} else {
+			wait = NULL;
+		}
+	}
+	mutex_unlock(&src->lock);
+
+	if (wait)
+		return wait;
+
+	if (ktime_compare(ktime_get(), exp) >= 0)
+		return ERR_PTR(-ETIMEDOUT);
+
+	/* name-registry must be locked for lookup *and* collecting data */
+	down_read(&bus->name_registry->rwlock);
+
+	/* find and pin destination */
+
+	ret = kdbus_pin_dst(bus, kmsg, &name, &dst);
+	if (ret < 0)
+		goto exit;
+
+	if (!kdbus_conn_policy_talk(src, current_cred(), dst)) {
+		ret = -EPERM;
+		goto exit;
+	}
+
+	wait = kdbus_reply_new(dst, src, &kmsg->msg, name, true);
+	if (IS_ERR(wait)) {
+		ret = PTR_ERR(wait);
+		wait = NULL;
+		goto exit;
+	}
+
+	/* attach metadata */
+
+	attach = kdbus_meta_calc_attach_flags(src, dst);
+
+	if (!src->faked_meta) {
+		ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
+		if (ret < 0)
+			goto exit;
+	}
+
+	ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach);
+	if (ret < 0)
+		goto exit;
+
+	/* send message */
+
+	kdbus_bus_eavesdrop(bus, src, kmsg);
+
+	ret = kdbus_conn_entry_insert(src, dst, kmsg, wait);
+	if (ret < 0)
+		goto exit;
+
+	ret = 0;
+
+exit:
+	up_read(&bus->name_registry->rwlock);
+	if (ret < 0) {
+		kdbus_reply_unref(wait);
+		wait = ERR_PTR(ret);
+	}
+	kdbus_conn_unref(dst);
+	return wait;
+}
+
+static int kdbus_conn_unicast(struct kdbus_conn *src, struct kdbus_kmsg *kmsg)
+{
+	struct kdbus_name_entry *name = NULL;
+	struct kdbus_reply *wait = NULL;
+	struct kdbus_conn *dst = NULL;
+	struct kdbus_bus *bus = src->ep->bus;
+	bool is_signal = (kmsg->msg.flags & KDBUS_MSG_SIGNAL);
+	u64 attach;
+	int ret = 0;
+
+	if (WARN_ON(kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) ||
+	    WARN_ON(!(kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) &&
+		    kmsg->msg.cookie_reply != 0))
+		return -EINVAL;
+
+	/* name-registry must be locked for lookup *and* collecting data */
+	down_read(&bus->name_registry->rwlock);
+
+	/* find and pin destination */
+
+	ret = kdbus_pin_dst(bus, kmsg, &name, &dst);
+	if (ret < 0)
+		goto exit;
+
+	if (is_signal) {
+		/* like broadcasts we eavesdrop even if the msg is dropped */
+		kdbus_bus_eavesdrop(bus, src, kmsg);
+
+		/* drop silently if peer is not interested or not privileged */
+		if (!kdbus_match_db_match_kmsg(dst->match_db, src, kmsg) ||
+		    !kdbus_conn_policy_talk(dst, NULL, src))
+			goto exit;
+	} else if (!kdbus_conn_policy_talk(src, current_cred(), dst)) {
+		ret = -EPERM;
+		goto exit;
+	} else if (kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) {
+		wait = kdbus_reply_new(dst, src, &kmsg->msg, name, false);
+		if (IS_ERR(wait)) {
+			ret = PTR_ERR(wait);
+			wait = NULL;
+			goto exit;
+		}
+	}
+
+	/* attach metadata */
+
+	attach = kdbus_meta_calc_attach_flags(src, dst);
+
+	if (!src->faked_meta) {
+		ret = kdbus_meta_proc_collect(kmsg->proc_meta, attach);
+		if (ret < 0 && !is_signal)
+			goto exit;
+	}
+
+	ret = kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, src, attach);
+	if (ret < 0 && !is_signal)
+		goto exit;
+
+	/* send message */
+
+	if (!is_signal)
+		kdbus_bus_eavesdrop(bus, src, kmsg);
+
+	ret = kdbus_conn_entry_insert(src, dst, kmsg, wait);
+	if (ret < 0 && !is_signal)
+		goto exit;
+
+	/* signals are treated like broadcasts, recv-errors are ignored */
+	ret = 0;
+
+exit:
+	up_read(&bus->name_registry->rwlock);
+	kdbus_reply_unref(wait);
+	kdbus_conn_unref(dst);
+	return ret;
+}
+
+/**
+ * kdbus_conn_move_messages() - move messages from one connection to another
+ * @conn_dst:		Connection to copy to
+ * @conn_src:		Connection to copy from
+ * @name_id:		Filter for the sequence number of the registered
+ *			name, 0 means no filtering.
+ *
+ * Move all messages from one connection to another. This is used when
+ * an implementer connection is taking over/giving back a well-known name
+ * from/to an activator connection.
+ */
+void kdbus_conn_move_messages(struct kdbus_conn *conn_dst,
+			      struct kdbus_conn *conn_src,
+			      u64 name_id)
+{
+	struct kdbus_queue_entry *e, *e_tmp;
+	struct kdbus_reply *r, *r_tmp;
+	struct kdbus_bus *bus;
+	struct kdbus_conn *c;
+	LIST_HEAD(msg_list);
+	int i, ret = 0;
+
+	if (WARN_ON(conn_src == conn_dst))
+		return;
+
+	bus = conn_src->ep->bus;
+
+	/* lock order: domain -> bus -> ep -> names -> conn */
+	down_read(&bus->conn_rwlock);
+	hash_for_each(bus->conn_hash, i, c, hentry) {
+		if (c == conn_src || c == conn_dst)
+			continue;
+
+		mutex_lock(&c->lock);
+		list_for_each_entry_safe(r, r_tmp, &c->reply_list, entry) {
+			if (r->reply_src != conn_src)
+				continue;
+
+			/* filter messages for a specific name */
+			if (name_id > 0 && r->name_id != name_id)
+				continue;
+
+			kdbus_conn_unref(r->reply_src);
+			r->reply_src = kdbus_conn_ref(conn_dst);
+		}
+		mutex_unlock(&c->lock);
+	}
+	up_read(&bus->conn_rwlock);
+
+	kdbus_conn_lock2(conn_src, conn_dst);
+	list_for_each_entry_safe(e, e_tmp, &conn_src->queue.msg_list, entry) {
+		/* filter messages for a specific name */
+		if (name_id > 0 && e->dst_name_id != name_id)
+			continue;
+
+		if (!(conn_dst->flags & KDBUS_HELLO_ACCEPT_FD) &&
+		    e->msg_res && e->msg_res->fds_count > 0) {
+			kdbus_conn_lost_message(conn_dst);
+			kdbus_queue_entry_free(e);
+			continue;
+		}
+
+		ret = kdbus_queue_entry_move(e, conn_dst);
+		if (ret < 0) {
+			kdbus_conn_lost_message(conn_dst);
+			kdbus_queue_entry_free(e);
+			continue;
+		}
+	}
+	kdbus_conn_unlock2(conn_src, conn_dst);
+
+	/* wake up poll() */
+	wake_up_interruptible(&conn_dst->wait);
+}
+
+/* query the policy-database for all names of @whom */
+static bool kdbus_conn_policy_query_all(struct kdbus_conn *conn,
+					const struct cred *conn_creds,
+					struct kdbus_policy_db *db,
+					struct kdbus_conn *whom,
+					unsigned int access)
+{
+	struct kdbus_name_entry *ne;
+	bool pass = false;
+	int res;
+
+	lockdep_assert_held(&conn->ep->bus->name_registry->rwlock);
+
+	down_read(&db->entries_rwlock);
+	mutex_lock(&whom->lock);
+
+	list_for_each_entry(ne, &whom->names_list, conn_entry) {
+		res = kdbus_policy_query_unlocked(db, conn_creds ? : conn->cred,
+						  ne->name,
+						  kdbus_strhash(ne->name));
+		if (res >= (int)access) {
+			pass = true;
+			break;
+		}
+	}
+
+	mutex_unlock(&whom->lock);
+	up_read(&db->entries_rwlock);
+
+	return pass;
+}
+
+/**
+ * kdbus_conn_policy_own_name() - verify a connection can own the given name
+ * @conn:		Connection
+ * @conn_creds:		Credentials of @conn to use for policy check
+ * @name:		Name
+ *
+ * This verifies that @conn is allowed to acquire the well-known name @name.
+ *
+ * Return: true if allowed, false if not.
+ */
+bool kdbus_conn_policy_own_name(struct kdbus_conn *conn,
+				const struct cred *conn_creds,
+				const char *name)
+{
+	unsigned int hash = kdbus_strhash(name);
+	int res;
+
+	if (!conn_creds)
+		conn_creds = conn->cred;
+
+	if (conn->ep->user) {
+		res = kdbus_policy_query(&conn->ep->policy_db, conn_creds,
+					 name, hash);
+		if (res < KDBUS_POLICY_OWN)
+			return false;
+	}
+
+	if (conn->privileged)
+		return true;
+
+	res = kdbus_policy_query(&conn->ep->bus->policy_db, conn_creds,
+				 name, hash);
+	return res >= KDBUS_POLICY_OWN;
+}
+
+/**
+ * kdbus_conn_policy_talk() - verify a connection can talk to a given peer
+ * @conn:		Connection that tries to talk
+ * @conn_creds:		Credentials of @conn to use for policy check
+ * @to:			Connection that is talked to
+ *
+ * This verifies that @conn is allowed to talk to @to.
+ *
+ * Return: true if allowed, false if not.
+ */
+bool kdbus_conn_policy_talk(struct kdbus_conn *conn,
+			    const struct cred *conn_creds,
+			    struct kdbus_conn *to)
+{
+	if (!conn_creds)
+		conn_creds = conn->cred;
+
+	if (conn->ep->user &&
+	    !kdbus_conn_policy_query_all(conn, conn_creds, &conn->ep->policy_db,
+					 to, KDBUS_POLICY_TALK))
+		return false;
+
+	if (conn->privileged)
+		return true;
+	if (uid_eq(conn_creds->euid, to->cred->uid))
+		return true;
+
+	return kdbus_conn_policy_query_all(conn, conn_creds,
+					   &conn->ep->bus->policy_db, to,
+					   KDBUS_POLICY_TALK);
+}
+
+/**
+ * kdbus_conn_policy_see_name_unlocked() - verify a connection can see a given
+ *					   name
+ * @conn:		Connection
+ * @conn_creds:		Credentials of @conn to use for policy check
+ * @name:		Name
+ *
+ * This verifies that @conn is allowed to see the well-known name @name. Caller
+ * must hold policy-lock.
+ *
+ * Return: true if allowed, false if not.
+ */
+bool kdbus_conn_policy_see_name_unlocked(struct kdbus_conn *conn,
+					 const struct cred *conn_creds,
+					 const char *name)
+{
+	int res;
+
+	/*
+	 * By default, all names are visible on a bus. SEE policies can only be
+	 * installed on custom endpoints, where by default no name is visible.
+	 */
+	if (!conn->ep->user)
+		return true;
+
+	res = kdbus_policy_query_unlocked(&conn->ep->policy_db,
+					  conn_creds ? : conn->cred,
+					  name, kdbus_strhash(name));
+	return res >= KDBUS_POLICY_SEE;
+}
+
+static bool kdbus_conn_policy_see_name(struct kdbus_conn *conn,
+				       const struct cred *conn_creds,
+				       const char *name)
+{
+	bool res;
+
+	down_read(&conn->ep->policy_db.entries_rwlock);
+	res = kdbus_conn_policy_see_name_unlocked(conn, conn_creds, name);
+	up_read(&conn->ep->policy_db.entries_rwlock);
+
+	return res;
+}
+
+static bool kdbus_conn_policy_see(struct kdbus_conn *conn,
+				  const struct cred *conn_creds,
+				  struct kdbus_conn *whom)
+{
+	/*
+	 * By default, all names are visible on a bus, so a connection can
+	 * always see other connections. SEE policies can only be installed on
+	 * custom endpoints, where by default no name is visible and we hide
+	 * peers from each other, unless you see at least _one_ name of the
+	 * peer.
+	 */
+	return !conn->ep->user ||
+	       kdbus_conn_policy_query_all(conn, conn_creds,
+					   &conn->ep->policy_db, whom,
+					   KDBUS_POLICY_SEE);
+}
+
+/**
+ * kdbus_conn_policy_see_notification() - verify a connection is allowed to
+ *					  receive a given kernel notification
+ * @conn:		Connection
+ * @conn_creds:		Credentials of @conn to use for policy check
+ * @kmsg:		The message carrying the notification
+ *
+ * This checks whether @conn is allowed to see the kernel notification @kmsg.
+ *
+ * Return: true if allowed, false if not.
+ */
+bool kdbus_conn_policy_see_notification(struct kdbus_conn *conn,
+					const struct cred *conn_creds,
+					const struct kdbus_kmsg *kmsg)
+{
+	if (WARN_ON(kmsg->msg.src_id != KDBUS_SRC_ID_KERNEL))
+		return false;
+
+	/*
+	 * Depending on the notification type, broadcasted kernel notifications
+	 * have to be filtered:
+	 *
+	 * KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}: This notification is forwarded
+	 *     to a peer if, and only if, that peer can see the name this
+	 *     notification is for.
+	 *
+	 * KDBUS_ITEM_ID_{ADD,REMOVE}: As new peers cannot have names, and all
+	 *     names are dropped before a peer is removed, those notifications
+	 *     cannot be seen on custom endpoints. Thus, we only pass them
+	 *     through on default endpoints.
+	 */
+
+	switch (kmsg->notify_type) {
+	case KDBUS_ITEM_NAME_ADD:
+	case KDBUS_ITEM_NAME_REMOVE:
+	case KDBUS_ITEM_NAME_CHANGE:
+		return kdbus_conn_policy_see_name(conn, conn_creds,
+						  kmsg->notify_name);
+
+	case KDBUS_ITEM_ID_ADD:
+	case KDBUS_ITEM_ID_REMOVE:
+		return !conn->ep->user;
+
+	default:
+		WARN(1, "Invalid type for notification broadcast: %llu\n",
+		     (unsigned long long)kmsg->notify_type);
+		return false;
+	}
+}
+
+/**
+ * kdbus_cmd_hello() - handle KDBUS_CMD_HELLO
+ * @ep:			Endpoint to operate on
+ * @privileged:		Whether the caller is privileged
+ * @argp:		Command payload
+ *
+ * Return: Newly created connection on success, ERR_PTR on failure.
+ */
+struct kdbus_conn *kdbus_cmd_hello(struct kdbus_ep *ep, bool privileged,
+				   void __user *argp)
+{
+	struct kdbus_cmd_hello *cmd;
+	struct kdbus_conn *c = NULL;
+	const char *item_name;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+		{ .type = KDBUS_ITEM_NAME },
+		{ .type = KDBUS_ITEM_CREDS },
+		{ .type = KDBUS_ITEM_PIDS },
+		{ .type = KDBUS_ITEM_SECLABEL },
+		{ .type = KDBUS_ITEM_CONN_DESCRIPTION },
+		{ .type = KDBUS_ITEM_POLICY_ACCESS, .multiple = true },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE |
+				 KDBUS_HELLO_ACCEPT_FD |
+				 KDBUS_HELLO_ACTIVATOR |
+				 KDBUS_HELLO_POLICY_HOLDER |
+				 KDBUS_HELLO_MONITOR,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	if (ret > 0)
+		return NULL;
+
+	item_name = argv[1].item ? argv[1].item->str : NULL;
+
+	c = kdbus_conn_new(ep, privileged, cmd, item_name,
+			   argv[2].item ? &argv[2].item->creds : NULL,
+			   argv[3].item ? &argv[3].item->pids : NULL,
+			   argv[4].item ? argv[4].item->str : NULL,
+			   argv[5].item ? argv[5].item->str : NULL);
+	if (IS_ERR(c)) {
+		ret = PTR_ERR(c);
+		c = NULL;
+		goto exit;
+	}
+
+	ret = kdbus_conn_connect(c, item_name);
+	if (ret < 0)
+		goto exit;
+
+	if (kdbus_conn_is_activator(c) || kdbus_conn_is_policy_holder(c)) {
+		ret = kdbus_conn_acquire(c);
+		if (ret < 0)
+			goto exit;
+
+		ret = kdbus_policy_set(&c->ep->bus->policy_db, args.items,
+				       args.items_size, 1,
+				       kdbus_conn_is_policy_holder(c), c);
+		kdbus_conn_release(c);
+		if (ret < 0)
+			goto exit;
+	}
+
+	if (copy_to_user(argp, cmd, sizeof(*cmd)))
+		ret = -EFAULT;
+
+exit:
+	ret = kdbus_args_clear(&args, ret);
+	if (ret < 0) {
+		if (c) {
+			kdbus_conn_disconnect(c, false);
+			kdbus_conn_unref(c);
+		}
+		return ERR_PTR(ret);
+	}
+	return c;
+}
+
+/**
+ * kdbus_cmd_byebye_unlocked() - handle KDBUS_CMD_BYEBYE
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * The caller must not hold any active reference to @conn or this will deadlock.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_byebye_unlocked(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_cmd *cmd;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	if (!kdbus_conn_is_ordinary(conn))
+		return -EOPNOTSUPP;
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	ret = kdbus_conn_disconnect(conn, true);
+	return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_conn_info() - handle KDBUS_CMD_CONN_INFO
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_conn_info(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_meta_conn *conn_meta = NULL;
+	struct kdbus_pool_slice *slice = NULL;
+	struct kdbus_name_entry *entry = NULL;
+	struct kdbus_conn *owner_conn = NULL;
+	struct kdbus_info info = {};
+	struct kdbus_cmd_info *cmd;
+	struct kdbus_bus *bus = conn->ep->bus;
+	struct kvec kvec;
+	size_t meta_size;
+	const char *name;
+	u64 attach_flags;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+		{ .type = KDBUS_ITEM_NAME },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	/* registry must be held throughout lookup *and* collecting data */
+	down_read(&bus->name_registry->rwlock);
+
+	ret = kdbus_sanitize_attach_flags(cmd->attach_flags, &attach_flags);
+	if (ret < 0)
+		goto exit;
+
+	name = argv[1].item ? argv[1].item->str : NULL;
+
+	if (name) {
+		entry = kdbus_name_lookup_unlocked(bus->name_registry, name);
+		if (!entry || !entry->conn ||
+		    !kdbus_conn_policy_see_name(conn, current_cred(), name) ||
+		    (cmd->id != 0 && entry->conn->id != cmd->id)) {
+			/* pretend a name doesn't exist if you cannot see it */
+			ret = -ESRCH;
+			goto exit;
+		}
+
+		owner_conn = kdbus_conn_ref(entry->conn);
+	} else if (cmd->id > 0) {
+		owner_conn = kdbus_bus_find_conn_by_id(bus, cmd->id);
+		if (!owner_conn || !kdbus_conn_policy_see(conn, current_cred(),
+							  owner_conn)) {
+			/* pretend an id doesn't exist if you cannot see it */
+			ret = -ENXIO;
+			goto exit;
+		}
+	} else {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	info.id = owner_conn->id;
+	info.flags = owner_conn->flags;
+	kdbus_kvec_set(&kvec, &info, sizeof(info), &info.size);
+
+	attach_flags &= atomic64_read(&owner_conn->attach_flags_send);
+
+	conn_meta = kdbus_meta_conn_new();
+	if (IS_ERR(conn_meta)) {
+		ret = PTR_ERR(conn_meta);
+		conn_meta = NULL;
+		goto exit;
+	}
+
+	ret = kdbus_meta_conn_collect(conn_meta, NULL, owner_conn,
+				      attach_flags);
+	if (ret < 0)
+		goto exit;
+
+	ret = kdbus_meta_export_prepare(owner_conn->meta, conn_meta,
+					&attach_flags, &meta_size);
+	if (ret < 0)
+		goto exit;
+
+	slice = kdbus_pool_slice_alloc(conn->pool,
+				       info.size + meta_size, false);
+	if (IS_ERR(slice)) {
+		ret = PTR_ERR(slice);
+		slice = NULL;
+		goto exit;
+	}
+
+	ret = kdbus_meta_export(owner_conn->meta, conn_meta, attach_flags,
+				slice, sizeof(info), &meta_size);
+	if (ret < 0)
+		goto exit;
+
+	info.size += meta_size;
+
+	ret = kdbus_pool_slice_copy_kvec(slice, 0, &kvec, 1, sizeof(info));
+	if (ret < 0)
+		goto exit;
+
+	kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->info_size);
+
+	if (kdbus_member_set_user(&cmd->offset, argp, typeof(*cmd), offset) ||
+	    kdbus_member_set_user(&cmd->info_size, argp,
+				  typeof(*cmd), info_size)) {
+		ret = -EFAULT;
+		goto exit;
+	}
+
+	ret = 0;
+
+exit:
+	up_read(&bus->name_registry->rwlock);
+	kdbus_pool_slice_release(slice);
+	kdbus_meta_conn_unref(conn_meta);
+	kdbus_conn_unref(owner_conn);
+	return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_update() - handle KDBUS_CMD_UPDATE
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_update(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_bus *bus = conn->ep->bus;
+	struct kdbus_item *item_policy;
+	u64 *item_attach_send = NULL;
+	u64 *item_attach_recv = NULL;
+	struct kdbus_cmd *cmd;
+	u64 attach_send;
+	u64 attach_recv;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+		{ .type = KDBUS_ITEM_ATTACH_FLAGS_SEND },
+		{ .type = KDBUS_ITEM_ATTACH_FLAGS_RECV },
+		{ .type = KDBUS_ITEM_NAME, .multiple = true },
+		{ .type = KDBUS_ITEM_POLICY_ACCESS, .multiple = true },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	item_attach_send = argv[1].item ? &argv[1].item->data64[0] : NULL;
+	item_attach_recv = argv[2].item ? &argv[2].item->data64[0] : NULL;
+	item_policy = argv[3].item ? : argv[4].item;
+
+	if (item_attach_send) {
+		if (!kdbus_conn_is_ordinary(conn) &&
+		    !kdbus_conn_is_monitor(conn)) {
+			ret = -EOPNOTSUPP;
+			goto exit;
+		}
+
+		ret = kdbus_sanitize_attach_flags(*item_attach_send,
+						  &attach_send);
+		if (ret < 0)
+			goto exit;
+
+		if (bus->attach_flags_req & ~attach_send) {
+			ret = -EINVAL;
+			goto exit;
+		}
+	}
+
+	if (item_attach_recv) {
+		if (!kdbus_conn_is_ordinary(conn) &&
+		    !kdbus_conn_is_monitor(conn) &&
+		    !kdbus_conn_is_activator(conn)) {
+			ret = -EOPNOTSUPP;
+			goto exit;
+		}
+
+		ret = kdbus_sanitize_attach_flags(*item_attach_recv,
+						  &attach_recv);
+		if (ret < 0)
+			goto exit;
+	}
+
+	if (item_policy && !kdbus_conn_is_policy_holder(conn)) {
+		ret = -EOPNOTSUPP;
+		goto exit;
+	}
+
+	/* now that we verified the input, update the connection */
+
+	if (item_policy) {
+		ret = kdbus_policy_set(&conn->ep->bus->policy_db, cmd->items,
+				       KDBUS_ITEMS_SIZE(cmd, items),
+				       1, true, conn);
+		if (ret < 0)
+			goto exit;
+	}
+
+	if (item_attach_send)
+		atomic64_set(&conn->attach_flags_send, attach_send);
+
+	if (item_attach_recv)
+		atomic64_set(&conn->attach_flags_recv, attach_recv);
+
+exit:
+	return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_send() - handle KDBUS_CMD_SEND
+ * @conn:		connection to operate on
+ * @f:			file this command was called on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_send(struct kdbus_conn *conn, struct file *f, void __user *argp)
+{
+	struct kdbus_cmd_send *cmd;
+	struct kdbus_kmsg *kmsg = NULL;
+	struct file *cancel_fd = NULL;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+		{ .type = KDBUS_ITEM_CANCEL_FD },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE |
+				 KDBUS_SEND_SYNC_REPLY,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	if (!kdbus_conn_is_ordinary(conn))
+		return -EOPNOTSUPP;
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	cmd->reply.return_flags = 0;
+	kdbus_pool_publish_empty(conn->pool, &cmd->reply.offset,
+				 &cmd->reply.msg_size);
+
+	if (argv[1].item) {
+		cancel_fd = fget(argv[1].item->fds[0]);
+		if (IS_ERR(cancel_fd)) {
+			ret = PTR_ERR(cancel_fd);
+			cancel_fd = NULL;
+			goto exit;
+		}
+
+		if (!cancel_fd->f_op->poll) {
+			ret = -EINVAL;
+			goto exit;
+		}
+	}
+
+	kmsg = kdbus_kmsg_new_from_cmd(conn, cmd);
+	if (IS_ERR(kmsg)) {
+		ret = PTR_ERR(kmsg);
+		kmsg = NULL;
+		goto exit;
+	}
+
+	if (kmsg->msg.dst_id == KDBUS_DST_ID_BROADCAST) {
+		down_read(&conn->ep->bus->name_registry->rwlock);
+		kdbus_bus_broadcast(conn->ep->bus, conn, kmsg);
+		up_read(&conn->ep->bus->name_registry->rwlock);
+	} else if (cmd->flags & KDBUS_SEND_SYNC_REPLY) {
+		struct kdbus_reply *r;
+		ktime_t exp;
+
+		exp = ns_to_ktime(kmsg->msg.timeout_ns);
+		r = kdbus_conn_call(conn, kmsg, exp);
+		if (IS_ERR(r)) {
+			ret = PTR_ERR(r);
+			goto exit;
+		}
+
+		ret = kdbus_conn_wait_reply(conn, cmd, f, cancel_fd, r, exp);
+		kdbus_reply_unref(r);
+		if (ret < 0)
+			goto exit;
+	} else if ((kmsg->msg.flags & KDBUS_MSG_EXPECT_REPLY) ||
+		   kmsg->msg.cookie_reply == 0) {
+		ret = kdbus_conn_unicast(conn, kmsg);
+		if (ret < 0)
+			goto exit;
+	} else {
+		ret = kdbus_conn_reply(conn, kmsg);
+		if (ret < 0)
+			goto exit;
+	}
+
+	if (kdbus_member_set_user(&cmd->reply, argp, typeof(*cmd), reply))
+		ret = -EFAULT;
+
+exit:
+	if (cancel_fd)
+		fput(cancel_fd);
+	kdbus_kmsg_free(kmsg);
+	return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_recv() - handle KDBUS_CMD_RECV
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_recv(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_queue_entry *entry;
+	struct kdbus_cmd_recv *cmd;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE |
+				 KDBUS_RECV_PEEK |
+				 KDBUS_RECV_DROP |
+				 KDBUS_RECV_USE_PRIORITY,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	if (!kdbus_conn_is_ordinary(conn) &&
+	    !kdbus_conn_is_monitor(conn) &&
+	    !kdbus_conn_is_activator(conn))
+		return -EOPNOTSUPP;
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	cmd->dropped_msgs = 0;
+	cmd->msg.return_flags = 0;
+	kdbus_pool_publish_empty(conn->pool, &cmd->msg.offset,
+				 &cmd->msg.msg_size);
+
+	/* DROP+priority is not realiably, so prevent it */
+	if ((cmd->flags & KDBUS_RECV_DROP) &&
+	    (cmd->flags & KDBUS_RECV_USE_PRIORITY)) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	mutex_lock(&conn->lock);
+
+	entry = kdbus_queue_peek(&conn->queue, cmd->priority,
+				 cmd->flags & KDBUS_RECV_USE_PRIORITY);
+	if (!entry) {
+		mutex_unlock(&conn->lock);
+		ret = -EAGAIN;
+	} else if (cmd->flags & KDBUS_RECV_DROP) {
+		struct kdbus_reply *reply = kdbus_reply_ref(entry->reply);
+
+		kdbus_queue_entry_free(entry);
+
+		mutex_unlock(&conn->lock);
+
+		if (reply) {
+			mutex_lock(&reply->reply_dst->lock);
+			if (!list_empty(&reply->entry)) {
+				kdbus_reply_unlink(reply);
+				if (reply->sync)
+					kdbus_sync_reply_wakeup(reply, -EPIPE);
+				else
+					kdbus_notify_reply_dead(conn->ep->bus,
+							reply->reply_dst->id,
+							reply->cookie);
+			}
+			mutex_unlock(&reply->reply_dst->lock);
+			kdbus_notify_flush(conn->ep->bus);
+		}
+
+		kdbus_reply_unref(reply);
+	} else {
+		bool install_fds;
+
+		/*
+		 * PEEK just returns the location of the next message. Do not
+		 * install FDs nor memfds nor anything else. The only
+		 * information of interest should be the message header and
+		 * metadata. Any FD numbers in the payload is undefined for
+		 * PEEK'ed messages.
+		 * Also make sure to never install fds into a connection that
+		 * has refused to receive any. Ordinary connections will not get
+		 * messages with FDs queued (the receiver will get -ECOMM), but
+		 * eavesdroppers might.
+		 */
+		install_fds = (conn->flags & KDBUS_HELLO_ACCEPT_FD) &&
+			      !(cmd->flags & KDBUS_RECV_PEEK);
+
+		ret = kdbus_queue_entry_install(entry,
+						&cmd->msg.return_flags,
+						install_fds);
+		if (ret < 0) {
+			mutex_unlock(&conn->lock);
+			goto exit;
+		}
+
+		kdbus_pool_slice_publish(entry->slice, &cmd->msg.offset,
+					 &cmd->msg.msg_size);
+
+		if (!(cmd->flags & KDBUS_RECV_PEEK))
+			kdbus_queue_entry_free(entry);
+
+		mutex_unlock(&conn->lock);
+	}
+
+	cmd->dropped_msgs = atomic_xchg(&conn->lost_count, 0);
+	if (cmd->dropped_msgs > 0)
+		cmd->return_flags |= KDBUS_RECV_RETURN_DROPPED_MSGS;
+
+	if (kdbus_member_set_user(&cmd->msg, argp, typeof(*cmd), msg) ||
+	    kdbus_member_set_user(&cmd->dropped_msgs, argp, typeof(*cmd),
+				  dropped_msgs))
+		ret = -EFAULT;
+
+exit:
+	return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_free() - handle KDBUS_CMD_FREE
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_free(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_cmd_free *cmd;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	if (!kdbus_conn_is_ordinary(conn) &&
+	    !kdbus_conn_is_monitor(conn) &&
+	    !kdbus_conn_is_activator(conn))
+		return -EOPNOTSUPP;
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	ret = kdbus_pool_release_offset(conn->pool, cmd->offset);
+
+	return kdbus_args_clear(&args, ret);
+}
ipc/kdbus/connection.h new file +257 -0

257 lines not on this page · raw message

ipc/kdbus/item.c new file +339 -0

339 lines not on this page · raw message

ipc/kdbus/item.h new file +64 -0

64 lines not on this page · raw message

ipc/kdbus/message.c new file +616 -0

125 lines not on this page · raw message

ipc/kdbus/message.h new file +133 -0

133 lines not on this page · raw message

ipc/kdbus/queue.c new file +678 -0

678 lines not on this page · raw message

ipc/kdbus/queue.h new file +92 -0

92 lines not on this page · raw message

ipc/kdbus/reply.c new file +259 -0

259 lines not on this page · raw message

ipc/kdbus/reply.h new file +68 -0

68 lines not on this page · raw message

ipc/kdbus/util.h +1 -1
index 9caadb337912..740b19880985 100644

Lines of this file are not shown. View the raw message.

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.3.1

Part of the diff is not shown here. View the raw message.

#6 lore raw

PATCH v4 6/14 kdbus: add node and filesystem implementation

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

kdbusfs is a filesystem that will expose a fresh kdbus domain context each time it is mounted. Per mount point, there will be a 'control' node, which can be used to create buses. fs.c contains the implementation of that pseudo-fs. Exported inodes of 'file' type have their i_fop set to either kdbus_handle_control_ops or kdbus_handle_ep_ops, depending on their type. The actual dispatching of file operations is done from handle.c

node.c is an implementation of a kdbus object that has an id and children, organized in an R/B tree. The tree is used by the filesystem code for lookup and iterator functions, and to deactivate children once the parent is deactivated. Every inode exported by kdbusfs is backed by a kdbus_node, hence it is embedded in struct kdbus_ep, struct kdbus_bus and struct kdbus_domain.

---
 include/uapi/linux/magic.h |   2 +
 ipc/kdbus/fs.c             | 510 +++++++++++++++++++++++++
 ipc/kdbus/fs.h             |  28 ++
 ipc/kdbus/node.c           | 910 +++++++++++++++++++++++++++++++++++++++++++++
 ipc/kdbus/node.h           |  84 +++++
 5 files changed, 1534 insertions(+)
 create mode 100644 ipc/kdbus/fs.c
 create mode 100644 ipc/kdbus/fs.h
 create mode 100644 ipc/kdbus/node.c
 create mode 100644 ipc/kdbus/node.h
include/uapi/linux/magic.h +2 -0
index 7d664ea85ebd..1cf05c066158 100644
@@ -74,4 +74,6 @@
 #define BTRFS_TEST_MAGIC	0x73727279
 #define NSFS_MAGIC		0x6e736673
 
+#define KDBUS_SUPER_MAGIC	0x44427573
+
 #endif /* __LINUX_MAGIC_H__ */
ipc/kdbus/fs.c new file +510 -0

510 lines not on this page · raw message

ipc/kdbus/fs.h new file +28 -0
new file mode 100644
index 000000000000..62f7d6abf11e
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2013-2015 Daniel Mack <[email protected]>
+ * Copyright (C) 2013-2015 David Herrmann <[email protected]>
+ * Copyright (C) 2013-2015 Linux Foundation
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef __KDBUSFS_H
+#define __KDBUSFS_H
+
+#include <linux/kernel.h>
+
+struct kdbus_node;
+
+int kdbus_fs_init(void);
+void kdbus_fs_exit(void);
+void kdbus_fs_flush(struct kdbus_node *node);
+
+#define kdbus_node_from_inode(_inode) \
+	((struct kdbus_node *)(_inode)->i_private)
+
+#endif
ipc/kdbus/node.c new file +910 -0

910 lines not on this page · raw message

ipc/kdbus/node.h new file +84 -0

84 lines not on this page · raw message

Signature · 1 line
2.3.1
#7 lore raw

PATCH v4 7/14 kdbus: add code to gather metadata

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

A connection chooses which metadata it wants to have attached to each message it receives with kdbus_cmd_hello.attach_flags. The metadata will be attached as items to the messages. All metadata refers to information about the sending task at sending time, unless otherwise stated. Also, the metadata is copied, not referenced, so even if the sending task doesn't exist anymore at the time the message is received, the information is still preserved.

In traditional D-Bus, userspace tasks like polkit or journald make a live lookup in procfs and sysfs to gain information about a sending task. This is racy, of course, as in a a connection-less system like D-Bus, the originating peer can go away immediately after sending the message. As we're moving D-Bus prmitives into the kernel, we have to provide the same semantics here, and inform the receiving peer on the live credentials of the sending peer.

Metadata is collected at the following times.

* When a bus is created (KDBUS_CMD_MAKE), information about the calling task is collected. This data is returned by the kernel via the KDBUS_CMD_BUS_CREATOR_INFO call.

* When a connection is created (KDBUS_CMD_HELLO), information about the calling task is collected. Alternatively, a privileged connection may provide 'faked' information about credentials, PIDs and security labels which will be stored instead. This data is returned by the kernel as information on a connection (KDBUS_CMD_CONN_INFO). Only metadata that a connection allowed to be sent (by setting its bit in attach_flags_send) will be exported in this way.

* When a message is sent (KDBUS_CMD_SEND), information about the sending task and the sending connection are collected. This metadata will be attached to the message when it arrives in the receiver's pool. If the connection sending the message installed faked credentials (see kdbus.connection(7)), the message will not be augmented by any information about the currently sending task.

Which metadata items are actually delivered depends on the following sets and masks:

(a) the system-wide kmod creds mask
(module parameter 'attach_flags_mask')

(b) the per-connection send creds mask, set by the connecting client

(c) the per-connection receive creds mask, set by the connecting client

(d) the per-bus minimal creds mask, set by the bus creator

(e) the per-bus owner creds mask, set by the bus creator

(f) the mask specified when querying creds of a bus peer

(g) the mask specified when querying creds of a bus owner

With the following rules:

[1] The creds attached to messages are determined as a & b & c.

[2] When connecting to a bus (KDBUS_CMD_HELLO), and ~b & d != 0, the call will fail with, -1, and errno is set to ECONNREFUSED.

[3] When querying creds of a bus peer, the creds returned are a & b & f.

[4] When querying creds of a bus owner, the creds returned are a & e & g.

See kdbus.metadata(7) and kdbus.item(7) for more details on which metadata can currently be attached to messages.

---
 ipc/kdbus/metadata.c | 1164 ++++++++++++++++++++++++++++++++++++++++++++++++++
 ipc/kdbus/metadata.h |   57 +++
 2 files changed, 1221 insertions(+)
 create mode 100644 ipc/kdbus/metadata.c
 create mode 100644 ipc/kdbus/metadata.h
ipc/kdbus/metadata.c new file +1164 -0
new file mode 100644
index 000000000000..06e0a54a276a
@@ -0,0 +1,1164 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2013-2015 Daniel Mack <[email protected]>
+ * Copyright (C) 2013-2015 David Herrmann <[email protected]>
+ * Copyright (C) 2013-2015 Linux Foundation
+ * Copyright (C) 2014-2015 Djalal Harouni <[email protected]>
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/audit.h>
+#include <linux/capability.h>
+#include <linux/cgroup.h>
+#include <linux/cred.h>
+#include <linux/file.h>
+#include <linux/fs_struct.h>
+#include <linux/init.h>
+#include <linux/kref.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/security.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uidgid.h>
+#include <linux/uio.h>
+#include <linux/user_namespace.h>
+#include <linux/version.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "endpoint.h"
+#include "item.h"
+#include "message.h"
+#include "metadata.h"
+#include "names.h"
+
+/**
+ * struct kdbus_meta_proc - Process metadata
+ * @kref:		Reference counting
+ * @lock:		Object lock
+ * @collected:		Bitmask of collected items
+ * @valid:		Bitmask of collected and valid items
+ * @uid:		UID of process
+ * @euid:		EUID of process
+ * @suid:		SUID of process
+ * @fsuid:		FSUID of process
+ * @gid:		GID of process
+ * @egid:		EGID of process
+ * @sgid:		SGID of process
+ * @fsgid:		FSGID of process
+ * @pid:		PID of process
+ * @tgid:		TGID of process
+ * @ppid:		PPID of process
+ * @auxgrps:		Auxiliary groups
+ * @n_auxgrps:		Number of items in @auxgrps
+ * @tid_comm:		TID comm line
+ * @pid_comm:		PID comm line
+ * @exe_path:		Executable path
+ * @root_path:		Root-FS path
+ * @cmdline:		Command-line
+ * @cgroup:		Full cgroup path
+ * @caps:		Capabilities
+ * @caps_namespace:	User-namespace of @caps
+ * @seclabel:		Seclabel
+ * @audit_loginuid:	Audit login-UID
+ * @audit_sessionid:	Audit session-ID
+ */
+struct kdbus_meta_proc {
+	struct kref kref;
+	struct mutex lock;
+	u64 collected;
+	u64 valid;
+
+	/* KDBUS_ITEM_CREDS */
+	kuid_t uid, euid, suid, fsuid;
+	kgid_t gid, egid, sgid, fsgid;
+
+	/* KDBUS_ITEM_PIDS */
+	struct pid *pid;
+	struct pid *tgid;
+	struct pid *ppid;
+
+	/* KDBUS_ITEM_AUXGROUPS */
+	kgid_t *auxgrps;
+	size_t n_auxgrps;
+
+	/* KDBUS_ITEM_TID_COMM */
+	char tid_comm[TASK_COMM_LEN];
+	/* KDBUS_ITEM_PID_COMM */
+	char pid_comm[TASK_COMM_LEN];
+
+	/* KDBUS_ITEM_EXE */
+	struct path exe_path;
+	struct path root_path;
+
+	/* KDBUS_ITEM_CMDLINE */
+	char *cmdline;
+
+	/* KDBUS_ITEM_CGROUP */
+	char *cgroup;
+
+	/* KDBUS_ITEM_CAPS */
+	struct caps {
+		/* binary compatible to kdbus_caps */
+		u32 last_cap;
+		struct {
+			u32 caps[_KERNEL_CAPABILITY_U32S];
+		} set[4];
+	} caps;
+	struct user_namespace *caps_namespace;
+
+	/* KDBUS_ITEM_SECLABEL */
+	char *seclabel;
+
+	/* KDBUS_ITEM_AUDIT */
+	kuid_t audit_loginuid;
+	unsigned int audit_sessionid;
+};
+
+/**
+ * struct kdbus_meta_conn
+ * @kref:		Reference counting
+ * @lock:		Object lock
+ * @collected:		Bitmask of collected items
+ * @valid:		Bitmask of collected and valid items
+ * @ts:			Timestamp values
+ * @owned_names_items:	Serialized items for owned names
+ * @owned_names_size:	Size of @owned_names_items
+ * @conn_description:	Connection description
+ */
+struct kdbus_meta_conn {
+	struct kref kref;
+	struct mutex lock;
+	u64 collected;
+	u64 valid;
+
+	/* KDBUS_ITEM_TIMESTAMP */
+	struct kdbus_timestamp ts;
+
+	/* KDBUS_ITEM_OWNED_NAME */
+	struct kdbus_item *owned_names_items;
+	size_t owned_names_size;
+
+	/* KDBUS_ITEM_CONN_DESCRIPTION */
+	char *conn_description;
+};
+
+/**
+ * kdbus_meta_proc_new() - Create process metadata object
+ *
+ * Return: Pointer to new object on success, ERR_PTR on failure.
+ */
+struct kdbus_meta_proc *kdbus_meta_proc_new(void)
+{
+	struct kdbus_meta_proc *mp;
+
+	mp = kzalloc(sizeof(*mp), GFP_KERNEL);
+	if (!mp)
+		return ERR_PTR(-ENOMEM);
+
+	kref_init(&mp->kref);
+	mutex_init(&mp->lock);
+
+	return mp;
+}
+
+static void kdbus_meta_proc_free(struct kref *kref)
+{
+	struct kdbus_meta_proc *mp = container_of(kref, struct kdbus_meta_proc,
+						  kref);
+
+	path_put(&mp->exe_path);
+	path_put(&mp->root_path);
+	put_user_ns(mp->caps_namespace);
+	put_pid(mp->ppid);
+	put_pid(mp->tgid);
+	put_pid(mp->pid);
+
+	kfree(mp->seclabel);
+	kfree(mp->auxgrps);
+	kfree(mp->cmdline);
+	kfree(mp->cgroup);
+	kfree(mp);
+}
+
+/**
+ * kdbus_meta_proc_ref() - Gain reference
+ * @mp:		Process metadata object
+ *
+ * Return: @mp is returned
+ */
+struct kdbus_meta_proc *kdbus_meta_proc_ref(struct kdbus_meta_proc *mp)
+{
+	if (mp)
+		kref_get(&mp->kref);
+	return mp;
+}
+
+/**
+ * kdbus_meta_proc_unref() - Drop reference
+ * @mp:		Process metadata object
+ *
+ * Return: NULL
+ */
+struct kdbus_meta_proc *kdbus_meta_proc_unref(struct kdbus_meta_proc *mp)
+{
+	if (mp)
+		kref_put(&mp->kref, kdbus_meta_proc_free);
+	return NULL;
+}
+
+static void kdbus_meta_proc_collect_creds(struct kdbus_meta_proc *mp)
+{
+	mp->uid		= current_uid();
+	mp->euid	= current_euid();
+	mp->suid	= current_suid();
+	mp->fsuid	= current_fsuid();
+
+	mp->gid		= current_gid();
+	mp->egid	= current_egid();
+	mp->sgid	= current_sgid();
+	mp->fsgid	= current_fsgid();
+
+	mp->valid |= KDBUS_ATTACH_CREDS;
+}
+
+static void kdbus_meta_proc_collect_pids(struct kdbus_meta_proc *mp)
+{
+	struct task_struct *parent;
+
+	mp->pid = get_pid(task_pid(current));
+	mp->tgid = get_pid(task_tgid(current));
+
+	rcu_read_lock();
+	parent = rcu_dereference(current->real_parent);
+	mp->ppid = get_pid(task_tgid(parent));
+	rcu_read_unlock();
+
+	mp->valid |= KDBUS_ATTACH_PIDS;
+}
+
+static int kdbus_meta_proc_collect_auxgroups(struct kdbus_meta_proc *mp)
+{
+	struct group_info *info;
+	size_t i;
+
+	info = get_current_groups();
+
+	if (info->ngroups > 0) {
+		mp->auxgrps = kmalloc_array(info->ngroups, sizeof(kgid_t),
+					    GFP_KERNEL);
+		if (!mp->auxgrps) {
+			put_group_info(info);
+			return -ENOMEM;
+		}
+
+		for (i = 0; i < info->ngroups; i++)
+			mp->auxgrps[i] = GROUP_AT(info, i);
+	}
+
+	mp->n_auxgrps = info->ngroups;
+	put_group_info(info);
+	mp->valid |= KDBUS_ATTACH_AUXGROUPS;
+
+	return 0;
+}
+
+static void kdbus_meta_proc_collect_tid_comm(struct kdbus_meta_proc *mp)
+{
+	get_task_comm(mp->tid_comm, current);
+	mp->valid |= KDBUS_ATTACH_TID_COMM;
+}
+
+static void kdbus_meta_proc_collect_pid_comm(struct kdbus_meta_proc *mp)
+{
+	get_task_comm(mp->pid_comm, current->group_leader);
+	mp->valid |= KDBUS_ATTACH_PID_COMM;
+}
+
+static void kdbus_meta_proc_collect_exe(struct kdbus_meta_proc *mp)
+{
+	struct mm_struct *mm;
+
+	mm = get_task_mm(current);
+	if (!mm)
+		return;
+
+	down_read(&mm->mmap_sem);
+	if (mm->exe_file) {
+		mp->exe_path = mm->exe_file->f_path;
+		path_get(&mp->exe_path);
+		get_fs_root(current->fs, &mp->root_path);
+		mp->valid |= KDBUS_ATTACH_EXE;
+	}
+	up_read(&mm->mmap_sem);
+
+	mmput(mm);
+}
+
+static int kdbus_meta_proc_collect_cmdline(struct kdbus_meta_proc *mp)
+{
+	struct mm_struct *mm;
+	char *cmdline;
+
+	mm = get_task_mm(current);
+	if (!mm)
+		return 0;
+
+	if (!mm->arg_end) {
+		mmput(mm);
+		return 0;
+	}
+
+	cmdline = strndup_user((const char __user *)mm->arg_start,
+			       mm->arg_end - mm->arg_start);
+	mmput(mm);
+
+	if (IS_ERR(cmdline))
+		return PTR_ERR(cmdline);
+
+	mp->cmdline = cmdline;
+	mp->valid |= KDBUS_ATTACH_CMDLINE;
+
+	return 0;
+}
+
+static int kdbus_meta_proc_collect_cgroup(struct kdbus_meta_proc *mp)
+{
+#ifdef CONFIG_CGROUPS
+	void *page;
+	char *s;
+
+	page = (void *)__get_free_page(GFP_TEMPORARY);
+	if (!page)
+		return -ENOMEM;
+
+	s = task_cgroup_path(current, page, PAGE_SIZE);
+	if (s) {
+		mp->cgroup = kstrdup(s, GFP_KERNEL);
+		if (!mp->cgroup) {
+			free_page((unsigned long)page);
+			return -ENOMEM;
+		}
+	}
+
+	free_page((unsigned long)page);
+	mp->valid |= KDBUS_ATTACH_CGROUP;
+#endif
+
+	return 0;
+}
+
+static void kdbus_meta_proc_collect_caps(struct kdbus_meta_proc *mp)
+{
+	const struct cred *c = current_cred();
+	int i;
+
+	/* ABI: "last_cap" equals /proc/sys/kernel/cap_last_cap */
+	mp->caps.last_cap = CAP_LAST_CAP;
+	mp->caps_namespace = get_user_ns(current_user_ns());
+
+	CAP_FOR_EACH_U32(i) {
+		mp->caps.set[0].caps[i] = c->cap_inheritable.cap[i];
+		mp->caps.set[1].caps[i] = c->cap_permitted.cap[i];
+		mp->caps.set[2].caps[i] = c->cap_effective.cap[i];
+		mp->caps.set[3].caps[i] = c->cap_bset.cap[i];
+	}
+
+	/* clear unused bits */
+	for (i = 0; i < 4; i++)
+		mp->caps.set[i].caps[CAP_TO_INDEX(CAP_LAST_CAP)] &=
+						CAP_LAST_U32_VALID_MASK;
+
+	mp->valid |= KDBUS_ATTACH_CAPS;
+}
+
+static int kdbus_meta_proc_collect_seclabel(struct kdbus_meta_proc *mp)
+{
+#ifdef CONFIG_SECURITY
+	char *ctx = NULL;
+	u32 sid, len;
+	int ret;
+
+	security_task_getsecid(current, &sid);
+	ret = security_secid_to_secctx(sid, &ctx, &len);
+	if (ret < 0) {
+		/*
+		 * EOPNOTSUPP means no security module is active,
+		 * lets skip adding the seclabel then. This effectively
+		 * drops the SECLABEL item.
+		 */
+		return (ret == -EOPNOTSUPP) ? 0 : ret;
+	}
+
+	mp->seclabel = kstrdup(ctx, GFP_KERNEL);
+	security_release_secctx(ctx, len);
+	if (!mp->seclabel)
+		return -ENOMEM;
+
+	mp->valid |= KDBUS_ATTACH_SECLABEL;
+#endif
+
+	return 0;
+}
+
+static void kdbus_meta_proc_collect_audit(struct kdbus_meta_proc *mp)
+{
+#ifdef CONFIG_AUDITSYSCALL
+	mp->audit_loginuid = audit_get_loginuid(current);
+	mp->audit_sessionid = audit_get_sessionid(current);
+	mp->valid |= KDBUS_ATTACH_AUDIT;
+#endif
+}
+
+/**
+ * kdbus_meta_proc_collect() - Collect process metadata
+ * @mp:		Process metadata object
+ * @what:	Attach flags to collect
+ *
+ * This collects process metadata from current and saves it in @mp.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_meta_proc_collect(struct kdbus_meta_proc *mp, u64 what)
+{
+	int ret;
+
+	if (!mp || !(what & (KDBUS_ATTACH_CREDS |
+			     KDBUS_ATTACH_PIDS |
+			     KDBUS_ATTACH_AUXGROUPS |
+			     KDBUS_ATTACH_TID_COMM |
+			     KDBUS_ATTACH_PID_COMM |
+			     KDBUS_ATTACH_EXE |
+			     KDBUS_ATTACH_CMDLINE |
+			     KDBUS_ATTACH_CGROUP |
+			     KDBUS_ATTACH_CAPS |
+			     KDBUS_ATTACH_SECLABEL |
+			     KDBUS_ATTACH_AUDIT)))
+		return 0;
+
+	mutex_lock(&mp->lock);
+
+	if ((what & KDBUS_ATTACH_CREDS) &&
+	    !(mp->collected & KDBUS_ATTACH_CREDS)) {
+		kdbus_meta_proc_collect_creds(mp);
+		mp->collected |= KDBUS_ATTACH_CREDS;
+	}
+
+	if ((what & KDBUS_ATTACH_PIDS) &&
+	    !(mp->collected & KDBUS_ATTACH_PIDS)) {
+		kdbus_meta_proc_collect_pids(mp);
+		mp->collected |= KDBUS_ATTACH_PIDS;
+	}
+
+	if ((what & KDBUS_ATTACH_AUXGROUPS) &&
+	    !(mp->collected & KDBUS_ATTACH_AUXGROUPS)) {
+		ret = kdbus_meta_proc_collect_auxgroups(mp);
+		if (ret < 0)
+			goto exit_unlock;
+		mp->collected |= KDBUS_ATTACH_AUXGROUPS;
+	}
+
+	if ((what & KDBUS_ATTACH_TID_COMM) &&
+	    !(mp->collected & KDBUS_ATTACH_TID_COMM)) {
+		kdbus_meta_proc_collect_tid_comm(mp);
+		mp->collected |= KDBUS_ATTACH_TID_COMM;
+	}
+
+	if ((what & KDBUS_ATTACH_PID_COMM) &&
+	    !(mp->collected & KDBUS_ATTACH_PID_COMM)) {
+		kdbus_meta_proc_collect_pid_comm(mp);
+		mp->collected |= KDBUS_ATTACH_PID_COMM;
+	}
+
+	if ((what & KDBUS_ATTACH_EXE) &&
+	    !(mp->collected & KDBUS_ATTACH_EXE)) {
+		kdbus_meta_proc_collect_exe(mp);
+		mp->collected |= KDBUS_ATTACH_EXE;
+	}
+
+	if ((what & KDBUS_ATTACH_CMDLINE) &&
+	    !(mp->collected & KDBUS_ATTACH_CMDLINE)) {
+		ret = kdbus_meta_proc_collect_cmdline(mp);
+		if (ret < 0)
+			goto exit_unlock;
+		mp->collected |= KDBUS_ATTACH_CMDLINE;
+	}
+
+	if ((what & KDBUS_ATTACH_CGROUP) &&
+	    !(mp->collected & KDBUS_ATTACH_CGROUP)) {
+		ret = kdbus_meta_proc_collect_cgroup(mp);
+		if (ret < 0)
+			goto exit_unlock;
+		mp->collected |= KDBUS_ATTACH_CGROUP;
+	}
+
+	if ((what & KDBUS_ATTACH_CAPS) &&
+	    !(mp->collected & KDBUS_ATTACH_CAPS)) {
+		kdbus_meta_proc_collect_caps(mp);
+		mp->collected |= KDBUS_ATTACH_CAPS;
+	}
+
+	if ((what & KDBUS_ATTACH_SECLABEL) &&
+	    !(mp->collected & KDBUS_ATTACH_SECLABEL)) {
+		ret = kdbus_meta_proc_collect_seclabel(mp);
+		if (ret < 0)
+			goto exit_unlock;
+		mp->collected |= KDBUS_ATTACH_SECLABEL;
+	}
+
+	if ((what & KDBUS_ATTACH_AUDIT) &&
+	    !(mp->collected & KDBUS_ATTACH_AUDIT)) {
+		kdbus_meta_proc_collect_audit(mp);
+		mp->collected |= KDBUS_ATTACH_AUDIT;
+	}
+
+	ret = 0;
+
+exit_unlock:
+	mutex_unlock(&mp->lock);
+	return ret;
+}
+
+/**
+ * kdbus_meta_proc_fake() - Fill process metadata from faked credentials
+ * @mp:		Metadata
+ * @creds:	Creds to set, may be %NULL
+ * @pids:	PIDs to set, may be %NULL
+ * @seclabel:	Seclabel to set, may be %NULL
+ *
+ * This function takes information stored in @creds, @pids and @seclabel and
+ * resolves them to kernel-representations, if possible. A call to this function
+ * is considered an alternative to calling kdbus_meta_add_current(), which
+ * derives the same information from the 'current' task.
+ *
+ * This call uses the current task's namespaces to resolve the given
+ * information.
+ *
+ * Return: 0 on success, negative error number otherwise.
+ */
+int kdbus_meta_proc_fake(struct kdbus_meta_proc *mp,
+			 const struct kdbus_creds *creds,
+			 const struct kdbus_pids *pids,
+			 const char *seclabel)
+{
+	int ret;
+
+	if (!mp)
+		return 0;
+
+	mutex_lock(&mp->lock);
+
+	if (creds && !(mp->collected & KDBUS_ATTACH_CREDS)) {
+		struct user_namespace *ns = current_user_ns();
+
+		mp->uid		= make_kuid(ns, creds->uid);
+		mp->euid	= make_kuid(ns, creds->euid);
+		mp->suid	= make_kuid(ns, creds->suid);
+		mp->fsuid	= make_kuid(ns, creds->fsuid);
+
+		mp->gid		= make_kgid(ns, creds->gid);
+		mp->egid	= make_kgid(ns, creds->egid);
+		mp->sgid	= make_kgid(ns, creds->sgid);
+		mp->fsgid	= make_kgid(ns, creds->fsgid);
+
+		if ((creds->uid   != (uid_t)-1 && !uid_valid(mp->uid))   ||
+		    (creds->euid  != (uid_t)-1 && !uid_valid(mp->euid))  ||
+		    (creds->suid  != (uid_t)-1 && !uid_valid(mp->suid))  ||
+		    (creds->fsuid != (uid_t)-1 && !uid_valid(mp->fsuid)) ||
+		    (creds->gid   != (gid_t)-1 && !gid_valid(mp->gid))   ||
+		    (creds->egid  != (gid_t)-1 && !gid_valid(mp->egid))  ||
+		    (creds->sgid  != (gid_t)-1 && !gid_valid(mp->sgid))  ||
+		    (creds->fsgid != (gid_t)-1 && !gid_valid(mp->fsgid))) {
+			ret = -EINVAL;
+			goto exit_unlock;
+		}
+
+		mp->valid |= KDBUS_ATTACH_CREDS;
+		mp->collected |= KDBUS_ATTACH_CREDS;
+	}
+
+	if (pids && !(mp->collected & KDBUS_ATTACH_PIDS)) {
+		mp->pid = get_pid(find_vpid(pids->tid));
+		mp->tgid = get_pid(find_vpid(pids->pid));
+		mp->ppid = get_pid(find_vpid(pids->ppid));
+
+		if ((pids->tid != 0 && !mp->pid) ||
+		    (pids->pid != 0 && !mp->tgid) ||
+		    (pids->ppid != 0 && !mp->ppid)) {
+			put_pid(mp->pid);
+			put_pid(mp->tgid);
+			put_pid(mp->ppid);
+			mp->pid = NULL;
+			mp->tgid = NULL;
+			mp->ppid = NULL;
+			ret = -EINVAL;
+			goto exit_unlock;
+		}
+
+		mp->valid |= KDBUS_ATTACH_PIDS;
+		mp->collected |= KDBUS_ATTACH_PIDS;
+	}
+
+	if (seclabel && !(mp->collected & KDBUS_ATTACH_SECLABEL)) {
+		mp->seclabel = kstrdup(seclabel, GFP_KERNEL);
+		if (!mp->seclabel) {
+			ret = -ENOMEM;
+			goto exit_unlock;
+		}
+
+		mp->valid |= KDBUS_ATTACH_SECLABEL;
+		mp->collected |= KDBUS_ATTACH_SECLABEL;
+	}
+
+	ret = 0;
+
+exit_unlock:
+	mutex_unlock(&mp->lock);
+	return ret;
+}
+
+/**
+ * kdbus_meta_conn_new() - Create connection metadata object
+ *
+ * Return: Pointer to new object on success, ERR_PTR on failure.
+ */
+struct kdbus_meta_conn *kdbus_meta_conn_new(void)
+{
+	struct kdbus_meta_conn *mc;
+
+	mc = kzalloc(sizeof(*mc), GFP_KERNEL);
+	if (!mc)
+		return ERR_PTR(-ENOMEM);
+
+	kref_init(&mc->kref);
+	mutex_init(&mc->lock);
+
+	return mc;
+}
+
+static void kdbus_meta_conn_free(struct kref *kref)
+{
+	struct kdbus_meta_conn *mc =
+		container_of(kref, struct kdbus_meta_conn, kref);
+
+	kfree(mc->conn_description);
+	kfree(mc->owned_names_items);
+	kfree(mc);
+}
+
+/**
+ * kdbus_meta_conn_ref() - Gain reference
+ * @mc:		Connection metadata object
+ */
+struct kdbus_meta_conn *kdbus_meta_conn_ref(struct kdbus_meta_conn *mc)
+{
+	if (mc)
+		kref_get(&mc->kref);
+	return mc;
+}
+
+/**
+ * kdbus_meta_conn_unref() - Drop reference
+ * @mc:		Connection metadata object
+ */
+struct kdbus_meta_conn *kdbus_meta_conn_unref(struct kdbus_meta_conn *mc)
+{
+	if (mc)
+		kref_put(&mc->kref, kdbus_meta_conn_free);
+	return NULL;
+}
+
+static void kdbus_meta_conn_collect_timestamp(struct kdbus_meta_conn *mc,
+					      struct kdbus_kmsg *kmsg)
+{
+	struct timespec ts;
+
+	ktime_get_ts(&ts);
+	mc->ts.monotonic_ns = timespec_to_ns(&ts);
+
+	ktime_get_real_ts(&ts);
+	mc->ts.realtime_ns = timespec_to_ns(&ts);
+
+	if (kmsg)
+		mc->ts.seqnum = kmsg->seq;
+
+	mc->valid |= KDBUS_ATTACH_TIMESTAMP;
+}
+
+static int kdbus_meta_conn_collect_names(struct kdbus_meta_conn *mc,
+					 struct kdbus_conn *conn)
+{
+	const struct kdbus_name_entry *e;
+	struct kdbus_item *item;
+	size_t slen, size;
+
+	lockdep_assert_held(&conn->ep->bus->name_registry->rwlock);
+
+	size = 0;
+	list_for_each_entry(e, &conn->names_list, conn_entry)
+		size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_name) +
+					strlen(e->name) + 1);
+
+	if (!size)
+		return 0;
+
+	item = kmalloc(size, GFP_KERNEL);
+	if (!item)
+		return -ENOMEM;
+
+	mc->owned_names_items = item;
+	mc->owned_names_size = size;
+
+	list_for_each_entry(e, &conn->names_list, conn_entry) {
+		slen = strlen(e->name) + 1;
+		kdbus_item_set(item, KDBUS_ITEM_OWNED_NAME, NULL,
+			       sizeof(struct kdbus_name) + slen);
+		item->name.flags = e->flags;
+		memcpy(item->name.name, e->name, slen);
+		item = KDBUS_ITEM_NEXT(item);
+	}
+
+	/* sanity check: the buffer should be completely written now */
+	WARN_ON((u8 *)item != (u8 *)mc->owned_names_items + size);
+
+	mc->valid |= KDBUS_ATTACH_NAMES;
+	return 0;
+}
+
+static int kdbus_meta_conn_collect_description(struct kdbus_meta_conn *mc,
+					       struct kdbus_conn *conn)
+{
+	if (!conn->description)
+		return 0;
+
+	mc->conn_description = kstrdup(conn->description, GFP_KERNEL);
+	if (!mc->conn_description)
+		return -ENOMEM;
+
+	mc->valid |= KDBUS_ATTACH_CONN_DESCRIPTION;
+	return 0;
+}
+
+/**
+ * kdbus_meta_conn_collect() - Collect connection metadata
+ * @mc:		Message metadata object
+ * @kmsg:	Kmsg to collect data from
+ * @conn:	Connection to collect data from
+ * @what:	Attach flags to collect
+ *
+ * This collects connection metadata from @kmsg and @conn and saves it in @mc.
+ *
+ * If KDBUS_ATTACH_NAMES is set in @what and @conn is non-NULL, the caller must
+ * hold the name-registry read-lock of conn->ep->bus->registry.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_meta_conn_collect(struct kdbus_meta_conn *mc,
+			    struct kdbus_kmsg *kmsg,
+			    struct kdbus_conn *conn,
+			    u64 what)
+{
+	int ret;
+
+	if (!mc || !(what & (KDBUS_ATTACH_TIMESTAMP |
+			     KDBUS_ATTACH_NAMES |
+			     KDBUS_ATTACH_CONN_DESCRIPTION)))
+		return 0;
+
+	mutex_lock(&mc->lock);
+
+	if (kmsg && (what & KDBUS_ATTACH_TIMESTAMP) &&
+	    !(mc->collected & KDBUS_ATTACH_TIMESTAMP)) {
+		kdbus_meta_conn_collect_timestamp(mc, kmsg);
+		mc->collected |= KDBUS_ATTACH_TIMESTAMP;
+	}
+
+	if (conn && (what & KDBUS_ATTACH_NAMES) &&
+	    !(mc->collected & KDBUS_ATTACH_NAMES)) {
+		ret = kdbus_meta_conn_collect_names(mc, conn);
+		if (ret < 0)
+			goto exit_unlock;
+		mc->collected |= KDBUS_ATTACH_NAMES;
+	}
+
+	if (conn && (what & KDBUS_ATTACH_CONN_DESCRIPTION) &&
+	    !(mc->collected & KDBUS_ATTACH_CONN_DESCRIPTION)) {
+		ret = kdbus_meta_conn_collect_description(mc, conn);
+		if (ret < 0)
+			goto exit_unlock;
+		mc->collected |= KDBUS_ATTACH_CONN_DESCRIPTION;
+	}
+
+	ret = 0;
+
+exit_unlock:
+	mutex_unlock(&mc->lock);
+	return ret;
+}
+
+/*
+ * kdbus_meta_export_prepare() - Prepare metadata for export
+ * @mp:		Process metadata, or NULL
+ * @mc:		Connection metadata, or NULL
+ * @mask:	Pointer to mask of KDBUS_ATTACH_* flags to export
+ * @sz:		Pointer to return the size needed by the metadata
+ *
+ * Does a conservative calculation of how much space metadata information
+ * will take up during export. It is 'conservative' because for string
+ * translations in namespaces, it will use the kernel namespaces, which is
+ * the longest possible version.
+ *
+ * The actual size consumed by kdbus_meta_export() may hence vary from the
+ * one reported here, but it is guaranteed never to be greater.
+ *
+ * Return: 0 on success, negative error number otherwise.
+ */
+int kdbus_meta_export_prepare(struct kdbus_meta_proc *mp,
+			      struct kdbus_meta_conn *mc,
+			      u64 *mask, size_t *sz)
+{
+	char *exe_pathname = NULL;
+	void *exe_page = NULL;
+	size_t size = 0;
+	u64 valid = 0;
+	int ret = 0;
+
+	if (mp) {
+		mutex_lock(&mp->lock);
+		valid |= mp->valid;
+		mutex_unlock(&mp->lock);
+	}
+
+	if (mc) {
+		mutex_lock(&mc->lock);
+		valid |= mc->valid;
+		mutex_unlock(&mc->lock);
+	}
+
+	*mask &= valid;
+	*mask &= kdbus_meta_attach_mask;
+
+	if (!*mask)
+		goto exit;
+
+	/* process metadata */
+
+	if (mp && (*mask & KDBUS_ATTACH_CREDS))
+		size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_creds));
+
+	if (mp && (*mask & KDBUS_ATTACH_PIDS))
+		size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_pids));
+
+	if (mp && (*mask & KDBUS_ATTACH_AUXGROUPS))
+		size += KDBUS_ITEM_SIZE(mp->n_auxgrps * sizeof(u64));
+
+	if (mp && (*mask & KDBUS_ATTACH_TID_COMM))
+		size += KDBUS_ITEM_SIZE(strlen(mp->tid_comm) + 1);
+
+	if (mp && (*mask & KDBUS_ATTACH_PID_COMM))
+		size += KDBUS_ITEM_SIZE(strlen(mp->pid_comm) + 1);
+
+	if (mp && (*mask & KDBUS_ATTACH_EXE)) {
+		exe_page = (void *)__get_free_page(GFP_TEMPORARY);
+		if (!exe_page) {
+			ret = -ENOMEM;
+			goto exit;
+		}
+
+		exe_pathname = d_path(&mp->exe_path, exe_page, PAGE_SIZE);
+		if (IS_ERR(exe_pathname)) {
+			ret = PTR_ERR(exe_pathname);
+			goto exit;
+		}
+
+		size += KDBUS_ITEM_SIZE(strlen(exe_pathname) + 1);
+		free_page((unsigned long)exe_page);
+	}
+
+	if (mp && (*mask & KDBUS_ATTACH_CMDLINE))
+		size += KDBUS_ITEM_SIZE(strlen(mp->cmdline) + 1);
+
+	if (mp && (*mask & KDBUS_ATTACH_CGROUP))
+		size += KDBUS_ITEM_SIZE(strlen(mp->cgroup) + 1);
+
+	if (mp && (*mask & KDBUS_ATTACH_CAPS))
+		size += KDBUS_ITEM_SIZE(sizeof(mp->caps));
+
+	if (mp && (*mask & KDBUS_ATTACH_SECLABEL))
+		size += KDBUS_ITEM_SIZE(strlen(mp->seclabel) + 1);
+
+	if (mp && (*mask & KDBUS_ATTACH_AUDIT))
+		size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_audit));
+
+	/* connection metadata */
+
+	if (mc && (*mask & KDBUS_ATTACH_NAMES))
+		size += mc->owned_names_size;
+
+	if (mc && (*mask & KDBUS_ATTACH_CONN_DESCRIPTION))
+		size += KDBUS_ITEM_SIZE(strlen(mc->conn_description) + 1);
+
+	if (mc && (*mask & KDBUS_ATTACH_TIMESTAMP))
+		size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_timestamp));
+
+exit:
+	*sz = size;
+
+	return ret;
+}
+
+static int kdbus_meta_push_kvec(struct kvec *kvec,
+				struct kdbus_item_header *hdr,
+				u64 type, void *payload,
+				size_t payload_size, u64 *size)
+{
+	hdr->type = type;
+	hdr->size = KDBUS_ITEM_HEADER_SIZE + payload_size;
+	kdbus_kvec_set(kvec++, hdr, sizeof(*hdr), size);
+	kdbus_kvec_set(kvec++, payload, payload_size, size);
+	return 2 + !!kdbus_kvec_pad(kvec++, size);
+}
+
+/* This is equivalent to from_kuid_munged(), but maps INVALID_UID to itself */
+static uid_t kdbus_from_kuid_keep(kuid_t uid)
+{
+	return uid_valid(uid) ?
+		from_kuid_munged(current_user_ns(), uid) : ((uid_t)-1);
+}
+
+/* This is equivalent to from_kgid_munged(), but maps INVALID_GID to itself */
+static gid_t kdbus_from_kgid_keep(kgid_t gid)
+{
+	return gid_valid(gid) ?
+		from_kgid_munged(current_user_ns(), gid) : ((gid_t)-1);
+}
+
+/**
+ * kdbus_meta_export() - export information from metadata into a slice
+ * @mp:		Process metadata, or NULL
+ * @mc:		Connection metadata, or NULL
+ * @mask:	Mask of KDBUS_ATTACH_* flags to export
+ * @slice:	The slice to export to
+ * @offset:	The offset inside @slice to write to
+ * @real_size:	The real size the metadata consumed
+ *
+ * This function exports information from metadata into @slice at offset
+ * @offset inside that slice. Only information that is requested in @mask
+ * and that has been collected before is exported.
+ *
+ * In order to make sure not to write out of bounds, @mask must be the same
+ * value that was previously returned from kdbus_meta_export_prepare(). The
+ * function will, however, not necessarily write as many bytes as returned by
+ * kdbus_meta_export_prepare(); depending on the namespaces in question, it
+ * might use up less than that.
+ *
+ * All information will be translated using the current namespaces.
+ *
+ * Return: 0 on success, negative error number otherwise.
+ */
+int kdbus_meta_export(struct kdbus_meta_proc *mp,
+		      struct kdbus_meta_conn *mc,
+		      u64 mask,
+		      struct kdbus_pool_slice *slice,
+		      off_t offset,
+		      size_t *real_size)
+{
+	struct user_namespace *user_ns = current_user_ns();
+	struct kdbus_item_header item_hdr[13], *hdr;
+	char *exe_pathname = NULL;
+	struct kdbus_creds creds;
+	struct kdbus_pids pids;
+	void *exe_page = NULL;
+	struct kvec kvec[40];
+	u64 *auxgrps = NULL;
+	size_t cnt = 0;
+	u64 size = 0;
+	int ret = 0;
+
+	hdr = &item_hdr[0];
+
+	/*
+	 * TODO: We currently have no sane way of translating a set of caps
+	 * between different user namespaces. Until that changes, we have
+	 * to drop such items.
+	 */
+	if (mp && mp->caps_namespace != user_ns)
+		mask &= ~KDBUS_ATTACH_CAPS;
+
+	if (mask == 0) {
+		*real_size = 0;
+		return 0;
+	}
+
+	/* process metadata */
+
+	if (mp && (mask & KDBUS_ATTACH_CREDS)) {
+		creds.uid	= kdbus_from_kuid_keep(mp->uid);
+		creds.euid	= kdbus_from_kuid_keep(mp->euid);
+		creds.suid	= kdbus_from_kuid_keep(mp->suid);
+		creds.fsuid	= kdbus_from_kuid_keep(mp->fsuid);
+		creds.gid	= kdbus_from_kgid_keep(mp->gid);
+		creds.egid	= kdbus_from_kgid_keep(mp->egid);
+		creds.sgid	= kdbus_from_kgid_keep(mp->sgid);
+		creds.fsgid	= kdbus_from_kgid_keep(mp->fsgid);
+
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_CREDS,
+					    &creds, sizeof(creds), &size);
+	}
+
+	if (mp && (mask & KDBUS_ATTACH_PIDS)) {
+		pids.pid = pid_vnr(mp->tgid);
+		pids.tid = pid_vnr(mp->pid);
+		pids.ppid = pid_vnr(mp->ppid);
+
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_PIDS,
+					    &pids, sizeof(pids), &size);
+	}
+
+	if (mp && (mask & KDBUS_ATTACH_AUXGROUPS)) {
+		size_t payload_size = mp->n_auxgrps * sizeof(u64);
+		int i;
+
+		auxgrps = kmalloc(payload_size, GFP_KERNEL);
+		if (!auxgrps) {
+			ret = -ENOMEM;
+			goto exit;
+		}
+
+		for (i = 0; i < mp->n_auxgrps; i++)
+			auxgrps[i] = from_kgid_munged(user_ns, mp->auxgrps[i]);
+
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
+					    KDBUS_ITEM_AUXGROUPS,
+					    auxgrps, payload_size, &size);
+	}
+
+	if (mp && (mask & KDBUS_ATTACH_TID_COMM))
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
+					    KDBUS_ITEM_TID_COMM, mp->tid_comm,
+					    strlen(mp->tid_comm) + 1, &size);
+
+	if (mp && (mask & KDBUS_ATTACH_PID_COMM))
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
+					    KDBUS_ITEM_PID_COMM, mp->pid_comm,
+					    strlen(mp->pid_comm) + 1, &size);
+
+	if (mp && (mask & KDBUS_ATTACH_EXE)) {
+		struct path p;
+
+		/*
+		 * TODO: We need access to __d_path() so we can write the path
+		 * relative to conn->root_path. Once upstream, we need
+		 * EXPORT_SYMBOL(__d_path) or an equivalent of d_path() that
+		 * takes the root path directly. Until then, we drop this item
+		 * if the root-paths differ.
+		 */
+
+		get_fs_root(current->fs, &p);
+		if (path_equal(&p, &mp->root_path)) {
+			exe_page = (void *)__get_free_page(GFP_TEMPORARY);
+			if (!exe_page) {
+				path_put(&p);
+				ret = -ENOMEM;
+				goto exit;
+			}
+
+			exe_pathname = d_path(&mp->exe_path, exe_page,
+					      PAGE_SIZE);
+			if (IS_ERR(exe_pathname)) {
+				path_put(&p);
+				ret = PTR_ERR(exe_pathname);
+				goto exit;
+			}
+
+			cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
+						    KDBUS_ITEM_EXE,
+						    exe_pathname,
+						    strlen(exe_pathname) + 1,
+						    &size);
+		}
+		path_put(&p);
+	}
+
+	if (mp && (mask & KDBUS_ATTACH_CMDLINE))
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
+					    KDBUS_ITEM_CMDLINE, mp->cmdline,
+					    strlen(mp->cmdline) + 1, &size);
+
+	if (mp && (mask & KDBUS_ATTACH_CGROUP))
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
+					    KDBUS_ITEM_CGROUP, mp->cgroup,
+					    strlen(mp->cgroup) + 1, &size);
+
+	if (mp && (mask & KDBUS_ATTACH_CAPS))
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
+					    KDBUS_ITEM_CAPS, &mp->caps,
+					    sizeof(mp->caps), &size);
+
+	if (mp && (mask & KDBUS_ATTACH_SECLABEL))
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
+					    KDBUS_ITEM_SECLABEL, mp->seclabel,
+					    strlen(mp->seclabel) + 1, &size);
+
+	if (mp && (mask & KDBUS_ATTACH_AUDIT)) {
+		struct kdbus_audit a = {
+			.loginuid = from_kuid(user_ns, mp->audit_loginuid),
+			.sessionid = mp->audit_sessionid,
+		};
+
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++, KDBUS_ITEM_AUDIT,
+					    &a, sizeof(a), &size);
+	}
+
+	/* connection metadata */
+
+	if (mc && (mask & KDBUS_ATTACH_NAMES))
+		kdbus_kvec_set(&kvec[cnt++], mc->owned_names_items,
+			       mc->owned_names_size, &size);
+
+	if (mc && (mask & KDBUS_ATTACH_CONN_DESCRIPTION))
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
+					    KDBUS_ITEM_CONN_DESCRIPTION,
+					    mc->conn_description,
+					    strlen(mc->conn_description) + 1,
+					    &size);
+
+	if (mc && (mask & KDBUS_ATTACH_TIMESTAMP))
+		cnt += kdbus_meta_push_kvec(kvec + cnt, hdr++,
+					    KDBUS_ITEM_TIMESTAMP, &mc->ts,
+					    sizeof(mc->ts), &size);
+
+	ret = kdbus_pool_slice_copy_kvec(slice, offset, kvec, cnt, size);
+	*real_size = size;
+
+exit:
+	kfree(auxgrps);
+
+	if (exe_page)
+		free_page((unsigned long)exe_page);
+
+	return ret;
+}
+
+/**
+ * kdbus_meta_calc_attach_flags() - calculate attach flags for a sender
+ *				    and a receiver
+ * @sender:		Sending connection
+ * @receiver:		Receiving connection
+ *
+ * Return: the attach flags both the sender and the receiver have opted-in
+ * for.
+ */
+u64 kdbus_meta_calc_attach_flags(const struct kdbus_conn *sender,
+				 const struct kdbus_conn *receiver)
+{
+	return atomic64_read(&sender->attach_flags_send) &
+	       atomic64_read(&receiver->attach_flags_recv);
+}
ipc/kdbus/metadata.h new file +57 -0

57 lines not on this page · raw message

Signature · 1 line
2.3.1
#8 lore raw

PATCH v4 8/14 kdbus: add code for notifications and matches

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

This patch adds code for matches and notifications.

Notifications are broadcast messages generated by the kernel, which notify subscribes when connections are created or destroyed, when well-known-names have been claimed, released or changed ownership, or when reply messages have timed out.

Matches are used to tell the kernel driver which broadcast messages a connection is interested in. Matches can either be specific on one of the kernel-generated notification types, or carry a bloom filter mask to match against a message from userspace. The latter is a way to pre-filter messages from other connections in order to mitigate unnecessary wakeups.

---
 ipc/kdbus/match.c  | 559 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 ipc/kdbus/match.h  |  35 ++++
 ipc/kdbus/notify.c | 248 ++++++++++++++++++++++++
 ipc/kdbus/notify.h |  30 +++
 4 files changed, 872 insertions(+)
 create mode 100644 ipc/kdbus/match.c
 create mode 100644 ipc/kdbus/match.h
 create mode 100644 ipc/kdbus/notify.c
 create mode 100644 ipc/kdbus/notify.h
ipc/kdbus/match.c new file +559 -0
new file mode 100644
index 000000000000..30cec1ca819f
@@ -0,0 +1,559 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2013-2015 Daniel Mack <[email protected]>
+ * Copyright (C) 2013-2015 David Herrmann <[email protected]>
+ * Copyright (C) 2013-2015 Linux Foundation
+ * Copyright (C) 2014-2015 Djalal Harouni <[email protected]>
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/fs.h>
+#include <linux/hash.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "endpoint.h"
+#include "handle.h"
+#include "item.h"
+#include "match.h"
+#include "message.h"
+#include "names.h"
+
+/**
+ * struct kdbus_match_db - message filters
+ * @entries_list:	List of matches
+ * @mdb_rwlock:		Match data lock
+ * @entries_count:	Number of entries in database
+ */
+struct kdbus_match_db {
+	struct list_head entries_list;
+	struct rw_semaphore mdb_rwlock;
+	unsigned int entries_count;
+};
+
+/**
+ * struct kdbus_match_entry - a match database entry
+ * @cookie:		User-supplied cookie to lookup the entry
+ * @list_entry:		The list entry element for the db list
+ * @rules_list:		The list head for tracking rules of this entry
+ */
+struct kdbus_match_entry {
+	u64 cookie;
+	struct list_head list_entry;
+	struct list_head rules_list;
+};
+
+/**
+ * struct kdbus_bloom_mask - mask to match against filter
+ * @generations:	Number of generations carried
+ * @data:		Array of bloom bit fields
+ */
+struct kdbus_bloom_mask {
+	u64 generations;
+	u64 *data;
+};
+
+/**
+ * struct kdbus_match_rule - a rule appended to a match entry
+ * @type:		An item type to match agains
+ * @bloom_mask:		Bloom mask to match a message's filter against, used
+ *			with KDBUS_ITEM_BLOOM_MASK
+ * @name:		Name to match against, used with KDBUS_ITEM_NAME,
+ *			KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE}
+ * @old_id:		ID to match against, used with
+ *			KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE},
+ *			KDBUS_ITEM_ID_REMOVE
+ * @new_id:		ID to match against, used with
+ *			KDBUS_ITEM_NAME_{ADD,REMOVE,CHANGE},
+ *			KDBUS_ITEM_ID_REMOVE
+ * @src_id:		ID to match against, used with KDBUS_ITEM_ID
+ * @rules_entry:	Entry in the entry's rules list
+ */
+struct kdbus_match_rule {
+	u64 type;
+	union {
+		struct kdbus_bloom_mask bloom_mask;
+		struct {
+			char *name;
+			u64 old_id;
+			u64 new_id;
+		};
+		u64 src_id;
+	};
+	struct list_head rules_entry;
+};
+
+static void kdbus_match_rule_free(struct kdbus_match_rule *rule)
+{
+	if (!rule)
+		return;
+
+	switch (rule->type) {
+	case KDBUS_ITEM_BLOOM_MASK:
+		kfree(rule->bloom_mask.data);
+		break;
+
+	case KDBUS_ITEM_NAME:
+	case KDBUS_ITEM_NAME_ADD:
+	case KDBUS_ITEM_NAME_REMOVE:
+	case KDBUS_ITEM_NAME_CHANGE:
+		kfree(rule->name);
+		break;
+
+	case KDBUS_ITEM_ID:
+	case KDBUS_ITEM_ID_ADD:
+	case KDBUS_ITEM_ID_REMOVE:
+		break;
+
+	default:
+		BUG();
+	}
+
+	list_del(&rule->rules_entry);
+	kfree(rule);
+}
+
+static void kdbus_match_entry_free(struct kdbus_match_entry *entry)
+{
+	struct kdbus_match_rule *r, *tmp;
+
+	if (!entry)
+		return;
+
+	list_for_each_entry_safe(r, tmp, &entry->rules_list, rules_entry)
+		kdbus_match_rule_free(r);
+
+	list_del(&entry->list_entry);
+	kfree(entry);
+}
+
+/**
+ * kdbus_match_db_free() - free match db resources
+ * @mdb:		The match database
+ */
+void kdbus_match_db_free(struct kdbus_match_db *mdb)
+{
+	struct kdbus_match_entry *entry, *tmp;
+
+	if (!mdb)
+		return;
+
+	list_for_each_entry_safe(entry, tmp, &mdb->entries_list, list_entry)
+		kdbus_match_entry_free(entry);
+
+	kfree(mdb);
+}
+
+/**
+ * kdbus_match_db_new() - create a new match database
+ *
+ * Return: a new kdbus_match_db on success, ERR_PTR on failure.
+ */
+struct kdbus_match_db *kdbus_match_db_new(void)
+{
+	struct kdbus_match_db *d;
+
+	d = kzalloc(sizeof(*d), GFP_KERNEL);
+	if (!d)
+		return ERR_PTR(-ENOMEM);
+
+	init_rwsem(&d->mdb_rwlock);
+	INIT_LIST_HEAD(&d->entries_list);
+
+	return d;
+}
+
+static bool kdbus_match_bloom(const struct kdbus_bloom_filter *filter,
+			      const struct kdbus_bloom_mask *mask,
+			      const struct kdbus_conn *conn)
+{
+	size_t n = conn->ep->bus->bloom.size / sizeof(u64);
+	const u64 *m;
+	size_t i;
+
+	/*
+	 * The message's filter carries a generation identifier, the
+	 * match's mask possibly carries an array of multiple generations
+	 * of the mask. Select the mask with the closest match of the
+	 * filter's generation.
+	 */
+	m = mask->data + (min(filter->generation, mask->generations - 1) * n);
+
+	/*
+	 * The message's filter contains the messages properties,
+	 * the match's mask contains the properties to look for in the
+	 * message. Check the mask bit field against the filter bit field,
+	 * if the message possibly carries the properties the connection
+	 * has subscribed to.
+	 */
+	for (i = 0; i < n; i++)
+		if ((filter->data[i] & m[i]) != m[i])
+			return false;
+
+	return true;
+}
+
+static bool kdbus_match_rules(const struct kdbus_match_entry *entry,
+			      struct kdbus_conn *conn_src,
+			      struct kdbus_kmsg *kmsg)
+{
+	struct kdbus_match_rule *r;
+
+	if (conn_src)
+		lockdep_assert_held(&conn_src->ep->bus->name_registry->rwlock);
+
+	/*
+	 * Walk all the rules and bail out immediately
+	 * if any of them is unsatisfied.
+	 */
+
+	list_for_each_entry(r, &entry->rules_list, rules_entry) {
+		if (conn_src) {
+			/* messages from userspace */
+
+			switch (r->type) {
+			case KDBUS_ITEM_BLOOM_MASK:
+				if (!kdbus_match_bloom(kmsg->bloom_filter,
+						       &r->bloom_mask,
+						       conn_src))
+					return false;
+				break;
+
+			case KDBUS_ITEM_ID:
+				if (r->src_id != conn_src->id &&
+				    r->src_id != KDBUS_MATCH_ID_ANY)
+					return false;
+
+				break;
+
+			case KDBUS_ITEM_NAME:
+				if (!kdbus_conn_has_name(conn_src, r->name))
+					return false;
+
+				break;
+
+			default:
+				return false;
+			}
+		} else {
+			/* kernel notifications */
+
+			if (kmsg->notify_type != r->type)
+				return false;
+
+			switch (r->type) {
+			case KDBUS_ITEM_ID_ADD:
+				if (r->new_id != KDBUS_MATCH_ID_ANY &&
+				    r->new_id != kmsg->notify_new_id)
+					return false;
+
+				break;
+
+			case KDBUS_ITEM_ID_REMOVE:
+				if (r->old_id != KDBUS_MATCH_ID_ANY &&
+				    r->old_id != kmsg->notify_old_id)
+					return false;
+
+				break;
+
+			case KDBUS_ITEM_NAME_ADD:
+			case KDBUS_ITEM_NAME_CHANGE:
+			case KDBUS_ITEM_NAME_REMOVE:
+				if ((r->old_id != KDBUS_MATCH_ID_ANY &&
+				     r->old_id != kmsg->notify_old_id) ||
+				    (r->new_id != KDBUS_MATCH_ID_ANY &&
+				     r->new_id != kmsg->notify_new_id) ||
+				    (r->name && kmsg->notify_name &&
+				     strcmp(r->name, kmsg->notify_name) != 0))
+					return false;
+
+				break;
+
+			default:
+				return false;
+			}
+		}
+	}
+
+	return true;
+}
+
+/**
+ * kdbus_match_db_match_kmsg() - match a kmsg object agains the database entries
+ * @mdb:		The match database
+ * @conn_src:		The connection object originating the message
+ * @kmsg:		The kmsg to perform the match on
+ *
+ * This function will walk through all the database entries previously uploaded
+ * with kdbus_match_db_add(). As soon as any of them has an all-satisfied rule
+ * set, this function will return true.
+ *
+ * The caller must hold the registry lock of conn_src->ep->bus, in case conn_src
+ * is non-NULL.
+ *
+ * Return: true if there was a matching database entry, false otherwise.
+ */
+bool kdbus_match_db_match_kmsg(struct kdbus_match_db *mdb,
+			       struct kdbus_conn *conn_src,
+			       struct kdbus_kmsg *kmsg)
+{
+	struct kdbus_match_entry *entry;
+	bool matched = false;
+
+	down_read(&mdb->mdb_rwlock);
+	list_for_each_entry(entry, &mdb->entries_list, list_entry) {
+		matched = kdbus_match_rules(entry, conn_src, kmsg);
+		if (matched)
+			break;
+	}
+	up_read(&mdb->mdb_rwlock);
+
+	return matched;
+}
+
+static int kdbus_match_db_remove_unlocked(struct kdbus_match_db *mdb,
+					  u64 cookie)
+{
+	struct kdbus_match_entry *entry, *tmp;
+	bool found = false;
+
+	list_for_each_entry_safe(entry, tmp, &mdb->entries_list, list_entry)
+		if (entry->cookie == cookie) {
+			kdbus_match_entry_free(entry);
+			--mdb->entries_count;
+			found = true;
+		}
+
+	return found ? 0 : -EBADSLT;
+}
+
+/**
+ * kdbus_cmd_match_add() - handle KDBUS_CMD_MATCH_ADD
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * One call to this function (or one ioctl(KDBUS_CMD_MATCH_ADD), respectively,
+ * adds one new database entry with n rules attached to it. Each rule is
+ * described with an kdbus_item, and an entry is considered matching if all
+ * its rules are satisfied.
+ *
+ * The items attached to a kdbus_cmd_match struct have the following mapping:
+ *
+ * KDBUS_ITEM_BLOOM_MASK:	A bloom mask
+ * KDBUS_ITEM_NAME:		A connection's source name
+ * KDBUS_ITEM_ID:		A connection ID
+ * KDBUS_ITEM_NAME_ADD:
+ * KDBUS_ITEM_NAME_REMOVE:
+ * KDBUS_ITEM_NAME_CHANGE:	Well-known name changes, carry
+ *				kdbus_notify_name_change
+ * KDBUS_ITEM_ID_ADD:
+ * KDBUS_ITEM_ID_REMOVE:	Connection ID changes, carry
+ *				kdbus_notify_id_change
+ *
+ * For kdbus_notify_{id,name}_change structs, only the ID and name fields
+ * are looked at when adding an entry. The flags are unused.
+ *
+ * Also note that KDBUS_ITEM_BLOOM_MASK, KDBUS_ITEM_NAME and KDBUS_ITEM_ID
+ * are used to match messages from userspace, while the others apply to
+ * kernel-generated notifications.
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_match_db *mdb = conn->match_db;
+	struct kdbus_match_entry *entry = NULL;
+	struct kdbus_cmd_match *cmd;
+	struct kdbus_item *item;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+		{ .type = KDBUS_ITEM_BLOOM_MASK, .multiple = true },
+		{ .type = KDBUS_ITEM_NAME, .multiple = true },
+		{ .type = KDBUS_ITEM_ID, .multiple = true },
+		{ .type = KDBUS_ITEM_NAME_ADD, .multiple = true },
+		{ .type = KDBUS_ITEM_NAME_REMOVE, .multiple = true },
+		{ .type = KDBUS_ITEM_NAME_CHANGE, .multiple = true },
+		{ .type = KDBUS_ITEM_ID_ADD, .multiple = true },
+		{ .type = KDBUS_ITEM_ID_REMOVE, .multiple = true },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE |
+				 KDBUS_MATCH_REPLACE,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	if (!kdbus_conn_is_ordinary(conn))
+		return -EOPNOTSUPP;
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+	if (!entry) {
+		ret = -ENOMEM;
+		goto exit;
+	}
+
+	entry->cookie = cmd->cookie;
+	INIT_LIST_HEAD(&entry->list_entry);
+	INIT_LIST_HEAD(&entry->rules_list);
+
+	KDBUS_ITEMS_FOREACH(item, cmd->items, KDBUS_ITEMS_SIZE(cmd, items)) {
+		struct kdbus_match_rule *rule;
+		size_t size = item->size - offsetof(struct kdbus_item, data);
+
+		rule = kzalloc(sizeof(*rule), GFP_KERNEL);
+		if (!rule) {
+			ret = -ENOMEM;
+			goto exit;
+		}
+
+		rule->type = item->type;
+		INIT_LIST_HEAD(&rule->rules_entry);
+
+		switch (item->type) {
+		case KDBUS_ITEM_BLOOM_MASK: {
+			u64 bsize = conn->ep->bus->bloom.size;
+			u64 generations;
+			u64 remainder;
+
+			generations = div64_u64_rem(size, bsize, &remainder);
+			if (size < bsize || remainder > 0) {
+				ret = -EDOM;
+				break;
+			}
+
+			rule->bloom_mask.data = kmemdup(item->data,
+							size, GFP_KERNEL);
+			if (!rule->bloom_mask.data) {
+				ret = -ENOMEM;
+				break;
+			}
+
+			rule->bloom_mask.generations = generations;
+			break;
+		}
+
+		case KDBUS_ITEM_NAME:
+			if (!kdbus_name_is_valid(item->str, false)) {
+				ret = -EINVAL;
+				break;
+			}
+
+			rule->name = kstrdup(item->str, GFP_KERNEL);
+			if (!rule->name)
+				ret = -ENOMEM;
+
+			break;
+
+		case KDBUS_ITEM_ID:
+			rule->src_id = item->id;
+			break;
+
+		case KDBUS_ITEM_NAME_ADD:
+		case KDBUS_ITEM_NAME_REMOVE:
+		case KDBUS_ITEM_NAME_CHANGE:
+			rule->old_id = item->name_change.old_id.id;
+			rule->new_id = item->name_change.new_id.id;
+
+			if (size > sizeof(struct kdbus_notify_name_change)) {
+				rule->name = kstrdup(item->name_change.name,
+						     GFP_KERNEL);
+				if (!rule->name)
+					ret = -ENOMEM;
+			}
+
+			break;
+
+		case KDBUS_ITEM_ID_ADD:
+		case KDBUS_ITEM_ID_REMOVE:
+			if (item->type == KDBUS_ITEM_ID_ADD)
+				rule->new_id = item->id_change.id;
+			else
+				rule->old_id = item->id_change.id;
+
+			break;
+		}
+
+		if (ret < 0) {
+			kdbus_match_rule_free(rule);
+			goto exit;
+		}
+
+		list_add_tail(&rule->rules_entry, &entry->rules_list);
+	}
+
+	down_write(&mdb->mdb_rwlock);
+
+	/* Remove any entry that has the same cookie as the current one. */
+	if (cmd->flags & KDBUS_MATCH_REPLACE)
+		kdbus_match_db_remove_unlocked(mdb, entry->cookie);
+
+	/*
+	 * If the above removal caught any entry, there will be room for the
+	 * new one.
+	 */
+	if (++mdb->entries_count > KDBUS_MATCH_MAX) {
+		--mdb->entries_count;
+		ret = -EMFILE;
+	} else {
+		list_add_tail(&entry->list_entry, &mdb->entries_list);
+		entry = NULL;
+	}
+
+	up_write(&mdb->mdb_rwlock);
+
+exit:
+	kdbus_match_entry_free(entry);
+	return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_match_remove() - handle KDBUS_CMD_MATCH_REMOVE
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_match_remove(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_cmd_match *cmd;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	if (!kdbus_conn_is_ordinary(conn))
+		return -EOPNOTSUPP;
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	down_write(&conn->match_db->mdb_rwlock);
+	ret = kdbus_match_db_remove_unlocked(conn->match_db, cmd->cookie);
+	up_write(&conn->match_db->mdb_rwlock);
+
+	return kdbus_args_clear(&args, ret);
+}
ipc/kdbus/match.h new file +35 -0
new file mode 100644
index 000000000000..ea4292938deb
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2013-2015 Daniel Mack <[email protected]>
+ * Copyright (C) 2013-2015 David Herrmann <[email protected]>
+ * Copyright (C) 2013-2015 Linux Foundation
+ * Copyright (C) 2014-2015 Djalal Harouni <[email protected]>
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef __KDBUS_MATCH_H
+#define __KDBUS_MATCH_H
+
+struct kdbus_conn;
+struct kdbus_kmsg;
+struct kdbus_match_db;
+
+struct kdbus_match_db *kdbus_match_db_new(void);
+void kdbus_match_db_free(struct kdbus_match_db *db);
+int kdbus_match_db_add(struct kdbus_conn *conn,
+		       struct kdbus_cmd_match *cmd);
+int kdbus_match_db_remove(struct kdbus_conn *conn,
+			  struct kdbus_cmd_match *cmd);
+bool kdbus_match_db_match_kmsg(struct kdbus_match_db *db,
+			       struct kdbus_conn *conn_src,
+			       struct kdbus_kmsg *kmsg);
+
+int kdbus_cmd_match_add(struct kdbus_conn *conn, void __user *argp);
+int kdbus_cmd_match_remove(struct kdbus_conn *conn, void __user *argp);
+
+#endif
ipc/kdbus/notify.c new file +248 -0

248 lines not on this page · raw message

ipc/kdbus/notify.h new file +30 -0
new file mode 100644
index 000000000000..03df464cb735
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2013-2015 Daniel Mack <[email protected]>
+ * Copyright (C) 2013-2015 David Herrmann <[email protected]>
+ * Copyright (C) 2013-2015 Linux Foundation
+ * Copyright (C) 2014-2015 Djalal Harouni <[email protected]>
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#ifndef __KDBUS_NOTIFY_H
+#define __KDBUS_NOTIFY_H
+
+struct kdbus_bus;
+
+int kdbus_notify_id_change(struct kdbus_bus *bus, u64 type, u64 id, u64 flags);
+int kdbus_notify_reply_timeout(struct kdbus_bus *bus, u64 id, u64 cookie);
+int kdbus_notify_reply_dead(struct kdbus_bus *bus, u64 id, u64 cookie);
+int kdbus_notify_name_change(struct kdbus_bus *bus, u64 type,
+			     u64 old_id, u64 new_id,
+			     u64 old_flags, u64 new_flags,
+			     const char *name);
+void kdbus_notify_flush(struct kdbus_bus *bus);
+void kdbus_notify_free(struct kdbus_bus *bus);
+
+#endif
Signature · 1 line
2.3.1
#9 lore raw

PATCH v4 9/14 kdbus: add code for buses, domains and endpoints

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

Add the logic to handle the following entities:

Domain:
A domain is an unamed object containing a number of buses. A domain is automatically created when an instance of kdbusfs is mounted, and destroyed when it is unmounted. Every domain offers its own 'control' device node to create buses. Domains are isolated from each other.

Bus:
A bus is a named object inside a domain. Clients exchange messages over a bus. Multiple buses themselves have no connection to each other; messages can only be exchanged on the same bus. The default entry point to a bus, where clients establish the connection to, is the "bus" device node /sys/fs/kdbus/<bus name>/bus. Common operating system setups create one "system bus" per system, and one "user bus" for every logged-in user. Applications or services may create their own private named buses.

Endpoint:
An endpoint provides the device node to talk to a bus. Opening an endpoint creates a new connection to the bus to which the endpoint belongs. Every bus has a default endpoint called "bus". A bus can optionally offer additional endpoints with custom names to provide a restricted access to the same bus. Custom endpoints carry additional policy which can be used to give sandboxed processes only a locked-down, limited, filtered access to the same bus.

See kdbus(7), kdbus.bus(7), kdbus.endpoint(7) and kdbus.fs(7) for more details.

---
 ipc/kdbus/bus.c      | 560 +++++++++++++++++++++++++++++++++++++++++++++++++++
 ipc/kdbus/bus.h      | 101 ++++++++++
 ipc/kdbus/domain.c   | 296 +++++++++++++++++++++++++++
 ipc/kdbus/domain.h   |  77 +++++++
 ipc/kdbus/endpoint.c | 275 +++++++++++++++++++++++++
 ipc/kdbus/endpoint.h |  67 ++++++
 6 files changed, 1376 insertions(+)
 create mode 100644 ipc/kdbus/bus.c
 create mode 100644 ipc/kdbus/bus.h
 create mode 100644 ipc/kdbus/domain.c
 create mode 100644 ipc/kdbus/domain.h
 create mode 100644 ipc/kdbus/endpoint.c
 create mode 100644 ipc/kdbus/endpoint.h
ipc/kdbus/bus.c new file +560 -0
new file mode 100644
index 000000000000..9d0679eb59f6
@@ -0,0 +1,560 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2013-2015 Daniel Mack <[email protected]>
+ * Copyright (C) 2013-2015 David Herrmann <[email protected]>
+ * Copyright (C) 2013-2015 Linux Foundation
+ * Copyright (C) 2014-2015 Djalal Harouni
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/fs.h>
+#include <linux/hashtable.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+
+#include "bus.h"
+#include "notify.h"
+#include "connection.h"
+#include "domain.h"
+#include "endpoint.h"
+#include "handle.h"
+#include "item.h"
+#include "match.h"
+#include "message.h"
+#include "metadata.h"
+#include "names.h"
+#include "policy.h"
+#include "util.h"
+
+static void kdbus_bus_free(struct kdbus_node *node)
+{
+	struct kdbus_bus *bus = container_of(node, struct kdbus_bus, node);
+
+	WARN_ON(!list_empty(&bus->monitors_list));
+	WARN_ON(!hash_empty(bus->conn_hash));
+
+	kdbus_notify_free(bus);
+
+	kdbus_user_unref(bus->creator);
+	kdbus_name_registry_free(bus->name_registry);
+	kdbus_domain_unref(bus->domain);
+	kdbus_policy_db_clear(&bus->policy_db);
+	kdbus_meta_proc_unref(bus->creator_meta);
+	kfree(bus);
+}
+
+static void kdbus_bus_release(struct kdbus_node *node, bool was_active)
+{
+	struct kdbus_bus *bus = container_of(node, struct kdbus_bus, node);
+
+	if (was_active)
+		atomic_dec(&bus->creator->buses);
+}
+
+static struct kdbus_bus *kdbus_bus_new(struct kdbus_domain *domain,
+				       const char *name,
+				       struct kdbus_bloom_parameter *bloom,
+				       const u64 *pattach_owner,
+				       const u64 *pattach_recv,
+				       u64 flags, kuid_t uid, kgid_t gid)
+{
+	struct kdbus_bus *b;
+	u64 attach_owner;
+	u64 attach_recv;
+	int ret;
+
+	if (bloom->size < 8 || bloom->size > KDBUS_BUS_BLOOM_MAX_SIZE ||
+	    !KDBUS_IS_ALIGNED8(bloom->size) || bloom->n_hash < 1)
+		return ERR_PTR(-EINVAL);
+
+	ret = kdbus_sanitize_attach_flags(pattach_recv ? *pattach_recv : 0,
+					  &attach_recv);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	ret = kdbus_sanitize_attach_flags(pattach_owner ? *pattach_owner : 0,
+					  &attach_owner);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	ret = kdbus_verify_uid_prefix(name, domain->user_namespace, uid);
+	if (ret < 0)
+		return ERR_PTR(ret);
+
+	b = kzalloc(sizeof(*b), GFP_KERNEL);
+	if (!b)
+		return ERR_PTR(-ENOMEM);
+
+	kdbus_node_init(&b->node, KDBUS_NODE_BUS);
+
+	b->node.free_cb = kdbus_bus_free;
+	b->node.release_cb = kdbus_bus_release;
+	b->node.uid = uid;
+	b->node.gid = gid;
+	b->node.mode = S_IRUSR | S_IXUSR;
+
+	if (flags & (KDBUS_MAKE_ACCESS_GROUP | KDBUS_MAKE_ACCESS_WORLD))
+		b->node.mode |= S_IRGRP | S_IXGRP;
+	if (flags & KDBUS_MAKE_ACCESS_WORLD)
+		b->node.mode |= S_IROTH | S_IXOTH;
+
+	b->id = atomic64_inc_return(&domain->last_id);
+	b->bus_flags = flags;
+	b->attach_flags_req = attach_recv;
+	b->attach_flags_owner = attach_owner;
+	generate_random_uuid(b->id128);
+	b->bloom = *bloom;
+	b->domain = kdbus_domain_ref(domain);
+
+	kdbus_policy_db_init(&b->policy_db);
+
+	init_rwsem(&b->conn_rwlock);
+	hash_init(b->conn_hash);
+	INIT_LIST_HEAD(&b->monitors_list);
+
+	INIT_LIST_HEAD(&b->notify_list);
+	spin_lock_init(&b->notify_lock);
+	mutex_init(&b->notify_flush_lock);
+
+	ret = kdbus_node_link(&b->node, &domain->node, name);
+	if (ret < 0)
+		goto exit_unref;
+
+	/* cache the metadata/credentials of the creator */
+	b->creator_meta = kdbus_meta_proc_new();
+	if (IS_ERR(b->creator_meta)) {
+		ret = PTR_ERR(b->creator_meta);
+		b->creator_meta = NULL;
+		goto exit_unref;
+	}
+
+	ret = kdbus_meta_proc_collect(b->creator_meta,
+				      KDBUS_ATTACH_CREDS |
+				      KDBUS_ATTACH_PIDS |
+				      KDBUS_ATTACH_AUXGROUPS |
+				      KDBUS_ATTACH_TID_COMM |
+				      KDBUS_ATTACH_PID_COMM |
+				      KDBUS_ATTACH_EXE |
+				      KDBUS_ATTACH_CMDLINE |
+				      KDBUS_ATTACH_CGROUP |
+				      KDBUS_ATTACH_CAPS |
+				      KDBUS_ATTACH_SECLABEL |
+				      KDBUS_ATTACH_AUDIT);
+	if (ret < 0)
+		goto exit_unref;
+
+	b->name_registry = kdbus_name_registry_new();
+	if (IS_ERR(b->name_registry)) {
+		ret = PTR_ERR(b->name_registry);
+		b->name_registry = NULL;
+		goto exit_unref;
+	}
+
+	/*
+	 * Bus-limits of the creator are accounted on its real UID, just like
+	 * all other per-user limits.
+	 */
+	b->creator = kdbus_user_lookup(domain, current_uid());
+	if (IS_ERR(b->creator)) {
+		ret = PTR_ERR(b->creator);
+		b->creator = NULL;
+		goto exit_unref;
+	}
+
+	return b;
+
+exit_unref:
+	kdbus_node_deactivate(&b->node);
+	kdbus_node_unref(&b->node);
+	return ERR_PTR(ret);
+}
+
+/**
+ * kdbus_bus_ref() - increase the reference counter of a kdbus_bus
+ * @bus:		The bus to reference
+ *
+ * Every user of a bus, except for its creator, must add a reference to the
+ * kdbus_bus using this function.
+ *
+ * Return: the bus itself
+ */
+struct kdbus_bus *kdbus_bus_ref(struct kdbus_bus *bus)
+{
+	if (bus)
+		kdbus_node_ref(&bus->node);
+	return bus;
+}
+
+/**
+ * kdbus_bus_unref() - decrease the reference counter of a kdbus_bus
+ * @bus:		The bus to unref
+ *
+ * Release a reference. If the reference count drops to 0, the bus will be
+ * freed.
+ *
+ * Return: NULL
+ */
+struct kdbus_bus *kdbus_bus_unref(struct kdbus_bus *bus)
+{
+	if (bus)
+		kdbus_node_unref(&bus->node);
+	return NULL;
+}
+
+/**
+ * kdbus_bus_find_conn_by_id() - find a connection with a given id
+ * @bus:		The bus to look for the connection
+ * @id:			The 64-bit connection id
+ *
+ * Looks up a connection with a given id. The returned connection
+ * is ref'ed, and needs to be unref'ed by the user. Returns NULL if
+ * the connection can't be found.
+ */
+struct kdbus_conn *kdbus_bus_find_conn_by_id(struct kdbus_bus *bus, u64 id)
+{
+	struct kdbus_conn *conn, *found = NULL;
+
+	down_read(&bus->conn_rwlock);
+	hash_for_each_possible(bus->conn_hash, conn, hentry, id)
+		if (conn->id == id) {
+			found = kdbus_conn_ref(conn);
+			break;
+		}
+	up_read(&bus->conn_rwlock);
+
+	return found;
+}
+
+/**
+ * kdbus_bus_broadcast() - send a message to all subscribed connections
+ * @bus:	The bus the connections are connected to
+ * @conn_src:	The source connection, may be %NULL for kernel notifications
+ * @kmsg:	The message to send.
+ *
+ * Send @kmsg to all connections that are currently active on the bus.
+ * Connections must still have matches installed in order to let the message
+ * pass.
+ *
+ * The caller must hold the name-registry lock of @bus.
+ */
+void kdbus_bus_broadcast(struct kdbus_bus *bus,
+			 struct kdbus_conn *conn_src,
+			 struct kdbus_kmsg *kmsg)
+{
+	struct kdbus_conn *conn_dst;
+	unsigned int i;
+	int ret;
+
+	lockdep_assert_held(&bus->name_registry->rwlock);
+
+	/*
+	 * Make sure broadcast are queued on monitors before we send it out to
+	 * anyone else. Otherwise, connections might react to broadcasts before
+	 * the monitor gets the broadcast queued. In the worst case, the
+	 * monitor sees a reaction to the broadcast before the broadcast itself.
+	 * We don't give ordering guarantees across connections (and monitors
+	 * can re-construct order via sequence numbers), but we should at least
+	 * try to avoid re-ordering for monitors.
+	 */
+	kdbus_bus_eavesdrop(bus, conn_src, kmsg);
+
+	down_read(&bus->conn_rwlock);
+	hash_for_each(bus->conn_hash, i, conn_dst, hentry) {
+		if (conn_dst->id == kmsg->msg.src_id)
+			continue;
+		if (!kdbus_conn_is_ordinary(conn_dst))
+			continue;
+
+		/*
+		 * Check if there is a match for the kmsg object in
+		 * the destination connection match db
+		 */
+		if (!kdbus_match_db_match_kmsg(conn_dst->match_db, conn_src,
+					       kmsg))
+			continue;
+
+		if (conn_src) {
+			u64 attach_flags;
+
+			/*
+			 * Anyone can send broadcasts, as they have no
+			 * destination. But a receiver needs TALK access to
+			 * the sender in order to receive broadcasts.
+			 */
+			if (!kdbus_conn_policy_talk(conn_dst, NULL, conn_src))
+				continue;
+
+			attach_flags = kdbus_meta_calc_attach_flags(conn_src,
+								    conn_dst);
+
+			/*
+			 * Keep sending messages even if we cannot acquire the
+			 * requested metadata. It's up to the receiver to drop
+			 * messages that lack expected metadata.
+			 */
+			if (!conn_src->faked_meta)
+				kdbus_meta_proc_collect(kmsg->proc_meta,
+							attach_flags);
+			kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, conn_src,
+						attach_flags);
+		} else {
+			/*
+			 * Check if there is a policy db that prevents the
+			 * destination connection from receiving this kernel
+			 * notification
+			 */
+			if (!kdbus_conn_policy_see_notification(conn_dst, NULL,
+								kmsg))
+				continue;
+		}
+
+		ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL);
+		if (ret < 0)
+			kdbus_conn_lost_message(conn_dst);
+	}
+	up_read(&bus->conn_rwlock);
+}
+
+/**
+ * kdbus_bus_eavesdrop() - send a message to all subscribed monitors
+ * @bus:	The bus the monitors are connected to
+ * @conn_src:	The source connection, may be %NULL for kernel notifications
+ * @kmsg:	The message to send.
+ *
+ * Send @kmsg to all monitors that are currently active on the bus. Monitors
+ * must still have matches installed in order to let the message pass.
+ *
+ * The caller must hold the name-registry lock of @bus.
+ */
+void kdbus_bus_eavesdrop(struct kdbus_bus *bus,
+			 struct kdbus_conn *conn_src,
+			 struct kdbus_kmsg *kmsg)
+{
+	struct kdbus_conn *conn_dst;
+	int ret;
+
+	/*
+	 * Monitor connections get all messages; ignore possible errors
+	 * when sending messages to monitor connections.
+	 */
+
+	lockdep_assert_held(&bus->name_registry->rwlock);
+
+	down_read(&bus->conn_rwlock);
+	list_for_each_entry(conn_dst, &bus->monitors_list, monitor_entry) {
+		/*
+		 * Collect metadata requested by the destination connection.
+		 * Ignore errors, as receivers need to check metadata
+		 * availability, anyway. So it's still better to send messages
+		 * that lack data, than to skip it entirely.
+		 */
+		if (conn_src) {
+			u64 attach_flags;
+
+			attach_flags = kdbus_meta_calc_attach_flags(conn_src,
+								    conn_dst);
+			if (!conn_src->faked_meta)
+				kdbus_meta_proc_collect(kmsg->proc_meta,
+							attach_flags);
+			kdbus_meta_conn_collect(kmsg->conn_meta, kmsg, conn_src,
+						attach_flags);
+		}
+
+		ret = kdbus_conn_entry_insert(conn_src, conn_dst, kmsg, NULL);
+		if (ret < 0)
+			kdbus_conn_lost_message(conn_dst);
+	}
+	up_read(&bus->conn_rwlock);
+}
+
+/**
+ * kdbus_cmd_bus_make() - handle KDBUS_CMD_BUS_MAKE
+ * @domain:		domain to operate on
+ * @argp:		command payload
+ *
+ * Return: Newly created bus on success, ERR_PTR on failure.
+ */
+struct kdbus_bus *kdbus_cmd_bus_make(struct kdbus_domain *domain,
+				     void __user *argp)
+{
+	struct kdbus_bus *bus = NULL;
+	struct kdbus_cmd *cmd;
+	struct kdbus_ep *ep = NULL;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+		{ .type = KDBUS_ITEM_MAKE_NAME, .mandatory = true },
+		{ .type = KDBUS_ITEM_BLOOM_PARAMETER, .mandatory = true },
+		{ .type = KDBUS_ITEM_ATTACH_FLAGS_SEND },
+		{ .type = KDBUS_ITEM_ATTACH_FLAGS_RECV },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE |
+				 KDBUS_MAKE_ACCESS_GROUP |
+				 KDBUS_MAKE_ACCESS_WORLD,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret < 0)
+		return ERR_PTR(ret);
+	if (ret > 0)
+		return NULL;
+
+	bus = kdbus_bus_new(domain,
+			    argv[1].item->str, &argv[2].item->bloom_parameter,
+			    argv[3].item ? argv[3].item->data64 : NULL,
+			    argv[4].item ? argv[4].item->data64 : NULL,
+			    cmd->flags, current_euid(), current_egid());
+	if (IS_ERR(bus)) {
+		ret = PTR_ERR(bus);
+		bus = NULL;
+		goto exit;
+	}
+
+	if (atomic_inc_return(&bus->creator->buses) > KDBUS_USER_MAX_BUSES) {
+		atomic_dec(&bus->creator->buses);
+		ret = -EMFILE;
+		goto exit;
+	}
+
+	if (!kdbus_node_activate(&bus->node)) {
+		atomic_dec(&bus->creator->buses);
+		ret = -ESHUTDOWN;
+		goto exit;
+	}
+
+	ep = kdbus_ep_new(bus, "bus", cmd->flags, bus->node.uid, bus->node.gid,
+			  false);
+	if (IS_ERR(ep)) {
+		ret = PTR_ERR(ep);
+		ep = NULL;
+		goto exit;
+	}
+
+	if (!kdbus_node_activate(&ep->node)) {
+		ret = -ESHUTDOWN;
+		goto exit;
+	}
+
+	/*
+	 * Drop our own reference, effectively causing the endpoint to be
+	 * deactivated and released when the parent bus is.
+	 */
+	ep = kdbus_ep_unref(ep);
+
+exit:
+	ret = kdbus_args_clear(&args, ret);
+	if (ret < 0) {
+		if (ep) {
+			kdbus_node_deactivate(&ep->node);
+			kdbus_ep_unref(ep);
+		}
+		if (bus) {
+			kdbus_node_deactivate(&bus->node);
+			kdbus_bus_unref(bus);
+		}
+		return ERR_PTR(ret);
+	}
+	return bus;
+}
+
+/**
+ * kdbus_cmd_bus_creator_info() - handle KDBUS_CMD_BUS_CREATOR_INFO
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_cmd_info *cmd;
+	struct kdbus_bus *bus = conn->ep->bus;
+	struct kdbus_pool_slice *slice = NULL;
+	struct kdbus_item_header item_hdr;
+	struct kdbus_info info = {};
+	size_t meta_size, name_len;
+	struct kvec kvec[5];
+	u64 hdr_size = 0;
+	u64 attach_flags;
+	size_t cnt = 0;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	ret = kdbus_sanitize_attach_flags(cmd->attach_flags, &attach_flags);
+	if (ret < 0)
+		goto exit;
+
+	attach_flags &= bus->attach_flags_owner;
+
+	ret = kdbus_meta_export_prepare(bus->creator_meta, NULL,
+					&attach_flags, &meta_size);
+	if (ret < 0)
+		goto exit;
+
+	name_len = strlen(bus->node.name) + 1;
+	info.id = bus->id;
+	info.flags = bus->bus_flags;
+	item_hdr.type = KDBUS_ITEM_MAKE_NAME;
+	item_hdr.size = KDBUS_ITEM_HEADER_SIZE + name_len;
+
+	kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &hdr_size);
+	kdbus_kvec_set(&kvec[cnt++], &item_hdr, sizeof(item_hdr), &hdr_size);
+	kdbus_kvec_set(&kvec[cnt++], bus->node.name, name_len, &hdr_size);
+	cnt += !!kdbus_kvec_pad(&kvec[cnt], &hdr_size);
+
+	slice = kdbus_pool_slice_alloc(conn->pool, hdr_size + meta_size, false);
+	if (IS_ERR(slice)) {
+		ret = PTR_ERR(slice);
+		slice = NULL;
+		goto exit;
+	}
+
+	ret = kdbus_meta_export(bus->creator_meta, NULL, attach_flags,
+				slice, hdr_size, &meta_size);
+	if (ret < 0)
+		goto exit;
+
+	info.size = hdr_size + meta_size;
+
+	ret = kdbus_pool_slice_copy_kvec(slice, 0, kvec, cnt, hdr_size);
+	if (ret < 0)
+		goto exit;
+
+	kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->info_size);
+
+	if (kdbus_member_set_user(&cmd->offset, argp, typeof(*cmd), offset) ||
+	    kdbus_member_set_user(&cmd->info_size, argp,
+				  typeof(*cmd), info_size))
+		ret = -EFAULT;
+
+exit:
+	kdbus_pool_slice_release(slice);
+
+	return kdbus_args_clear(&args, ret);
+}
ipc/kdbus/bus.h new file +101 -0

101 lines not on this page · raw message

ipc/kdbus/domain.c new file +296 -0

296 lines not on this page · raw message

ipc/kdbus/domain.h new file +77 -0

77 lines not on this page · raw message

ipc/kdbus/endpoint.c new file +275 -0

275 lines not on this page · raw message

ipc/kdbus/endpoint.h new file +67 -0

67 lines not on this page · raw message

Signature · 1 line
2.3.1
#10 lore raw

PATCH v4 10/14 kdbus: add name registry implementation

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

This patch adds the name registry implementation.

Each bus instantiates a name registry to resolve well-known names into unique connection IDs for message delivery. The registry will be queried when a message is sent with kdbus_msg.dst_id set to KDBUS_DST_ID_NAME, or when a registry dump is requested.

It's important to have this registry implemented in the kernel to implement lookups and take-overs in a race-free way.

---
 ipc/kdbus/names.c | 772 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 ipc/kdbus/names.h |  74 ++++++
 2 files changed, 846 insertions(+)
 create mode 100644 ipc/kdbus/names.c
 create mode 100644 ipc/kdbus/names.h
ipc/kdbus/names.c new file +772 -0
new file mode 100644
index 000000000000..657008e1bb37
@@ -0,0 +1,772 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2013-2015 Daniel Mack <[email protected]>
+ * Copyright (C) 2013-2015 David Herrmann <[email protected]>
+ * Copyright (C) 2013-2015 Linux Foundation
+ * Copyright (C) 2014-2015 Djalal Harouni <tixxdz-Umm1ozX2/[email protected]>
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/ctype.h>
+#include <linux/fs.h>
+#include <linux/hash.h>
+#include <linux/idr.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rwsem.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/uio.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "endpoint.h"
+#include "handle.h"
+#include "item.h"
+#include "names.h"
+#include "notify.h"
+#include "policy.h"
+
+struct kdbus_name_pending {
+	u64 flags;
+	struct kdbus_conn *conn;
+	struct kdbus_name_entry *name;
+	struct list_head conn_entry;
+	struct list_head name_entry;
+};
+
+static int kdbus_name_pending_new(struct kdbus_name_entry *e,
+				  struct kdbus_conn *conn, u64 flags)
+{
+	struct kdbus_name_pending *p;
+
+	kdbus_conn_assert_active(conn);
+
+	p = kmalloc(sizeof(*p), GFP_KERNEL);
+	if (!p)
+		return -ENOMEM;
+
+	p->flags = flags;
+	p->conn = conn;
+	p->name = e;
+	list_add_tail(&p->conn_entry, &conn->names_queue_list);
+	list_add_tail(&p->name_entry, &e->queue);
+
+	return 0;
+}
+
+static void kdbus_name_pending_free(struct kdbus_name_pending *p)
+{
+	if (!p)
+		return;
+
+	list_del(&p->name_entry);
+	list_del(&p->conn_entry);
+	kfree(p);
+}
+
+static struct kdbus_name_entry *
+kdbus_name_entry_new(struct kdbus_name_registry *r, u32 hash, const char *name)
+{
+	struct kdbus_name_entry *e;
+	size_t namelen;
+
+	namelen = strlen(name);
+
+	e = kmalloc(sizeof(*e) + namelen + 1, GFP_KERNEL);
+	if (!e)
+		return ERR_PTR(-ENOMEM);
+
+	e->name_id = ++r->name_seq_last;
+	e->flags = 0;
+	e->conn = NULL;
+	e->activator = NULL;
+	INIT_LIST_HEAD(&e->queue);
+	INIT_LIST_HEAD(&e->conn_entry);
+	hash_add(r->entries_hash, &e->hentry, hash);
+	memcpy(e->name, name, namelen + 1);
+
+	return e;
+}
+
+static void kdbus_name_entry_free(struct kdbus_name_entry *e)
+{
+	if (!e)
+		return;
+
+	WARN_ON(!list_empty(&e->conn_entry));
+	WARN_ON(!list_empty(&e->queue));
+	WARN_ON(e->activator);
+	WARN_ON(e->conn);
+
+	hash_del(&e->hentry);
+	kfree(e);
+}
+
+static void kdbus_name_entry_set_owner(struct kdbus_name_entry *e,
+				       struct kdbus_conn *conn, u64 flags)
+{
+	WARN_ON(e->conn);
+
+	e->conn = kdbus_conn_ref(conn);
+	e->flags = flags;
+	atomic_inc(&conn->name_count);
+	list_add_tail(&e->conn_entry, &e->conn->names_list);
+}
+
+static void kdbus_name_entry_remove_owner(struct kdbus_name_entry *e)
+{
+	WARN_ON(!e->conn);
+
+	list_del_init(&e->conn_entry);
+	atomic_dec(&e->conn->name_count);
+	e->flags = 0;
+	e->conn = kdbus_conn_unref(e->conn);
+}
+
+static void kdbus_name_entry_replace_owner(struct kdbus_name_entry *e,
+					   struct kdbus_conn *conn, u64 flags)
+{
+	if (WARN_ON(!e->conn) || WARN_ON(conn == e->conn))
+		return;
+
+	kdbus_notify_name_change(conn->ep->bus, KDBUS_ITEM_NAME_CHANGE,
+				 e->conn->id, conn->id,
+				 e->flags, flags, e->name);
+	kdbus_name_entry_remove_owner(e);
+	kdbus_name_entry_set_owner(e, conn, flags);
+}
+
+/**
+ * kdbus_name_is_valid() - check if a name is valid
+ * @p:			The name to check
+ * @allow_wildcard:	Whether or not to allow a wildcard name
+ *
+ * A name is valid if all of the following criterias are met:
+ *
+ *  - The name has two or more elements separated by a period ('.') character.
+ *  - All elements must contain at least one character.
+ *  - Each element must only contain the ASCII characters "[A-Z][a-z][0-9]_-"
+ *    and must not begin with a digit.
+ *  - The name must not exceed KDBUS_NAME_MAX_LEN.
+ *  - If @allow_wildcard is true, the name may end on '.*'
+ */
+bool kdbus_name_is_valid(const char *p, bool allow_wildcard)
+{
+	bool dot, found_dot = false;
+	const char *q;
+
+	for (dot = true, q = p; *q; q++) {
+		if (*q == '.') {
+			if (dot)
+				return false;
+
+			found_dot = true;
+			dot = true;
+		} else {
+			bool good;
+
+			good = isalpha(*q) || (!dot && isdigit(*q)) ||
+				*q == '_' || *q == '-' ||
+				(allow_wildcard && dot &&
+					*q == '*' && *(q + 1) == '\0');
+
+			if (!good)
+				return false;
+
+			dot = false;
+		}
+	}
+
+	if (q - p > KDBUS_NAME_MAX_LEN)
+		return false;
+
+	if (dot)
+		return false;
+
+	if (!found_dot)
+		return false;
+
+	return true;
+}
+
+/**
+ * kdbus_name_registry_new() - create a new name registry
+ *
+ * Return: a new kdbus_name_registry on success, ERR_PTR on failure.
+ */
+struct kdbus_name_registry *kdbus_name_registry_new(void)
+{
+	struct kdbus_name_registry *r;
+
+	r = kmalloc(sizeof(*r), GFP_KERNEL);
+	if (!r)
+		return ERR_PTR(-ENOMEM);
+
+	hash_init(r->entries_hash);
+	init_rwsem(&r->rwlock);
+	r->name_seq_last = 0;
+
+	return r;
+}
+
+/**
+ * kdbus_name_registry_free() - drop a name reg's reference
+ * @reg:		The name registry, may be %NULL
+ *
+ * Cleanup the name registry's internal structures.
+ */
+void kdbus_name_registry_free(struct kdbus_name_registry *reg)
+{
+	if (!reg)
+		return;
+
+	WARN_ON(!hash_empty(reg->entries_hash));
+	kfree(reg);
+}
+
+static struct kdbus_name_entry *
+kdbus_name_find(struct kdbus_name_registry *reg, u32 hash, const char *name)
+{
+	struct kdbus_name_entry *e;
+
+	lockdep_assert_held(&reg->rwlock);
+
+	hash_for_each_possible(reg->entries_hash, e, hentry, hash)
+		if (strcmp(e->name, name) == 0)
+			return e;
+
+	return NULL;
+}
+
+/**
+ * kdbus_name_lookup_unlocked() - lookup name in registry
+ * @reg:		name registry
+ * @name:		name to lookup
+ *
+ * This looks up @name in the given name-registry and returns the
+ * kdbus_name_entry object. The caller must hold the registry-lock and must not
+ * access the returned object after releasing the lock.
+ *
+ * Return: Pointer to name-entry, or NULL if not found.
+ */
+struct kdbus_name_entry *
+kdbus_name_lookup_unlocked(struct kdbus_name_registry *reg, const char *name)
+{
+	return kdbus_name_find(reg, kdbus_strhash(name), name);
+}
+
+/**
+ * kdbus_name_acquire() - acquire a name
+ * @reg:		The name registry
+ * @conn:		The connection to pin this entry to
+ * @name:		The name to acquire
+ * @flags:		Acquisition flags (KDBUS_NAME_*)
+ * @return_flags:	Pointer to return flags for the acquired name
+ *			(KDBUS_NAME_*), may be %NULL
+ *
+ * Callers must ensure that @conn is either a privileged bus user or has
+ * sufficient privileges in the policy-db to own the well-known name @name.
+ *
+ * Return: 0 success, negative error number on failure.
+ */
+int kdbus_name_acquire(struct kdbus_name_registry *reg,
+		       struct kdbus_conn *conn, const char *name,
+		       u64 flags, u64 *return_flags)
+{
+	struct kdbus_name_entry *e;
+	u64 rflags = 0;
+	int ret = 0;
+	u32 hash;
+
+	kdbus_conn_assert_active(conn);
+
+	down_write(&reg->rwlock);
+
+	if (!kdbus_conn_policy_own_name(conn, current_cred(), name)) {
+		ret = -EPERM;
+		goto exit_unlock;
+	}
+
+	hash = kdbus_strhash(name);
+	e = kdbus_name_find(reg, hash, name);
+	if (!e) {
+		/* claim new name */
+
+		if (conn->activator_of) {
+			ret = -EINVAL;
+			goto exit_unlock;
+		}
+
+		e = kdbus_name_entry_new(reg, hash, name);
+		if (IS_ERR(e)) {
+			ret = PTR_ERR(e);
+			goto exit_unlock;
+		}
+
+		if (kdbus_conn_is_activator(conn)) {
+			e->activator = kdbus_conn_ref(conn);
+			conn->activator_of = e;
+		}
+
+		kdbus_name_entry_set_owner(e, conn, flags);
+		kdbus_notify_name_change(e->conn->ep->bus, KDBUS_ITEM_NAME_ADD,
+					 0, e->conn->id, 0, e->flags, e->name);
+	} else if (e->conn == conn || e == conn->activator_of) {
+		/* connection already owns that name */
+		ret = -EALREADY;
+	} else if (kdbus_conn_is_activator(conn)) {
+		/* activator claims existing name */
+
+		if (conn->activator_of) {
+			ret = -EINVAL; /* multiple names not allowed */
+		} else if (e->activator) {
+			ret = -EEXIST; /* only one activator per name */
+		} else {
+			e->activator = kdbus_conn_ref(conn);
+			conn->activator_of = e;
+		}
+	} else if (e->flags & KDBUS_NAME_ACTIVATOR) {
+		/* claim name of an activator */
+
+		kdbus_conn_move_messages(conn, e->activator, 0);
+		kdbus_name_entry_replace_owner(e, conn, flags);
+	} else if ((flags & KDBUS_NAME_REPLACE_EXISTING) &&
+		   (e->flags & KDBUS_NAME_ALLOW_REPLACEMENT)) {
+		/* claim name of a previous owner */
+
+		if (e->flags & KDBUS_NAME_QUEUE) {
+			/* move owner back to queue if they asked for it */
+			ret = kdbus_name_pending_new(e, e->conn, e->flags);
+			if (ret < 0)
+				goto exit_unlock;
+		}
+
+		kdbus_name_entry_replace_owner(e, conn, flags);
+	} else if (flags & KDBUS_NAME_QUEUE) {
+		/* add to waiting-queue of the name */
+
+		ret = kdbus_name_pending_new(e, conn, flags);
+		if (ret >= 0)
+			/* tell the caller that we queued it */
+			rflags |= KDBUS_NAME_IN_QUEUE;
+	} else {
+		/* the name is busy, return a failure */
+		ret = -EEXIST;
+	}
+
+	if (ret == 0 && return_flags)
+		*return_flags = rflags;
+
+exit_unlock:
+	up_write(&reg->rwlock);
+	kdbus_notify_flush(conn->ep->bus);
+	return ret;
+}
+
+static void kdbus_name_release_unlocked(struct kdbus_name_registry *reg,
+					struct kdbus_name_entry *e)
+{
+	struct kdbus_name_pending *p;
+
+	lockdep_assert_held(&reg->rwlock);
+
+	p = list_first_entry_or_null(&e->queue, struct kdbus_name_pending,
+				     name_entry);
+
+	if (p) {
+		/* give it to first active waiter in the queue */
+		kdbus_name_entry_replace_owner(e, p->conn, p->flags);
+		kdbus_name_pending_free(p);
+	} else if (e->activator && e->activator != e->conn) {
+		/* hand it back to an active activator connection */
+		kdbus_conn_move_messages(e->activator, e->conn, e->name_id);
+		kdbus_name_entry_replace_owner(e, e->activator,
+					       KDBUS_NAME_ACTIVATOR);
+	} else {
+		/* release the name */
+		kdbus_notify_name_change(e->conn->ep->bus,
+					 KDBUS_ITEM_NAME_REMOVE,
+					 e->conn->id, 0, e->flags, 0, e->name);
+		kdbus_name_entry_remove_owner(e);
+		kdbus_name_entry_free(e);
+	}
+}
+
+static int kdbus_name_release(struct kdbus_name_registry *reg,
+			      struct kdbus_conn *conn,
+			      const char *name)
+{
+	struct kdbus_name_pending *p;
+	struct kdbus_name_entry *e;
+	int ret = 0;
+
+	down_write(&reg->rwlock);
+	e = kdbus_name_find(reg, kdbus_strhash(name), name);
+	if (!e) {
+		ret = -ESRCH;
+	} else if (e->conn == conn) {
+		kdbus_name_release_unlocked(reg, e);
+	} else {
+		ret = -EADDRINUSE;
+		list_for_each_entry(p, &e->queue, name_entry) {
+			if (p->conn == conn) {
+				kdbus_name_pending_free(p);
+				ret = 0;
+				break;
+			}
+		}
+	}
+	up_write(&reg->rwlock);
+
+	kdbus_notify_flush(conn->ep->bus);
+	return ret;
+}
+
+/**
+ * kdbus_name_release_all() - remove all name entries of a given connection
+ * @reg:		name registry
+ * @conn:		connection
+ */
+void kdbus_name_release_all(struct kdbus_name_registry *reg,
+			    struct kdbus_conn *conn)
+{
+	struct kdbus_name_pending *p;
+	struct kdbus_conn *activator = NULL;
+	struct kdbus_name_entry *e;
+
+	down_write(&reg->rwlock);
+
+	if (kdbus_conn_is_activator(conn)) {
+		activator = conn->activator_of->activator;
+		conn->activator_of->activator = NULL;
+	}
+
+	while ((p = list_first_entry_or_null(&conn->names_queue_list,
+					     struct kdbus_name_pending,
+					     conn_entry)))
+		kdbus_name_pending_free(p);
+	while ((e = list_first_entry_or_null(&conn->names_list,
+					     struct kdbus_name_entry,
+					     conn_entry)))
+		kdbus_name_release_unlocked(reg, e);
+
+	up_write(&reg->rwlock);
+
+	kdbus_conn_unref(activator);
+	kdbus_notify_flush(conn->ep->bus);
+}
+
+/**
+ * kdbus_cmd_name_acquire() - handle KDBUS_CMD_NAME_ACQUIRE
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_name_acquire(struct kdbus_conn *conn, void __user *argp)
+{
+	const char *item_name;
+	struct kdbus_cmd *cmd;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+		{ .type = KDBUS_ITEM_NAME, .mandatory = true },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE |
+				 KDBUS_NAME_REPLACE_EXISTING |
+				 KDBUS_NAME_ALLOW_REPLACEMENT |
+				 KDBUS_NAME_QUEUE,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	if (!kdbus_conn_is_ordinary(conn))
+		return -EOPNOTSUPP;
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	item_name = argv[1].item->str;
+	if (!kdbus_name_is_valid(item_name, false)) {
+		ret = -EINVAL;
+		goto exit;
+	}
+
+	/*
+	 * Do atomic_inc_return here to reserve our slot, then decrement
+	 * it before returning.
+	 */
+	if (atomic_inc_return(&conn->name_count) > KDBUS_CONN_MAX_NAMES) {
+		ret = -E2BIG;
+		goto exit_dec;
+	}
+
+	ret = kdbus_name_acquire(conn->ep->bus->name_registry, conn, item_name,
+				 cmd->flags, &cmd->return_flags);
+	if (ret < 0)
+		goto exit_dec;
+
+exit_dec:
+	atomic_dec(&conn->name_count);
+exit:
+	return kdbus_args_clear(&args, ret);
+}
+
+/**
+ * kdbus_cmd_name_release() - handle KDBUS_CMD_NAME_RELEASE
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_name_release(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_cmd *cmd;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+		{ .type = KDBUS_ITEM_NAME, .mandatory = true },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	if (!kdbus_conn_is_ordinary(conn))
+		return -EOPNOTSUPP;
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	ret = kdbus_name_release(conn->ep->bus->name_registry, conn,
+				 argv[1].item->str);
+	return kdbus_args_clear(&args, ret);
+}
+
+static int kdbus_list_write(struct kdbus_conn *conn,
+			    struct kdbus_conn *c,
+			    struct kdbus_pool_slice *slice,
+			    size_t *pos,
+			    struct kdbus_name_entry *e,
+			    bool write)
+{
+	struct kvec kvec[4];
+	size_t cnt = 0;
+	int ret;
+
+	/* info header */
+	struct kdbus_info info = {
+		.size = 0,
+		.id = c->id,
+		.flags = c->flags,
+	};
+
+	/* fake the header of a kdbus_name item */
+	struct {
+		u64 size;
+		u64 type;
+		u64 flags;
+	} h = {};
+
+	if (e && !kdbus_conn_policy_see_name_unlocked(conn, current_cred(),
+						      e->name))
+		return 0;
+
+	kdbus_kvec_set(&kvec[cnt++], &info, sizeof(info), &info.size);
+
+	/* append name */
+	if (e) {
+		size_t slen = strlen(e->name) + 1;
+
+		h.size = offsetof(struct kdbus_item, name.name) + slen;
+		h.type = KDBUS_ITEM_OWNED_NAME;
+		h.flags = e->flags;
+
+		kdbus_kvec_set(&kvec[cnt++], &h, sizeof(h), &info.size);
+		kdbus_kvec_set(&kvec[cnt++], e->name, slen, &info.size);
+		cnt += !!kdbus_kvec_pad(&kvec[cnt], &info.size);
+	}
+
+	if (write) {
+		ret = kdbus_pool_slice_copy_kvec(slice, *pos, kvec,
+						 cnt, info.size);
+		if (ret < 0)
+			return ret;
+	}
+
+	*pos += info.size;
+	return 0;
+}
+
+static int kdbus_list_all(struct kdbus_conn *conn, u64 flags,
+			  struct kdbus_pool_slice *slice,
+			  size_t *pos, bool write)
+{
+	struct kdbus_conn *c;
+	size_t p = *pos;
+	int ret, i;
+
+	hash_for_each(conn->ep->bus->conn_hash, i, c, hentry) {
+		bool added = false;
+
+		/* skip monitors */
+		if (kdbus_conn_is_monitor(c))
+			continue;
+
+		/* skip activators */
+		if (!(flags & KDBUS_LIST_ACTIVATORS) &&
+		    kdbus_conn_is_activator(c))
+			continue;
+
+		/* all names the connection owns */
+		if (flags & (KDBUS_LIST_NAMES | KDBUS_LIST_ACTIVATORS)) {
+			struct kdbus_name_entry *e;
+
+			list_for_each_entry(e, &c->names_list, conn_entry) {
+				struct kdbus_conn *a = e->activator;
+
+				if ((flags & KDBUS_LIST_ACTIVATORS) &&
+				    a && a != c) {
+					ret = kdbus_list_write(conn, a, slice,
+							       &p, e, write);
+					if (ret < 0) {
+						mutex_unlock(&c->lock);
+						return ret;
+					}
+
+					added = true;
+				}
+
+				if (flags & KDBUS_LIST_NAMES ||
+				    kdbus_conn_is_activator(c)) {
+					ret = kdbus_list_write(conn, c, slice,
+							       &p, e, write);
+					if (ret < 0) {
+						mutex_unlock(&c->lock);
+						return ret;
+					}
+
+					added = true;
+				}
+			}
+		}
+
+		/* queue of names the connection is currently waiting for */
+		if (flags & KDBUS_LIST_QUEUED) {
+			struct kdbus_name_pending *q;
+
+			list_for_each_entry(q, &c->names_queue_list,
+					    conn_entry) {
+				ret = kdbus_list_write(conn, c, slice, &p,
+						       q->name, write);
+				if (ret < 0) {
+					mutex_unlock(&c->lock);
+					return ret;
+				}
+
+				added = true;
+			}
+		}
+
+		/* nothing added so far, just add the unique ID */
+		if (!added && flags & KDBUS_LIST_UNIQUE) {
+			ret = kdbus_list_write(conn, c, slice, &p, NULL, write);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	*pos = p;
+	return 0;
+}
+
+/**
+ * kdbus_cmd_list() - handle KDBUS_CMD_LIST
+ * @conn:		connection to operate on
+ * @argp:		command payload
+ *
+ * Return: 0 on success, negative error code on failure.
+ */
+int kdbus_cmd_list(struct kdbus_conn *conn, void __user *argp)
+{
+	struct kdbus_name_registry *reg = conn->ep->bus->name_registry;
+	struct kdbus_pool_slice *slice = NULL;
+	struct kdbus_cmd_list *cmd;
+	size_t pos, size;
+	int ret;
+
+	struct kdbus_arg argv[] = {
+		{ .type = KDBUS_ITEM_NEGOTIATE },
+	};
+	struct kdbus_args args = {
+		.allowed_flags = KDBUS_FLAG_NEGOTIATE |
+				 KDBUS_LIST_UNIQUE |
+				 KDBUS_LIST_NAMES |
+				 KDBUS_LIST_ACTIVATORS |
+				 KDBUS_LIST_QUEUED,
+		.argv = argv,
+		.argc = ARRAY_SIZE(argv),
+	};
+
+	ret = kdbus_args_parse(&args, argp, &cmd);
+	if (ret != 0)
+		return ret;
+
+	/* lock order: domain -> bus -> ep -> names -> conn */
+	down_read(&reg->rwlock);
+	down_read(&conn->ep->bus->conn_rwlock);
+	down_read(&conn->ep->policy_db.entries_rwlock);
+
+	/* size of records */
+	size = 0;
+	ret = kdbus_list_all(conn, cmd->flags, NULL, &size, false);
+	if (ret < 0)
+		goto exit_unlock;
+
+	if (size == 0) {
+		kdbus_pool_publish_empty(conn->pool, &cmd->offset,
+					 &cmd->list_size);
+	} else {
+		slice = kdbus_pool_slice_alloc(conn->pool, size, false);
+		if (IS_ERR(slice)) {
+			ret = PTR_ERR(slice);
+			slice = NULL;
+			goto exit_unlock;
+		}
+
+		/* copy the records */
+		pos = 0;
+		ret = kdbus_list_all(conn, cmd->flags, slice, &pos, true);
+		if (ret < 0)
+			goto exit_unlock;
+
+		WARN_ON(pos != size);
+		kdbus_pool_slice_publish(slice, &cmd->offset, &cmd->list_size);
+	}
+
+	if (kdbus_member_set_user(&cmd->offset, argp, typeof(*cmd), offset) ||
+	    kdbus_member_set_user(&cmd->list_size, argp,
+				  typeof(*cmd), list_size))
+		ret = -EFAULT;
+
+exit_unlock:
+	up_read(&conn->ep->policy_db.entries_rwlock);
+	up_read(&conn->ep->bus->conn_rwlock);
+	up_read(&reg->rwlock);
+	kdbus_pool_slice_release(slice);
+	return kdbus_args_clear(&args, ret);
+}
ipc/kdbus/names.h new file +74 -0

74 lines not on this page · raw message

Signature · 1 line
2.3.1
#11 lore raw

PATCH v4 11/14 kdbus: add policy database implementation

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

This patch adds the policy database implementation.

A policy database restricts the possibilities of connections to own, see and talk to well-known names. It can be associated with a bus (through a policy holder connection) or a custom endpoint.

By default, buses have an empty policy database that is augmented on demand when a policy holder connection is instantiated.

Policies are set through KDBUS_CMD_HELLO (when creating a policy holder connection), KDBUS_CMD_CONN_UPDATE (when updating a policy holder connection), KDBUS_CMD_EP_MAKE (creating a custom endpoint) or KDBUS_CMD_EP_UPDATE (updating a custom endpoint). In all cases, the name and policy access information is stored in items of type KDBUS_ITEM_NAME and KDBUS_ITEM_POLICY_ACCESS.

See kdbus.policy(7) for more details.

---
 ipc/kdbus/policy.c | 489 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 ipc/kdbus/policy.h |  51 ++++++
 2 files changed, 540 insertions(+)
 create mode 100644 ipc/kdbus/policy.c
 create mode 100644 ipc/kdbus/policy.h
ipc/kdbus/policy.c new file +489 -0
new file mode 100644
index 000000000000..dd7fffaafa84
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ * Copyright (C) 2013-2015 Greg Kroah-Hartman <[email protected]>
+ * Copyright (C) 2013-2015 Daniel Mack <[email protected]>
+ * Copyright (C) 2013-2015 David Herrmann <[email protected]>
+ * Copyright (C) 2013-2015 Linux Foundation
+ * Copyright (C) 2014-2015 Djalal Harouni
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+
+#include <linux/dcache.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/sizes.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+
+#include "bus.h"
+#include "connection.h"
+#include "domain.h"
+#include "item.h"
+#include "names.h"
+#include "policy.h"
+
+#define KDBUS_POLICY_HASH_SIZE	64
+
+/**
+ * struct kdbus_policy_db_entry_access - a database entry access item
+ * @type:		One of KDBUS_POLICY_ACCESS_* types
+ * @access:		Access to grant. One of KDBUS_POLICY_*
+ * @uid:		For KDBUS_POLICY_ACCESS_USER, the global uid
+ * @gid:		For KDBUS_POLICY_ACCESS_GROUP, the global gid
+ * @list:		List entry item for the entry's list
+ *
+ * This is the internal version of struct kdbus_policy_db_access.
+ */
+struct kdbus_policy_db_entry_access {
+	u8 type;		/* USER, GROUP, WORLD */
+	u8 access;		/* OWN, TALK, SEE */
+	union {
+		kuid_t uid;	/* global uid */
+		kgid_t gid;	/* global gid */
+	};
+	struct list_head list;
+};
+
+/**
+ * struct kdbus_policy_db_entry - a policy database entry
+ * @name:		The name to match the policy entry against
+ * @hentry:		The hash entry for the database's entries_hash
+ * @access_list:	List head for keeping tracks of the entry's
+ *			access items.
+ * @owner:		The owner of this entry. Can be a kdbus_conn or
+ *			a kdbus_ep object.
+ * @wildcard:		The name is a wildcard, such as ending on '.*'
+ */
+struct kdbus_policy_db_entry {
+	char *name;
+	struct hlist_node hentry;
+	struct list_head access_list;
+	const void *owner;
+	bool wildcard:1;
+};
+
+static void kdbus_policy_entry_free(struct kdbus_policy_db_entry *e)
+{
+	struct kdbus_policy_db_entry_access *a, *tmp;
+
+	list_for_each_entry_safe(a, tmp, &e->access_list, list) {
+		list_del(&a->list);
+		kfree(a);
+	}
+
+	kfree(e->name);
+	kfree(e);
+}
+
+static unsigned int kdbus_strnhash(const char *str, size_t len)
+{
+	unsigned long hash = init_name_hash();
+
+	while (len--)
+		hash = partial_name_hash(*str++, hash);
+
+	return end_name_hash(hash);
+}
+
+static const struct kdbus_policy_db_entry *
+kdbus_policy_lookup(struct kdbus_policy_db *db, const char *name, u32 hash)
+{
+	struct kdbus_policy_db_entry *e;
+	const char *dot;
+	size_t len;
+
+	/* find exact match */
+	hash_for_each_possible(db->entries_hash, e, hentry, hash)
+		if (strcmp(e->name, name) == 0 && !e->wildcard)
+			return e;
+
+	/* find wildcard match */
+
+	dot = strrchr(name, '.');
+	if (!dot)
+		return NULL;
+
+	len = dot - name;
+	hash = kdbus_strnhash(name, len);
+
+	hash_for_each_possible(db->entries_hash, e, hentry, hash)
+		if (e->wildcard && !strncmp(e->name, name, len) &&
+		    !e->name[len])
+			return e;
+
+	return NULL;
+}
+
+/**
+ * kdbus_policy_db_clear - release all memory from a policy db
+ * @db:		The policy database
+ */
+void kdbus_policy_db_clear(struct kdbus_policy_db *db)
+{
+	struct kdbus_policy_db_entry *e;
+	struct hlist_node *tmp;
+	unsigned int i;
+
+	/* purge entries */
+	down_write(&db->entries_rwlock);
+	hash_for_each_safe(db->entries_hash, i, tmp, e, hentry) {
+		hash_del(&e->hentry);
+		kdbus_policy_entry_free(e);
+	}
+	up_write(&db->entries_rwlock);
+}
+
+/**
+ * kdbus_policy_db_init() - initialize a new policy database
+ * @db:		The location of the database
+ *
+ * This initializes a new policy-db. The underlying memory must have been
+ * cleared to zero by the caller.
+ */
+void kdbus_policy_db_init(struct kdbus_policy_db *db)
+{
+	hash_init(db->entries_hash);
+	init_rwsem(&db->entries_rwlock);
+}
+
+/**
+ * kdbus_policy_query_unlocked() - Query the policy database
+ * @db:		Policy database
+ * @cred:	Credentials to test against
+ * @name:	Name to query
+ * @hash:	Hash value of @name
+ *
+ * Same as kdbus_policy_query() but requires the caller to lock the policy
+ * database against concurrent writes.
+ *
+ * Return: The highest KDBUS_POLICY_* access type found, or -EPERM if none.
+ */
+int kdbus_policy_query_unlocked(struct kdbus_policy_db *db,
+				const struct cred *cred, const char *name,
+				unsigned int hash)
+{
+	struct kdbus_policy_db_entry_access *a;
+	const struct kdbus_policy_db_entry *e;
+	int i, highest = -EPERM;
+
+	e = kdbus_policy_lookup(db, name, hash);
+	if (!e)
+		return -EPERM;
+
+	list_for_each_entry(a, &e->access_list, list) {
+		if ((int)a->access <= highest)
+			continue;
+
+		switch (a->type) {
+		case KDBUS_POLICY_ACCESS_USER:
+			if (uid_eq(cred->euid, a->uid))
+				highest = a->access;
+			break;
+		case KDBUS_POLICY_ACCESS_GROUP:
+			if (gid_eq(cred->egid, a->gid)) {
+				highest = a->access;
+				break;
+			}
+
+			for (i = 0; i < cred->group_info->ngroups; i++) {
+				kgid_t gid = GROUP_AT(cred->group_info, i);
+
+				if (gid_eq(gid, a->gid)) {
+					highest = a->access;
+					break;
+				}
+			}
+
+			break;
+		case KDBUS_POLICY_ACCESS_WORLD:
+			highest = a->access;
+			break;
+		}
+
+		/* OWN is the highest possible policy */
+		if (highest >= KDBUS_POLICY_OWN)
+			break;
+	}
+
+	return highest;
+}
+
+/**
+ * kdbus_policy_query() - Query the policy database
+ * @db:		Policy database
+ * @cred:	Credentials to test against
+ * @name:	Name to query
+ * @hash:	Hash value of @name
+ *
+ * Query the policy database @db for the access rights of @cred to the name
+ * @name. The access rights of @cred are returned, or -EPERM if no access is
+ * granted.
+ *
+ * This call effectively searches for the highest access-right granted to
+ * @cred. The caller should really cache those as policy lookups are rather
+ * expensive.
+ *
+ * Return: The highest KDBUS_POLICY_* access type found, or -EPERM if none.
+ */
+int kdbus_policy_query(struct kdbus_policy_db *db, const struct cred *cred,
+		       const char *name, unsigned int hash)
+{
+	int ret;
+
+	down_read(&db->entries_rwlock);
+	ret = kdbus_policy_query_unlocked(db, cred, name, hash);
+	up_read(&db->entries_rwlock);
+
+	return ret;
+}
+
+static void __kdbus_policy_remove_owner(struct kdbus_policy_db *db,
+					const void *owner)
+{
+	struct kdbus_policy_db_entry *e;
+	struct hlist_node *tmp;
+	int i;
+
+	hash_for_each_safe(db->entries_hash, i, tmp, e, hentry)
+		if (e->owner == owner) {
+			hash_del(&e->hentry);
+			kdbus_policy_entry_free(e);
+		}
+}
+
+/**
+ * kdbus_policy_remove_owner() - remove all entries related to a connection
+ * @db:		The policy database
+ * @owner:	The connection which items to remove
+ */
+void kdbus_policy_remove_owner(struct kdbus_policy_db *db,
+			       const void *owner)
+{
+	down_write(&db->entries_rwlock);
+	__kdbus_policy_remove_owner(db, owner);
+	up_write(&db->entries_rwlock);
+}
+
+/*
+ * Convert user provided policy access to internal kdbus policy
+ * access
+ */
+static struct kdbus_policy_db_entry_access *
+kdbus_policy_make_access(const struct kdbus_policy_access *uaccess)
+{
+	int ret;
+	struct kdbus_policy_db_entry_access *a;
+
+	a = kzalloc(sizeof(*a), GFP_KERNEL);
+	if (!a)
+		return ERR_PTR(-ENOMEM);
+
+	ret = -EINVAL;
+	switch (uaccess->access) {
+	case KDBUS_POLICY_SEE:
+	case KDBUS_POLICY_TALK:
+	case KDBUS_POLICY_OWN:
+		a->access = uaccess->access;
+		break;
+	default:
+		goto err;
+	}
+
+	switch (uaccess->type) {
+	case KDBUS_POLICY_ACCESS_USER:
+		a->uid = make_kuid(current_user_ns(), uaccess->id);
+		if (!uid_valid(a->uid))
+			goto err;
+
+		break;
+	case KDBUS_POLICY_ACCESS_GROUP:
+		a->gid = make_kgid(current_user_ns(), uaccess->id);
+		if (!gid_valid(a->gid))
+			goto err;
+
+		break;
+	case KDBUS_POLICY_ACCESS_WORLD:
+		break;
+	default:
+		goto err;
+	}
+
+	a->type = uaccess->type;
+
+	return a;
+
+err:
+	kfree(a);
+	return ERR_PTR(ret);
+}
+
+/**
+ * kdbus_policy_set() - set a connection's policy rules
+ * @db:				The policy database
+ * @items:			A list of kdbus_item elements that contain both
+ *				names and access rules to set.
+ * @items_size:			The total size of the items.
+ * @max_policies:		The maximum number of policy entries to allow.
+ *				Pass 0 for no limit.
+ * @allow_wildcards:		Boolean value whether wildcard entries (such
+ *				ending on '.*') should be allowed.
+ * @owner:			The owner of the new policy items.
+ *
+ * This function sets a new set of policies for a given owner. The names and
+ * access rules are gathered by walking the list of items passed in as
+ * argument. An item of type KDBUS_ITEM_NAME is expected before any number of
+ * KDBUS_ITEM_POLICY_ACCESS items. If there are more repetitions of this
+ * pattern than denoted in @max_policies, -EINVAL is returned.
+ *
+ * In order to allow atomic replacement of rules, the function first removes
+ * all entries that have been created for the given owner previously.
+ *
+ * Callers to this function must make sur that the owner is a custom
+ * endpoint, or if the endpoint is a default endpoint, then it must be
+ * either a policy holder or an activator.
+ *
+ * Return: 0 on success, negative errno on failure.
+ */
+int kdbus_policy_set(struct kdbus_policy_db *db,
+		     const struct kdbus_item *items,
+		     size_t items_size,
+		     size_t max_policies,
+		     bool allow_wildcards,
+		     const void *owner)
+{
+	struct kdbus_policy_db_entry_access *a;
+	struct kdbus_policy_db_entry *e, *p;
+	const struct kdbus_item *item;
+	struct hlist_node *tmp;
+	HLIST_HEAD(entries);
+	HLIST_HEAD(restore);
+	size_t count = 0;
+	int i, ret = 0;
+	u32 hash;
+
+	/* Walk the list of items and look for new policies */
+	e = NULL;
+	KDBUS_ITEMS_FOREACH(item, items, items_size) {
+		switch (item->type) {
+		case KDBUS_ITEM_NAME: {
+			size_t len;
+
+			if (max_policies && ++count > max_policies) {
+				ret = -E2BIG;
+				goto exit;
+			}
+
+			if (!kdbus_name_is_valid(item->str, true)) {
+				ret = -EINVAL;
+				goto exit;
+			}
+
+			e = kzalloc(sizeof(*e), GFP_KERNEL);
+			if (!e) {
+				ret = -ENOMEM;
+				goto exit;
+			}
+
+			INIT_LIST_HEAD(&e->access_list);
+			e->owner = owner;
+			hlist_add_head(&e->hentry, &entries);
+
+			e->name = kstrdup(item->str, GFP_KERNEL);
+			if (!e->name) {
+				ret = -ENOMEM;
+				goto exit;
+			}
+
+			/*
+			 * If a supplied name ends with an '.*', cut off that
+			 * part, only store anything before it, and mark the
+			 * entry as wildcard.
+			 */
+			len = strlen(e->name);
+			if (len > 2 &&
+			    e->name[len - 3] == '.' &&
+			    e->name[len - 2] == '*') {
+				if (!allow_wildcards) {
+					ret = -EINVAL;
+					goto exit;
+				}
+
+				e->name[len - 3] = '\0';
+				e->wildcard = true;
+			}
+
+			break;
+		}
+
+		case KDBUS_ITEM_POLICY_ACCESS:
+			if (!e) {
+				ret = -EINVAL;
+				goto exit;
+			}
+
+			a = kdbus_policy_make_access(&item->policy_access);
+			if (IS_ERR(a)) {
+				ret = PTR_ERR(a);
+				goto exit;
+			}
+
+			list_add_tail(&a->list, &e->access_list);
+			break;
+		}
+	}
+
+	down_write(&db->entries_rwlock);
+
+	/* remember previous entries to restore in case of failure */
+	hash_for_each_safe(db->entries_hash, i, tmp, e, hentry)
+		if (e->owner == owner) {
+			hash_del(&e->hentry);
+			hlist_add_head(&e->hentry, &restore);
+		}
+
+	hlist_for_each_entry_safe(e, tmp, &entries, hentry) {
+		/* prevent duplicates */
+		hash = kdbus_strhash(e->name);
+		hash_for_each_possible(db->entries_hash, p, hentry, hash)
+			if (strcmp(e->name, p->name) == 0 &&
+			    e->wildcard == p->wildcard) {
+				ret = -EEXIST;
+				goto restore;
+			}
+
+		hlist_del(&e->hentry);
+		hash_add(db->entries_hash, &e->hentry, hash);
+	}
+
+restore:
+	/* if we failed, flush all entries we added so far */
+	if (ret < 0)
+		__kdbus_policy_remove_owner(db, owner);
+
+	/* if we failed, restore entries, otherwise release them */
+	hlist_for_each_entry_safe(e, tmp, &restore, hentry) {
+		hlist_del(&e->hentry);
+		if (ret < 0) {
+			hash = kdbus_strhash(e->name);
+			hash_add(db->entries_hash, &e->hentry, hash);
+		} else {
+			kdbus_policy_entry_free(e);
+		}
+	}
+
+	up_write(&db->entries_rwlock);
+
+exit:
+	hlist_for_each_entry_safe(e, tmp, &entries, hentry) {
+		hlist_del(&e->hentry);
+		kdbus_policy_entry_free(e);
+	}
+
+	return ret;
+}
ipc/kdbus/policy.h new file +51 -0

51 lines not on this page · raw message

Signature · 1 line
2.3.1
#12 lore raw

PATCH v4 12/14 kdbus: add Makefile, Kconfig and MAINTAINERS entry

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

This patch hooks up the build system to actually compile the files added by previous patches. It also adds an entry to MAINTAINERS to direct people to Greg KH, David Herrmann, Djalal Harouni and me for questions and patches.

---
 MAINTAINERS        | 13 +++++++++++++
 init/Kconfig       | 12 ++++++++++++
 ipc/Makefile       |  2 +-
 ipc/kdbus/Makefile | 22 ++++++++++++++++++++++
 4 files changed, 48 insertions(+), 1 deletion(-)
 create mode 100644 ipc/kdbus/Makefile
MAINTAINERS +13 -0
index 6239a305dff0..e924246fb545 100644
@@ -5503,6 +5503,19 @@ S:	Maintained
 F:	Documentation/kbuild/kconfig-language.txt
 F:	scripts/kconfig/
 
+KDBUS
+M:	Greg Kroah-Hartman <[email protected]>
+M:	Daniel Mack <[email protected]>
+M:	David Herrmann <dh.herrmann-gM/[email protected]>
+M:	Djalal Harouni <tixxdz-Umm1ozX2/[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	ipc/kdbus/*
+F:	samples/kdbus/*
+F:	Documentation/kdbus/*
+F:	include/uapi/linux/kdbus.h
+F:	tools/testing/selftests/kdbus/
+
 KDUMP
 M:	Vivek Goyal <[email protected]>
 M:	Haren Myneni <hbabu-r/[email protected]>
init/Kconfig +12 -0
index f5dbc6d4261b..a7b462e7d647 100644
@@ -261,6 +261,18 @@ config POSIX_MQUEUE_SYSCTL
 	depends on SYSCTL
 	default y
 
+config KDBUS
+	tristate "kdbus interprocess communication"
+	depends on TMPFS
+	help
+	  D-Bus is a system for low-latency, low-overhead, easy to use
+	  interprocess communication (IPC).
+
+	  See Documentation/kdbus.txt
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called kdbus.
+
 config CROSS_MEMORY_ATTACH
 	bool "Enable process_vm_readv/writev syscalls"
 	depends on MMU
ipc/Makefile +1 -1
index 86c7300ecdf5..68ec4167d11b 100644
@@ -9,4 +9,4 @@ obj_mq-$(CONFIG_COMPAT) += compat_mq.o
 obj-$(CONFIG_POSIX_MQUEUE) += mqueue.o msgutil.o $(obj_mq-y)
 obj-$(CONFIG_IPC_NS) += namespace.o
 obj-$(CONFIG_POSIX_MQUEUE_SYSCTL) += mq_sysctl.o
-
+obj-$(CONFIG_KDBUS) += kdbus/
ipc/kdbus/Makefile new file +22 -0
new file mode 100644
index 000000000000..7ee9271e1449
@@ -0,0 +1,22 @@
+kdbus-y := \
+	bus.o \
+	connection.o \
+	endpoint.o \
+	fs.o \
+	handle.o \
+	item.o \
+	main.o \
+	match.o \
+	message.o \
+	metadata.o \
+	names.o \
+	node.o \
+	notify.o \
+	domain.o \
+	policy.o \
+	pool.o \
+	reply.o \
+	queue.o \
+	util.o
+
+obj-$(CONFIG_KDBUS) += kdbus.o
Signature · 1 line
2.3.1
#13 lore raw

Re: [PATCH 12/14] kdbus: add Makefile, Kconfig and MAINTAINERS entry

Quoted 4 lines · Greg Kroah-Hartman
init/Kconfig +0 -0
@@ -261,6 +261,18 @@ config POSIX_MQUEUE_SYSCTL
 	depends on SYSCTL

init/Kconfig config POSIX_MQUEUE_SYSCTL · line 271

Quoted 10 lines
init/Kconfig +8 -0
 	default y
 
+config KDBUS
+	tristate "kdbus interprocess communication"
+	depends on TMPFS
+	help
+	  D-Bus is a system for low-latency, low-overhead, easy to use
+	  interprocess communication (IPC).
+
+	  See Documentation/kdbus.txt

This one is missing from the series.

init/Kconfig config POSIX_MQUEUE_SYSCTL · line 274

Quoted 2 lines
init/Kconfig +2 -0
+	  To compile this driver as a module, choose M here: the
+	  module will be called kdbus.

It would be nice to also know who actually needs this... The old good 'if you have an ordinary machine, select m here'.

thanks,

Signature · 2 lines
js
suse labs
#14 lore raw

PATCH 1/1 kdbus: Fix CONFIG_KDBUS help text

Drop a left-over from the times when documentation lived in a simple text file, which is no longer the case. Mention the auto-generated man-pages and HTML files instead.

---

Thanks for reporting this, Jiri!

 init/Kconfig | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
init/Kconfig +4 -3
index a7b462e..6bda631 100644
@@ -268,10 +268,11 @@ config KDBUS
 	  D-Bus is a system for low-latency, low-overhead, easy to use
 	  interprocess communication (IPC).
 
-	  See Documentation/kdbus.txt
+	  See the man-pages and HTML files in Documentation/kdbus/
+	  that are generated by 'make mandocs' and 'make htmldocs'.
 
-	  To compile this driver as a module, choose M here: the
-	  module will be called kdbus.
+	  If you have an ordinary machine, select M here. The module
+	  will be called kdbus.
 
 config CROSS_MEMORY_ATTACH
 	bool "Enable process_vm_readv/writev syscalls"
Signature · 1 line
2.3.3
Reviewed-by on v1 1/1
#15 lore raw

Re: [PATCH] kdbus: Fix CONFIG_KDBUS help text

Hi

Quoted 9 lines · Daniel Mack

Drop a left-over from the times when documentation lived in a simple text file, which is no longer the case. Mention the auto-generated man-pages and HTML files instead.

---

Thanks for reporting this, Jiri!

Thanks
David

Quoted 26 lines
 init/Kconfig | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)
init/Kconfig +4 -4
index a7b462e..6bda631 100644
@@ -268,10 +268,11 @@ config KDBUS
          D-Bus is a system for low-latency, low-overhead, easy to use
          interprocess communication (IPC).
 
-         See Documentation/kdbus.txt
+         See the man-pages and HTML files in Documentation/kdbus/
+         that are generated by 'make mandocs' and 'make htmldocs'.
 
-         To compile this driver as a module, choose M here: the
-         module will be called kdbus.
+         If you have an ordinary machine, select M here. The module
+         will be called kdbus.
 
 config CROSS_MEMORY_ATTACH
        bool "Enable process_vm_readv/writev syscalls"
--

2.3.3

author Applied
#16 lore raw

Re: [PATCH] kdbus: Fix CONFIG_KDBUS help text

In reply to Daniel Mack
Quoted 10 lines · Daniel Mack

Drop a left-over from the times when documentation lived in a simple text file, which is no longer the case. Mention the auto-generated man-pages and HTML files instead.

---

Thanks for reporting this, Jiri!

Applied, thanks.

greg k-h

#17 lore raw

PATCH v4 13/14 kdbus: add walk-through user space example

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

Provide a walk-through example that explains how to use the low-level ioctl API that kdbus offers. This example is meant to be useful for developers who want to gain a in-depth understanding of how the kdbus API works by reading a well-documented real-world example.

This program computes prime-numbers based on the sieve of Eratosthenes. The master sets up a shared memory region and spawns workers which clear out the non-primes. The master reacts to keyboard input and to client-requests to control what each worker does. Note that this is in no way meant as efficient way to compute primes. It should only serve as example how a master/worker concept can be implemented with kdbus used as control messages.

The main process is called the 'master'. It creates a new, private bus which will be used between the master and its workers to communicate. The master then spawns a fixed number of workers. Whenever a worker dies (detected via SIGCHLD), the master spawns a new worker. When done, the master waits for all workers to exit, prints a status report and exits itself.

The master process does *not* keep track of its workers. Instead, this example implements a PULL model. That is, the master acquires a well-known name on the bus which each worker uses to request tasks from the master. If there are no more tasks, the master will return an empty task-list, which casues a worker to exit immediately.

As tasks can be computationally expensive, we support cancellation. Whenever the master process is interrupted, it will drop its well-known name on the bus. This causes kdbus to broadcast a name-change notification. The workers check for broadcast messages regularly and will exit if they receive one.

---
 samples/Makefile              |    3 +-
 samples/kdbus/.gitignore      |    1 +
 samples/kdbus/Makefile        |   10 +
 samples/kdbus/kdbus-api.h     |  114 ++++
 samples/kdbus/kdbus-workers.c | 1326 +++++++++++++++++++++++++++++++++++++++++
 5 files changed, 1453 insertions(+), 1 deletion(-)
 create mode 100644 samples/kdbus/.gitignore
 create mode 100644 samples/kdbus/Makefile
 create mode 100644 samples/kdbus/kdbus-api.h
 create mode 100644 samples/kdbus/kdbus-workers.c
samples/Makefile +2 -1
index f00257bcc5a7..f0ad51e5b342 100644
@@ -1,4 +1,5 @@
 # Makefile for Linux samples code
 
 obj-$(CONFIG_SAMPLES)	+= kobject/ kprobes/ trace_events/ livepatch/ \
-			   hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/
+			   hw_breakpoint/ kfifo/ kdb/ kdbus/ hidraw/ rpmsg/ \
+			   seccomp/
samples/kdbus/.gitignore new file +1 -0
new file mode 100644
index 000000000000..ee07d9857086
@@ -0,0 +1 @@
+kdbus-workers
samples/kdbus/Makefile new file +10 -0
new file mode 100644
index 000000000000..d009025369f4
@@ -0,0 +1,10 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-y += kdbus-workers
+
+always := $(hostprogs-y)
+
+HOSTCFLAGS_kdbus-workers.o +=		\
+	-I$(objtree)/usr/include/	\
+	-I$(objtree)/include/uapi/
samples/kdbus/kdbus-api.h new file +114 -0

114 lines not on this page · raw message

samples/kdbus/kdbus-workers.c new file +1326 -0

1,326 lines not on this page · raw message

Signature · 1 line
2.3.1
#18 lore raw

Re: [PATCH 13/14] kdbus: add walk-through user space example

Quoted 6 lines · Greg Kroah-Hartman
samples/kdbus/Makefile new file +0 -0
new file mode 100644
index 000000000000..d009025369f4

samples/kdbus/Makefile line 10

Quoted 10 lines
samples/kdbus/Makefile new file +10 -0
@@ -0,0 +1,10 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-y += kdbus-workers
+
+always := $(hostprogs-y)
+
+HOSTCFLAGS_kdbus-workers.o +=		\
+	-I$(objtree)/usr/include/	\
+	-I$(objtree)/include/uapi/

-lrt

For older glibcs, otherwise clock_gettime() isn't found on linking.

Thanks,
Sasha

#19 lore raw

PATCH 1/1 samples/kdbus: add -lrt

On older systems -lrt is needed for clock_gettime(). Add it to HOSTLOADLIBES of kdbus-workers so it builds fine on those systems.

---
Hi

This should fix the build-issues of the kdbus-examples on older systems where clock_gettime() is not available in -lc, but requires -lrt.

This is based on gregkh/char-misc/kdbus, and available on:
http://cgit.freedesktop.org/~dvdhrm/linux/log/?h=kdbus

Thanks
David

 samples/kdbus/Makefile | 1 +
 1 file changed, 1 insertion(+)
samples/kdbus/Makefile +1 -0
index d009025..eee9b9a 100644
@@ -8,3 +8,4 @@ always := $(hostprogs-y)
 HOSTCFLAGS_kdbus-workers.o +=		\
 	-I$(objtree)/usr/include/	\
 	-I$(objtree)/include/uapi/
+HOSTLOADLIBES_kdbus-workers := -lrt
Signature · 1 line
2.3.2
#20 lore raw

Re: [PATCH] kdbus: samples/kdbus: add -lrt

Quoted 5 lines · David Herrmann

On older systems -lrt is needed for clock_gettime(). Add it to HOSTLOADLIBES of kdbus-workers so it builds fine on those systems.

Quoted 8 lines

---
Hi

This should fix the build-issues of the kdbus-examples on older systems where clock_gettime() is not available in -lc, but requires -lrt.

This is based on gregkh/char-misc/kdbus, and available on:
http://cgit.freedesktop.org/~dvdhrm/linux/log/?h=kdbus

Now applied, thanks.

greg k-h

#21 lore raw

Re: [PATCH 13/14] kdbus: add walk-through user space example

In reply to Sasha Levin

Hi

Quoted 19 lines · Sasha Levin
Quoted 6 lines · Greg Kroah-Hartman
samples/kdbus/Makefile new file +0 -0
new file mode 100644
index 000000000000..d009025369f4

samples/kdbus/Makefile line 10

Quoted 10 lines
samples/kdbus/Makefile new file +10 -0
@@ -0,0 +1,10 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-y += kdbus-workers
+
+always := $(hostprogs-y)
+
+HOSTCFLAGS_kdbus-workers.o +=                \
+     -I$(objtree)/usr/include/       \
+     -I$(objtree)/include/uapi/

-lrt

Quoted 1 line

For older glibcs, otherwise clock_gettime() isn't found on linking.

Right, thanks! Fixed in "[PATCH] samples/kdbus: add -lrt".

Thanks
David

#22 lore raw

Re: [PATCH 13/14] kdbus: add walk-through user space example

In reply to Greg Kroah-Hartman

samples/kdbus/Makefile line 6

Quoted 9 lines · Greg Kroah-Hartman
samples/kdbus/Makefile new file +6 -0
@@ -0,0 +1,10 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-y += kdbus-workers
+
+always := $(hostprogs-y)

Errr, no. Not only it causes build failures (even with KDBUS=n), it definitely should not be built for everyone.

And why is it a host prog? It's a sample prog for the kernel I am building, i.e. for the destination arch, like all the other samples.

thanks,

Signature · 2 lines
js
suse labs
#23 lore raw

Re: [PATCH 13/14] kdbus: add walk-through user space example

Hi

Quoted 13 lines · Jiri Slaby

samples/kdbus/Makefile line 6

Quoted 9 lines · Greg Kroah-Hartman
samples/kdbus/Makefile new file +6 -0
@@ -0,0 +1,10 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-y += kdbus-workers
+
+always := $(hostprogs-y)

Errr, no. Not only it causes build failures (even with KDBUS=n), it definitely should not be built for everyone.

It's only built if CONFIG_SAMPLES is set, right?

What build-failures does it cause? linux/kdbus.h is not optional based on CONFIG_KDBUS, so the samples should build just fine. Can you tell me what kind of errors you get? The kbuild-robots didn't report anything so far.

Quoted 2 lines

And why is it a host prog? It's a sample prog for the kernel I am building, i.e. for the destination arch, like all the other samples.

It's modeled after the other user-space examples in ./samples/, which all use hostprogs (see samples/{bpf,hidraw,seccomp,uhid}/Makefile). I have no idea how to build programs that run on the target architecture. Documentation/kbuild/makefiles.txt doesn't list it, which is, I guess, the reason why everyone used hostprogs so far. And given that autotools calls the target architecture "--host", I actually thought this is what hostprogs does.. apparently that's not the case, sorry.

Thanks
David

#24 lore raw

Re: [PATCH 13/14] kdbus: add walk-through user space example

Ccing kbuild fellows (see the very bottom).

Quoted 17 lines · David Herrmann

Hi

Quoted 13 lines · Jiri Slaby

samples/kdbus/Makefile line 6

Quoted 9 lines · Greg Kroah-Hartman
samples/kdbus/Makefile new file +6 -0
@@ -0,0 +1,10 @@
+# kbuild trick to avoid linker error. Can be omitted if a module is built.
+obj- := dummy.o
+
+hostprogs-y += kdbus-workers
+
+always := $(hostprogs-y)

Errr, no. Not only it causes build failures (even with KDBUS=n), it definitely should not be built for everyone.

Quoted 1 line

It's only built if CONFIG_SAMPLES is set, right?

Yes, but I build other samples selected with separate CONFIG_ options like CONFIG_SAMPLE_LIVEPATCH. So this guy should have its own CONFIG_ option too, because I (and likely others) don't want to build it. All of the samples should be protected at least by their respective kernel CONFIG_ option IMO.

Quoted 4 lines

What build-failures does it cause? linux/kdbus.h is not optional based on CONFIG_KDBUS, so the samples should build just fine. Can you tell me what kind of errors you get? The kbuild-robots didn't report anything so far.

The output is this:
In file included from samples/kdbus/kdbus-workers.c:79:0:
/home/latest/linux/samples/kdbus/kdbus-api.h:5:25: fatal error:
linux/kdbus.h: No such file or directory

 #include <linux/kdbus.h>
                         ^

compilation terminated.

I now know that I have to install_headers *if* I want to build the sample. But I don't want to build it in the first place. (So the config option above should be all we need.)

Quoted 11 lines
Quoted 2 lines

And why is it a host prog? It's a sample prog for the kernel I am building, i.e. for the destination arch, like all the other samples.

It's modeled after the other user-space examples in ./samples/, which all use hostprogs (see samples/{bpf,hidraw,seccomp,uhid}/Makefile). I have no idea how to build programs that run on the target architecture. Documentation/kbuild/makefiles.txt doesn't list it, which is, I guess, the reason why everyone used hostprogs so far. And given that autotools calls the target architecture "--host", I actually thought this is what hostprogs does.. apparently that's not the case, sorry.

Oh, it's cut&paste, I see. This does not look correct though. The hack inclusive. Host progs are intended to be run on the host where the kernel is built. During the compilation or such (like x/menuconfig). Quite misleading naming if you are used to the autotools one.

thanks,

Signature · 2 lines
js
suse labs
#25 lore raw

Re: [PATCH 13/14] kdbus: add walk-through user space example

Dne 24.3.2015 v 18:37 Jiri Slaby napsal(a):

Quoted 10 lines · David Herrmann
Quoted 5 lines · Jiri Slaby
Quoted 2 lines

Errr, no. Not only it causes build failures (even with KDBUS=n), it definitely should not be built for everyone.

It's only built if CONFIG_SAMPLES is set, right?

Yes, but I build other samples selected with separate CONFIG_ options like CONFIG_SAMPLE_LIVEPATCH. So this guy should have its own CONFIG_ option too, because I (and likely others) don't want to build it. All of

Definitely.

Quoted 9 lines
Quoted 8 lines

It's modeled after the other user-space examples in ./samples/, which all use hostprogs (see samples/{bpf,hidraw,seccomp,uhid}/Makefile). I have no idea how to build programs that run on the target architecture. Documentation/kbuild/makefiles.txt doesn't list it, which is, I guess, the reason why everyone used hostprogs so far. And given that autotools calls the target architecture "--host", I actually thought this is what hostprogs does.. apparently that's not the case, sorry.

Quoted 4 lines

Oh, it's cut&paste, I see. This does not look correct though. The hack inclusive. Host progs are intended to be run on the host where the kernel is built. During the compilation or such (like x/menuconfig). Quite misleading naming if you are used to the autotools one.

when cross compiling, we are a bit between a rock and a hard place with the sample userspace programs:
- The target toolchain might not have libc support
- The host toolchain might be lacking recent kernel headers (therefore the need to do make headers_install)
- It's not clean whether the samples are meant to be ran on the build host or target.

There has been some work by Sam Ravnborg to introduce uapiprogs-y to for sample userspace programs, but for now, please use hostprogs-y, -Iusr/include and make each sample opt-in.

Michal

#26 lore raw

Re: [PATCH 13/14] kdbus: add walk-through user space example

Quoted 7 lines · Michal Marek

Dne 24.3.2015 v 18:37 Jiri Slaby napsal(a):

Quoted 4 lines

Oh, it's cut&paste, I see. This does not look correct though. The hack inclusive. Host progs are intended to be run on the host where the kernel is built. During the compilation or such (like x/menuconfig). Quite misleading naming if you are used to the autotools one.

Quoted 7 lines

when cross compiling, we are a bit between a rock and a hard place with the sample userspace programs:
- The target toolchain might not have libc support
- The host toolchain might be lacking recent kernel headers (therefore the need to do make headers_install)
- It's not clean whether the samples are meant to be ran on the build host or target.

Exactly. I just checked in a cross-compiled source tree, and none of the compiled standalone executables from samples/ or Documentation/ are actually built for the target platform. Only the samples which come as kernel modules are.

Quoted 3 lines

There has been some work by Sam Ravnborg to introduce uapiprogs-y to for sample userspace programs, but for now, please use hostprogs-y, -Iusr/include and make each sample opt-in.

Alright then. Does the attached patch fix your problem, Jiri?

Thanks,
Daniel

From 56309ab0717720fb086ec3209f1b1fa719072f61 Mon Sep 17 00:00:00 2001
From: Daniel Mack <[email protected]>
Date: Tue, 24 Mar 2015 19:41:56 +0100
Subject: [PATCH] kdbus: sample: build kdbus-workers conditionally

Give the kdbus sample its own config switch and only build it if it's explicitly switched on.

---
 samples/Kconfig        | 7 +++++++
 samples/kdbus/Makefile | 2 +-
 2 files changed, 8 insertions(+), 1 deletion(-)
samples/Kconfig +7 -0
index 224ebb4..a4c6b2f 100644
@@ -55,6 +55,13 @@ config SAMPLE_KDB
 	  Build an example of how to dynamically add the hello
 	  command to the kdb shell.
 
+config SAMPLE_KDBUS
+	bool "Build kdbus API example"
+	depends on KDBUS
+	help
+	  Build an example of how the kdbus API can be used from
+	  userspace.
+
 config SAMPLE_RPMSG_CLIENT
 	tristate "Build rpmsg client sample -- loadable modules only"
 	depends on RPMSG && m
samples/kdbus/Makefile +1 -1
index d009025..9e40c68 100644
@@ -1,7 +1,7 @@
 # kbuild trick to avoid linker error. Can be omitted if a module is built.
 obj- := dummy.o
 
-hostprogs-y += kdbus-workers
+hostprogs-$(CONFIG_SAMPLE_KDBUS) += kdbus-workers
 
 always := $(hostprogs-y)
 
Signature · 1 line
2.3.3
#27 lore raw

PATCH 1/1 samples: kdbus: build kdbus-workers conditionally

In reply to Jiri Slaby

Give the kdbus sample its own config switch and only build it if it's explicitly switched on.

---
 samples/Kconfig        | 7 +++++++
 samples/kdbus/Makefile | 2 +-
 2 files changed, 8 insertions(+), 1 deletion(-)
samples/Kconfig +7 -0
index 224ebb4..a4c6b2f 100644
@@ -55,6 +55,13 @@ config SAMPLE_KDB
 	  Build an example of how to dynamically add the hello
 	  command to the kdb shell.
 
+config SAMPLE_KDBUS
+	bool "Build kdbus API example"
+	depends on KDBUS
+	help
+	  Build an example of how the kdbus API can be used from
+	  userspace.
+
 config SAMPLE_RPMSG_CLIENT
 	tristate "Build rpmsg client sample -- loadable modules only"
 	depends on RPMSG && m
samples/kdbus/Makefile +1 -1
index d009025..9e40c68 100644
@@ -1,7 +1,7 @@
 # kbuild trick to avoid linker error. Can be omitted if a module is built.
 obj- := dummy.o
 
-hostprogs-y += kdbus-workers
+hostprogs-$(CONFIG_SAMPLE_KDBUS) += kdbus-workers
 
 always := $(hostprogs-y)
 
Signature · 1 line
2.3.4
author
#28 lore raw

Re: [PATCH] samples: kdbus: build kdbus-workers conditionally

Quoted 10 lines · Daniel Mack

Give the kdbus sample its own config switch and only build it if it's explicitly switched on.

---
 samples/Kconfig        | 7 +++++++
 samples/kdbus/Makefile | 2 +-
 2 files changed, 8 insertions(+), 1 deletion(-)

Now applied, thanks.

greg k-h

#29 lore raw

Re: [PATCH] samples: kdbus: build kdbus-workers conditionally

Quoted 12 lines · On Wed, 1 Apr 2015 14:47:20 +0200 Greg KH <[email protected]> wrote:
Quoted 10 lines · Daniel Mack

Give the kdbus sample its own config switch and only build it if it's explicitly switched on.

---
 samples/Kconfig        | 7 +++++++
 samples/kdbus/Makefile | 2 +-
 2 files changed, 8 insertions(+), 1 deletion(-)
Quoted 1 line

Now applied, thanks.

Is this going to fix i386 allmodconfig, currently unhappy on my Fedora Core 6 (lol) machine?

samples/kdbus/kdbus-workers.c:73:26: error: sys/signalfd.h: No such file or directory samples/kdbus/kdbus-workers.c: In function 'master_new':
samples/kdbus/kdbus-workers.c:231: warning: implicit declaration of function 'signalfd' samples/kdbus/kdbus-workers.c:231: error: 'SFD_CLOEXEC' undeclared (first use in this function) samples/kdbus/kdbus-workers.c:231: error: (Each undeclared identifier is reported only once samples/kdbus/kdbus-workers.c:231: error: for each function it appears in.) samples/kdbus/kdbus-workers.c: In function 'master_handle_signal':
samples/kdbus/kdbus-workers.c:406: error: storage size of 'val' isn't known samples/kdbus/kdbus-workers.c:406: warning: unused variable 'val' samples/kdbus/kdbus-workers.c: In function 'child_run':
samples/kdbus/kdbus-workers.c:773: error: 'CLOCK_MONOTONIC_COARSE' undeclared (first use in this function) samples/kdbus/kdbus-workers.c: In function 'bus_open_connection':
samples/kdbus/kdbus-workers.c:1038: error: 'O_CLOEXEC' undeclared (first use in this function) samples/kdbus/kdbus-workers.c: In function 'bus_make':
samples/kdbus/kdbus-workers.c:1275: error: 'O_CLOEXEC' undeclared (first use in this function)

#30 lore raw

Re: [PATCH] samples: kdbus: build kdbus-workers conditionally

Hi Andrew,

Quoted 14 lines · Andrew Morton
Quoted 12 lines · On Wed, 1 Apr 2015 14:47:20 +0200 Greg KH <[email protected]> wrote:
Quoted 10 lines · Daniel Mack

Give the kdbus sample its own config switch and only build it if it's explicitly switched on.

---
 samples/Kconfig        | 7 +++++++
 samples/kdbus/Makefile | 2 +-
 2 files changed, 8 insertions(+), 1 deletion(-)
Quoted 4 lines
Quoted 1 line

Now applied, thanks.

Is this going to fix i386 allmodconfig, currently unhappy on my Fedora Core 6 (lol) machine?

As allmodconfig will select CONFIG_SAMPLE_KDBUS just as it selected CONFIG_SAMPLES, that won't help, no.

Quoted 7 lines

samples/kdbus/kdbus-workers.c:73:26: error: sys/signalfd.h: No such file or directory samples/kdbus/kdbus-workers.c: In function 'master_new':
samples/kdbus/kdbus-workers.c:231: warning: implicit declaration of function 'signalfd' samples/kdbus/kdbus-workers.c:231: error: 'SFD_CLOEXEC' undeclared (first use in this function) samples/kdbus/kdbus-workers.c:231: error: (Each undeclared identifier is reported only once samples/kdbus/kdbus-workers.c:231: error: for each function it appears in.) samples/kdbus/kdbus-workers.c: In function 'master_handle_signal':

Quoted 8 lines

samples/kdbus/kdbus-workers.c:406: error: storage size of 'val' isn't known samples/kdbus/kdbus-workers.c:406: warning: unused variable 'val' samples/kdbus/kdbus-workers.c: In function 'child_run':
samples/kdbus/kdbus-workers.c:773: error: 'CLOCK_MONOTONIC_COARSE' undeclared (first use in this function) samples/kdbus/kdbus-workers.c: In function 'bus_open_connection':
samples/kdbus/kdbus-workers.c:1038: error: 'O_CLOEXEC' undeclared (first use in this function) samples/kdbus/kdbus-workers.c: In function 'bus_make':
samples/kdbus/kdbus-workers.c:1275: error: 'O_CLOEXEC' undeclared (first use in this function)

Hmm, so your libc headers lack support for signalfds, which were introduced in kernel v2.6.22 (2007), one year after the release of your distribution.

We can't probe for the existance of files in the local toolchain before compilation with kdbuild, so we have to stub out the code for glibc version that are known to lack features. This isn't particularly nice, but it should work.

Does the attached patch work for you?

Thanks,
Daniel

From 37871360c096b68928750903ac29968a7932e873 Mon Sep 17 00:00:00 2001
From: Daniel Mack <[email protected]>
Date: Fri, 3 Apr 2015 12:41:52 +0200
Subject: [PATCH] samples: kdbus: stub out code for glibc < 2.7

Andrew Morton reports the following build error in samples/kdbus on Fedora Core 6:

samples/kdbus/kdbus-workers.c:73:26: error: sys/signalfd.h: No such file or directory samples/kdbus/kdbus-workers.c: In function 'master_new':
samples/kdbus/kdbus-workers.c:231: warning: implicit declaration of function 'signalfd' samples/kdbus/kdbus-workers.c:231: error: 'SFD_CLOEXEC' undeclared (first use in this function) samples/kdbus/kdbus-workers.c:231: error: (Each undeclared identifier is reported only once samples/kdbus/kdbus-workers.c:231: error: for each function it appears in.) samples/kdbus/kdbus-workers.c: In function 'master_handle_signal':
samples/kdbus/kdbus-workers.c:406: error: storage size of 'val' isn't known samples/kdbus/kdbus-workers.c:406: warning: unused variable 'val' samples/kdbus/kdbus-workers.c: In function 'child_run':
samples/kdbus/kdbus-workers.c:773: error: 'CLOCK_MONOTONIC_COARSE' undeclared (first use in this function) samples/kdbus/kdbus-workers.c: In function 'bus_open_connection':
samples/kdbus/kdbus-workers.c:1038: error: 'O_CLOEXEC' undeclared (first use in this function) samples/kdbus/kdbus-workers.c: In function 'bus_make':
samples/kdbus/kdbus-workers.c:1275: error: 'O_CLOEXEC' undeclared (first use in this function)

Fedora Core 6 was released in 2006, which predates the introduction of signalfds in the kernel (v2.6.22, 2007).

The example cannot be built without signalfds, and kbuild cannot depend on specific features of the local libc when building userspace executables, so we have to work around the issue by checking for specific glibc versions at compile time and stub the entire thing if it can't be compiled.

---
 samples/kdbus/kdbus-workers.c | 23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)
samples/kdbus/kdbus-workers.c +21 -2
index d1d8f7a..e9ecec8 100644
@@ -57,6 +57,12 @@
  * top-down, but requires some forward-declarations. Just ignore those.
  */
 
+#include <stdio.h>
+#include <stdlib.h>
+
+/* glibc < 2.7 does not ship sys/signalfd.h */
+#if __GLIBC__ >= 2 && __GLIBC_MINOR__ >= 7
+
 #include <ctype.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -65,8 +71,6 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
 #include <sys/mman.h>
 #include <sys/poll.h>
@@ -1324,3 +1328,18 @@ static int bus_make(uid_t uid, const char *name)
 
 	return fd;
 }
+
+#else
+
+#warning "Skipping compilation due to unsupported libc version"
+
+int main(int argc, char **argv)
+{
+	fprintf(stderr,
+		"Compilation of %s was skipped due to unsupported libc.\n",
+		argv[0]);
+
+	return EXIT_FAILURE;
+}
+
+#endif /* libc sanity check */
Signature · 1 line
2.3.4
#31 lore raw

PATCH v4 14/14 kdbus: add selftests

In reply to Greg Kroah-Hartman

From: Daniel Mack <[email protected]>

This patch adds an extensive test suite for kdbus that checks the most important code paths in the driver. The idea is to extend the test suite over time.

Also, this code can serve as another example for how to use the kernel API from userspace.

The code in the kdbus test suite makes use of the ioctl wrappers provided by samples/kdbus/kdbus-api.h.

---
 tools/testing/selftests/Makefile                  |    1 +
 tools/testing/selftests/kdbus/.gitignore          |    3 +
 tools/testing/selftests/kdbus/Makefile            |   46 +
 tools/testing/selftests/kdbus/kdbus-enum.c        |   94 ++
 tools/testing/selftests/kdbus/kdbus-enum.h        |   14 +
 tools/testing/selftests/kdbus/kdbus-test.c        |  923 ++++++++++++
 tools/testing/selftests/kdbus/kdbus-test.h        |   85 ++
 tools/testing/selftests/kdbus/kdbus-util.c        | 1615 +++++++++++++++++++++
 tools/testing/selftests/kdbus/kdbus-util.h        |  222 +++
 tools/testing/selftests/kdbus/test-activator.c    |  318 ++++
 tools/testing/selftests/kdbus/test-attach-flags.c |  750 ++++++++++
 tools/testing/selftests/kdbus/test-benchmark.c    |  451 ++++++
 tools/testing/selftests/kdbus/test-bus.c          |  175 +++
 tools/testing/selftests/kdbus/test-chat.c         |  122 ++
 tools/testing/selftests/kdbus/test-connection.c   |  616 ++++++++
 tools/testing/selftests/kdbus/test-daemon.c       |   65 +
 tools/testing/selftests/kdbus/test-endpoint.c     |  341 +++++
 tools/testing/selftests/kdbus/test-fd.c           |  789 ++++++++++
 tools/testing/selftests/kdbus/test-free.c         |   64 +
 tools/testing/selftests/kdbus/test-match.c        |  441 ++++++
 tools/testing/selftests/kdbus/test-message.c      |  731 ++++++++++
 tools/testing/selftests/kdbus/test-metadata-ns.c  |  506 +++++++
 tools/testing/selftests/kdbus/test-monitor.c      |  176 +++
 tools/testing/selftests/kdbus/test-names.c        |  194 +++
 tools/testing/selftests/kdbus/test-policy-ns.c    |  632 ++++++++
 tools/testing/selftests/kdbus/test-policy-priv.c  | 1269 ++++++++++++++++
 tools/testing/selftests/kdbus/test-policy.c       |   80 +
 tools/testing/selftests/kdbus/test-sync.c         |  369 +++++
 tools/testing/selftests/kdbus/test-timeout.c      |   99 ++
 29 files changed, 11191 insertions(+)
 create mode 100644 tools/testing/selftests/kdbus/.gitignore
 create mode 100644 tools/testing/selftests/kdbus/Makefile
 create mode 100644 tools/testing/selftests/kdbus/kdbus-enum.c
 create mode 100644 tools/testing/selftests/kdbus/kdbus-enum.h
 create mode 100644 tools/testing/selftests/kdbus/kdbus-test.c
 create mode 100644 tools/testing/selftests/kdbus/kdbus-test.h
 create mode 100644 tools/testing/selftests/kdbus/kdbus-util.c
 create mode 100644 tools/testing/selftests/kdbus/kdbus-util.h
 create mode 100644 tools/testing/selftests/kdbus/test-activator.c
 create mode 100644 tools/testing/selftests/kdbus/test-attach-flags.c
 create mode 100644 tools/testing/selftests/kdbus/test-benchmark.c
 create mode 100644 tools/testing/selftests/kdbus/test-bus.c
 create mode 100644 tools/testing/selftests/kdbus/test-chat.c
 create mode 100644 tools/testing/selftests/kdbus/test-connection.c
 create mode 100644 tools/testing/selftests/kdbus/test-daemon.c
 create mode 100644 tools/testing/selftests/kdbus/test-endpoint.c
 create mode 100644 tools/testing/selftests/kdbus/test-fd.c
 create mode 100644 tools/testing/selftests/kdbus/test-free.c
 create mode 100644 tools/testing/selftests/kdbus/test-match.c
 create mode 100644 tools/testing/selftests/kdbus/test-message.c
 create mode 100644 tools/testing/selftests/kdbus/test-metadata-ns.c
 create mode 100644 tools/testing/selftests/kdbus/test-monitor.c
 create mode 100644 tools/testing/selftests/kdbus/test-names.c
 create mode 100644 tools/testing/selftests/kdbus/test-policy-ns.c
 create mode 100644 tools/testing/selftests/kdbus/test-policy-priv.c
 create mode 100644 tools/testing/selftests/kdbus/test-policy.c
 create mode 100644 tools/testing/selftests/kdbus/test-sync.c
 create mode 100644 tools/testing/selftests/kdbus/test-timeout.c
tools/testing/selftests/Makefile +1 -0
index 4e511221a0c1..7b51cceae9dd 100644
@@ -5,6 +5,7 @@ TARGETS += exec
 TARGETS += firmware
 TARGETS += ftrace
 TARGETS += kcmp
+TARGETS += kdbus
 TARGETS += memfd
 TARGETS += memory-hotplug
 TARGETS += mount
tools/testing/selftests/kdbus/.gitignore new file +3 -0
new file mode 100644
index 000000000000..7b421f76c888
@@ -0,0 +1,3 @@
+*.7
+manpage.*
+*.proc
tools/testing/selftests/kdbus/Makefile new file +46 -0

46 lines not on this page · raw message

tools/testing/selftests/kdbus/kdbus-enum.c new file +94 -0

94 lines not on this page · raw message

tools/testing/selftests/kdbus/kdbus-enum.h new file +14 -0
new file mode 100644
index 000000000000..a67cec3512a7
@@ -0,0 +1,14 @@
+/*
+ * Copyright (C) 2013-2015 Kay Sievers
+ *
+ * kdbus is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the
+ * Free Software Foundation; either version 2.1 of the License, or (at
+ * your option) any later version.
+ */
+#pragma once
+
+const char *enum_CMD(long long id);
+const char *enum_MSG(long long id);
+const char *enum_MATCH(long long id);
+const char *enum_PAYLOAD(long long id);
tools/testing/selftests/kdbus/kdbus-test.c new file +923 -0

923 lines not on this page · raw message

tools/testing/selftests/kdbus/kdbus-test.h new file +85 -0

85 lines not on this page · raw message

tools/testing/selftests/kdbus/kdbus-util.c new file +1615 -0

1,615 lines not on this page · raw message

tools/testing/selftests/kdbus/kdbus-util.h new file +222 -0

213 lines not on this page · raw message

tools/testing/selftests/kdbus/test-activator.c new file +318 -0

318 lines not on this page · raw message

tools/testing/selftests/kdbus/test-attach-flags.c new file +750 -0

750 lines not on this page · raw message

tools/testing/selftests/kdbus/test-benchmark.c new file +451 -0

451 lines not on this page · raw message

tools/testing/selftests/kdbus/test-bus.c new file +175 -0

175 lines not on this page · raw message

tools/testing/selftests/kdbus/test-chat.c new file +122 -0

122 lines not on this page · raw message

tools/testing/selftests/kdbus/test-connection.c new file +616 -0

616 lines not on this page · raw message

tools/testing/selftests/kdbus/test-daemon.c new file +65 -0

65 lines not on this page · raw message

tools/testing/selftests/kdbus/test-endpoint.c new file +341 -0

341 lines not on this page · raw message

tools/testing/selftests/kdbus/test-fd.c new file +789 -0

789 lines not on this page · raw message

tools/testing/selftests/kdbus/test-free.c new file +64 -0

64 lines not on this page · raw message

tools/testing/selftests/kdbus/test-match.c new file +441 -0

441 lines not on this page · raw message

tools/testing/selftests/kdbus/test-message.c new file +731 -0

731 lines not on this page · raw message

tools/testing/selftests/kdbus/test-metadata-ns.c new file +506 -0

506 lines not on this page · raw message

tools/testing/selftests/kdbus/test-monitor.c new file +176 -0

176 lines not on this page · raw message

tools/testing/selftests/kdbus/test-names.c new file +194 -0

194 lines not on this page · raw message

tools/testing/selftests/kdbus/test-policy-ns.c new file +632 -0

632 lines not on this page · raw message

tools/testing/selftests/kdbus/test-policy-priv.c new file +1269 -0

1,269 lines not on this page · raw message

tools/testing/selftests/kdbus/test-policy.c new file +80 -0

80 lines not on this page · raw message

tools/testing/selftests/kdbus/test-sync.c new file +369 -0

369 lines not on this page · raw message

tools/testing/selftests/kdbus/test-timeout.c new file +99 -0

99 lines not on this page · raw message

The rest of this diff is not shown. View the raw message.

Signature · 1 line
2.3.1

Part of the diff is not shown here. View the raw message.

#32 lore raw

PATCH 1/1 kdbus: fix minor typo in the walk-through example

In reply to Greg Kroah-Hartman

s/receveiver/receiver/

---
 samples/kdbus/kdbus-workers.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)
samples/kdbus/kdbus-workers.c +2 -2
index d1d8f7a7697b..d331e0186899 100644
@@ -787,8 +787,8 @@ static int child_run(struct child *c)
 	 * The 2nd item contains a vector to memory we want to send. It
 	 * can be content of any type. In our case, we're sending a one-byte
 	 * string only. The memory referenced by this item will be copied into
-	 * the pool of the receveiver connection, and does not need to be
-	 * valid after the command is employed.
+	 * the pool of the receiver connection, and does not need to be valid
+	 * after the command is employed.
 	 */
 	item = KDBUS_ITEM_NEXT(item);
 	item->type = KDBUS_ITEM_PAYLOAD_VEC;
Signature · 1 line
2.3.2
author
#33 lore raw

Re: [PATCH] kdbus: fix minor typo in the walk-through example

Quoted 6 lines · Nicolas Iooss

s/receveiver/receiver/

---
 samples/kdbus/kdbus-workers.c | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

Now applied, thanks.

greg k-h

#34 lore raw
In reply to Greg Kroah-Hartman
Quoted 4 lines · Greg Kroah-Hartman

kdbus is a kernel-level IPC implementation that aims for resemblance to the the protocol layer with the existing userspace D-Bus daemon while enabling some features that couldn't be implemented before in userspace.

[...]

What changed from last time?

The discussion from last time about performance seems to have stalled. kdbus is supposed to be fast -- that seems to be a large part of the point. Can anyone comment on how fast it actually is. I'm curious about:

- The time it takes to do the ioctl to send a message

- The time it takes to receive a message (poll + whatever ioctls)

- The time it takes to transfer a memfd (I don't care about how long
it takes to create or map a memfd -- that's exactly the same between kdbus and any other memfds user, I imagine)

- The time it takes to connect

I'm also interested in whether the current design is actually amenable to good performance. I'm told that it is, but ISTM there's a lot of heavyweight stuff going on with each send operation that will be hard to remove.

--Andy

#35 lore raw

Hi

On Tue, Mar 17, 2015 at 8:24 PM, Andy Lutomirski <[email protected]> wrote:
[...]

Quoted 6 lines

Can anyone comment on how fast it actually is. I'm curious about:

- The time it takes to do the ioctl to send a message

- The time it takes to receive a message (poll + whatever ioctls)

I'm not sure I can gather useful absolute data here. This highly depends on how you call it, how often you call it, what payloads you pass, what machine you're on.. you know all that.

So here's a flamegraph for you (which I use for comparisons to UDS):
http://people.freedesktop.org/~dvdhrm/kdbus_8kb.svg

This program sends unicast messages on kdbus and UDS, exactly the same number of times with the same 8kb payload. No parsing, no marshaling is done, just plain message passing. The interesting spikes are sys_read(), sys_write() and the 3 kdbus sys_ioctl(). Everything else should be ignored.

sys_read() and sys_ioctl(KDBUS_CMD_RECV) are about the same. But note that we don't copy any payload in RECV, so it scales O(1) compared to message-size.

sys_write() is about 3x faster than sys_ioctl(KDBUS_CMD_WRITE).

I see lots of room for improvement in both RECV and SEND. Caching the namespaces on a connection, would get rid of kdbus_queue_entry_install() in RECV, thus speeding it up by ~30%. In SEND, we could merge the kvec and iovec copying, to avoid calling shmem_begin_write() twice. We should also stop allocating management structures that are not used (like for metadata, if no metadata is transmitted). We should use stack-space for small ioctl objects, instead of memdup_user(). And so on.. Oh, and locking can be reduced. We haven't even looked at rcu, yet (though that's mostly interesting for policy and broadcasts, not unicasts).

Quoted 3 lines

- The time it takes to transfer a memfd (I don't care about how long
it takes to create or map a memfd -- that's exactly the same between kdbus and any other memfds user, I imagine)

The time to transmit a memfd is the same as to transmit a 64-byte payload. Ok, you also get to install the fd into the fd-table, but that's true regardless of the transport.
Here's a graph for 64byte transfers (otherwise, same as above):
http://people.freedesktop.org/~dvdhrm/kdbus_64b.svg

Quoted 1 line

- The time it takes to connect

No idea, never measured it. Why is it of interest?

Quoted 4 lines

I'm also interested in whether the current design is actually amenable to good performance. I'm told that it is, but ISTM there's a lot of heavyweight stuff going on with each send operation that will be hard to remove.

I disagree. What heavyweight stuff is going on?

Thanks
David

#36 lore raw
Quoted 24 lines · David Herrmann

Hi

On Tue, Mar 17, 2015 at 8:24 PM, Andy Lutomirski <[email protected]> wrote:
[...]

Quoted 6 lines

Can anyone comment on how fast it actually is. I'm curious about:

- The time it takes to do the ioctl to send a message

- The time it takes to receive a message (poll + whatever ioctls)

I'm not sure I can gather useful absolute data here. This highly depends on how you call it, how often you call it, what payloads you pass, what machine you're on.. you know all that.

So here's a flamegraph for you (which I use for comparisons to UDS):
http://people.freedesktop.org/~dvdhrm/kdbus_8kb.svg

This program sends unicast messages on kdbus and UDS, exactly the same number of times with the same 8kb payload. No parsing, no marshaling is done, just plain message passing. The interesting spikes are sys_read(), sys_write() and the 3 kdbus sys_ioctl(). Everything else should be ignored.

Quoted 5 lines

sys_read() and sys_ioctl(KDBUS_CMD_RECV) are about the same. But note that we don't copy any payload in RECV, so it scales O(1) compared to message-size.

sys_write() is about 3x faster than sys_ioctl(KDBUS_CMD_WRITE).

Is that factor of 3 for 8 kb payloads? If so, I expect it's a factor of much worse than 3 for small payloads.

Quoted 22 lines

I see lots of room for improvement in both RECV and SEND. Caching the namespaces on a connection, would get rid of kdbus_queue_entry_install() in RECV, thus speeding it up by ~30%. In SEND, we could merge the kvec and iovec copying, to avoid calling shmem_begin_write() twice. We should also stop allocating management structures that are not used (like for metadata, if no metadata is transmitted). We should use stack-space for small ioctl objects, instead of memdup_user(). And so on.. Oh, and locking can be reduced. We haven't even looked at rcu, yet (though that's mostly interesting for policy and broadcasts, not unicasts).

Quoted 3 lines

- The time it takes to transfer a memfd (I don't care about how long
it takes to create or map a memfd -- that's exactly the same between kdbus and any other memfds user, I imagine)

The time to transmit a memfd is the same as to transmit a 64-byte payload. Ok, you also get to install the fd into the fd-table, but that's true regardless of the transport.
Here's a graph for 64byte transfers (otherwise, same as above):
http://people.freedesktop.org/~dvdhrm/kdbus_64b.svg

Quoted 3 lines
Quoted 1 line

- The time it takes to connect

No idea, never measured it. Why is it of interest?

Gah, sorry, bad terminology. I mean the time it takes to send a message to a receiver that you haven't sent to before.

(The kdbus terminology is weird. You don't send to "endpoints", you don't "connect" to other participants, and it's not even clear to me what a participant in the bus is called.)

Quoted 7 lines
Quoted 5 lines

I'm also interested in whether the current design is actually amenable to good performance. I'm told that it is, but ISTM there's a lot of heavyweight stuff going on with each send operation that will be hard to remove.

I disagree. What heavyweight stuff is going on?

At least metadata generation, metadata free, and policy db checks seem expensive. It could be worth running a bunch of copies of your benchmark on different cores and seeing how it scales.

--Andy

#37 lore raw

Hi

Quoted 1 line · Andy Lutomirski

On Wed, Mar 18, 2015 at 6:54 AM, David Herrmann <[email protected]> wrote:

[...]

Quoted 6 lines
Quoted 6 lines

This program sends unicast messages on kdbus and UDS, exactly the same number of times with the same 8kb payload. No parsing, no marshaling is done, just plain message passing. The interesting spikes are sys_read(), sys_write() and the 3 kdbus sys_ioctl(). Everything else should be ignored.

Quoted 8 lines
Quoted 5 lines

sys_read() and sys_ioctl(KDBUS_CMD_RECV) are about the same. But note that we don't copy any payload in RECV, so it scales O(1) compared to message-size.

sys_write() is about 3x faster than sys_ioctl(KDBUS_CMD_WRITE).

Is that factor of 3 for 8 kb payloads? If so, I expect it's a factor of much worse than 3 for small payloads.

Yes, factor of 3x for 8kb payloads. ~3.8x for 64byte payloads (see the second flamegraph I linked, which contains data for 64byte payloads (which is basically nothing)).

Quoted 6 lines
Quoted 3 lines
Quoted 1 line

- The time it takes to connect

No idea, never measured it. Why is it of interest?

Gah, sorry, bad terminology. I mean the time it takes to send a message to a receiver that you haven't sent to before.

Cold message transactions are horribly slow for both, kdbus and UDS, and the performance varies heavily (factor of 10x). I haven't figured out whether it's icache/dcache misses, cold branch predictor, process mem faults, scheduler, whatever..

What I can say, is the kdbus paths are more expensive, in both LOC and execution time. We might be able to improve the cold-transaction performance with _unlikely_() annotations, shortcuts, etc. But I want much more benchmark data before I try to outsmart the compiler. It works reasonably well right now.

Note that from a algorithmic view, there's no difference between the first transaction and a following transaction. All relevant accesses are O(1).

(Actually looking at the numbers again, worst-case vs. average-case in message transaction is exactly 10x for both, UDS and kdbus. Skipping the first couple, I get <2x. std-dev is roughly 2%)

Quoted 3 lines

(The kdbus terminology is weird. You don't send to "endpoints", you don't "connect" to other participants, and it's not even clear to me what a participant in the bus is called.)

A participant is called a "connection" or "peer" (I prefer 'peer'). It "connects" to a bus via an endpoint of the bus. Endpoints are file-system entries and can be shared, and usually are. Unlike binder, kdbus does not know peer-to-peer links. That is, there is never (not even a temporary) link between only two peers. Messages are always sent to the bus, and the bus makes sure only the addressed recipients will get the message.

Quoted 11 lines
Quoted 7 lines
Quoted 5 lines

I'm also interested in whether the current design is actually amenable to good performance. I'm told that it is, but ISTM there's a lot of heavyweight stuff going on with each send operation that will be hard to remove.

I disagree. What heavyweight stuff is going on?

At least metadata generation, metadata free, and policy db checks seem expensive. It could be worth running a bunch of copies of your benchmark on different cores and seeing how it scales.

metadata handling is local to the connection that sends the message. It does not affect the overall performance of other bus operations in parallel. Furthermore, it's way faster than collecting the "same" data via /proc, so I don't think it slows down the overall transaction at all. If a receiver doesn't want metadata, it should not request it (by setting the receiver-metadata-mask). If a sender doesn't like the overhead, it should not send the metadata (by setting the sender-metadata-mask). Only if both peers set the metadata mask, it will be transmitted.

The policy-db does indeed look like a bottleneck. Until v2 we used to have a policy-cache, which I ripped out as it didn't meet our expectations. There are plans to rewrite it, but it's low-priority. Thing is, policy-setup is bus-privileged. As long as it's done in a sane manner (keeping the entries per name minimal), the hash-table based name-lookup gives suitable performance. Only if the number of entries per name rises, it gets problematic due to O(n) list-traversal. But even that could be optimized without a policy cache, by merging matching entries (see kdbus_policy_db_entry_access). Furthermore, the policy-db is skipped for privileged peers or if both, sender and recipient, trust each other (eg., have the same endpoint+uid). Thus, if you have a trusted transaction, the policy-db is skipped, anyway.

One real bottleneck I see is the name-registry rwlock. Acquiring/releasing names is still a heavy operation, as it blocks the whole bus due to acquiring the write-lock. Again, I have plans to optimize it (srcu would be an idea, syncing on name-acquire/release), but it's an implementation detail.

Thanks
David

#38 lore raw
Quoted 15 lines · David Herrmann

Hi

Quoted 1 line · Andy Lutomirski

On Wed, Mar 18, 2015 at 6:54 AM, David Herrmann <[email protected]> wrote:

[...]

Quoted 10 lines
Quoted 10 lines

This program sends unicast messages on kdbus and UDS, exactly the same number of times with the same 8kb payload. No parsing, no marshaling is done, just plain message passing. The interesting spikes are sys_read(), sys_write() and the 3 kdbus sys_ioctl(). Everything else should be ignored.

sys_read() and sys_ioctl(KDBUS_CMD_RECV) are about the same. But note that we don't copy any payload in RECV, so it scales O(1) compared to message-size.

Quoted 8 lines
Quoted 4 lines
Quoted 1 line

sys_write() is about 3x faster than sys_ioctl(KDBUS_CMD_WRITE).

Is that factor of 3 for 8 kb payloads? If so, I expect it's a factor of much worse than 3 for small payloads.

Yes, factor of 3x for 8kb payloads. ~3.8x for 64byte payloads (see the second flamegraph I linked, which contains data for 64byte payloads (which is basically nothing)).

I find this surprising. Are both of them so slow that copying 8kb is negligible? That's rather sad.

Quoted 47 lines
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines

- The time it takes to connect

No idea, never measured it. Why is it of interest?

Gah, sorry, bad terminology. I mean the time it takes to send a message to a receiver that you haven't sent to before.

Cold message transactions are horribly slow for both, kdbus and UDS, and the performance varies heavily (factor of 10x). I haven't figured out whether it's icache/dcache misses, cold branch predictor, process mem faults, scheduler, whatever..

What I can say, is the kdbus paths are more expensive, in both LOC and execution time. We might be able to improve the cold-transaction performance with _unlikely_() annotations, shortcuts, etc. But I want much more benchmark data before I try to outsmart the compiler. It works reasonably well right now.

Note that from a algorithmic view, there's no difference between the first transaction and a following transaction. All relevant accesses are O(1).

(Actually looking at the numbers again, worst-case vs. average-case in message transaction is exactly 10x for both, UDS and kdbus. Skipping the first couple, I get <2x. std-dev is roughly 2%)

Quoted 3 lines

(The kdbus terminology is weird. You don't send to "endpoints", you don't "connect" to other participants, and it's not even clear to me what a participant in the bus is called.)

A participant is called a "connection" or "peer" (I prefer 'peer'). It "connects" to a bus via an endpoint of the bus. Endpoints are file-system entries and can be shared, and usually are. Unlike binder, kdbus does not know peer-to-peer links. That is, there is never (not even a temporary) link between only two peers. Messages are always sent to the bus, and the bus makes sure only the addressed recipients will get the message.

Quoted 8 lines
Quoted 7 lines
Quoted 5 lines

I'm also interested in whether the current design is actually amenable to good performance. I'm told that it is, but ISTM there's a lot of heavyweight stuff going on with each send operation that will be hard to remove.

I disagree. What heavyweight stuff is going on?

Quoted 7 lines
Quoted 3 lines

At least metadata generation, metadata free, and policy db checks seem expensive. It could be worth running a bunch of copies of your benchmark on different cores and seeing how it scales.

metadata handling is local to the connection that sends the message. It does not affect the overall performance of other bus operations in parallel.

Sure it does if it writes to shared cachelines. Given that you're incrementing refcounts, I'm reasonable sure that you're touching lots of shared cachelines.

Quoted 7 lines

Furthermore, it's way faster than collecting the "same" data via /proc, so I don't think it slows down the overall transaction at all. If a receiver doesn't want metadata, it should not request it (by setting the receiver-metadata-mask). If a sender doesn't like the overhead, it should not send the metadata (by setting the sender-metadata-mask). Only if both peers set the metadata mask, it will be transmitted.

But you're comparing to the wrong thing, IMO. Of course it's much faster than /proc hackery, but it's probably much slower to do the metadata operation once per message than to do it when you connect to the endpoint. (Gah! It's a "bus" that could easily have tons of users but a single "endpoint". I'm still not used to it.)

Quoted 14 lines

The policy-db does indeed look like a bottleneck. Until v2 we used to have a policy-cache, which I ripped out as it didn't meet our expectations. There are plans to rewrite it, but it's low-priority. Thing is, policy-setup is bus-privileged. As long as it's done in a sane manner (keeping the entries per name minimal), the hash-table based name-lookup gives suitable performance. Only if the number of entries per name rises, it gets problematic due to O(n) list-traversal. But even that could be optimized without a policy cache, by merging matching entries (see kdbus_policy_db_entry_access). Furthermore, the policy-db is skipped for privileged peers or if both, sender and recipient, trust each other (eg., have the same endpoint+uid). Thus, if you have a trusted transaction, the policy-db is skipped, anyway.

Yeah, that's reasonable. I don't see any obvious way around that. (The policy semantics are still insane wrt connections with multiple names, though, but that should have nothing to do with performance. Insanity for historical reasons is still insanity.)

--Andy

#39 lore raw

Hi

Quoted 8 lines · Andy Lutomirski
Quoted 3 lines · David Herrmann

metadata handling is local to the connection that sends the message. It does not affect the overall performance of other bus operations in parallel.

Sure it does if it writes to shared cachelines. Given that you're incrementing refcounts, I'm reasonable sure that you're touching lots of shared cachelines.

Ok, sure, but it's still mostly local to the sending task. We take locks and ref-counts on the task-struct and mm, which is for most parts local to the CPU the task runs on. But this is inherent to accessing this kind of data, which is the fundamental difference in our views here, as seen below..

Quoted 8 lines
Quoted 7 lines

Furthermore, it's way faster than collecting the "same" data via /proc, so I don't think it slows down the overall transaction at all. If a receiver doesn't want metadata, it should not request it (by setting the receiver-metadata-mask). If a sender doesn't like the overhead, it should not send the metadata (by setting the sender-metadata-mask). Only if both peers set the metadata mask, it will be transmitted.

Quoted 5 lines

But you're comparing to the wrong thing, IMO. Of course it's much faster than /proc hackery, but it's probably much slower to do the metadata operation once per message than to do it when you connect to the endpoint. (Gah! It's a "bus" that could easily have tons of users but a single "endpoint". I'm still not used to it.)

Yes, of course your assumption is right if you compare against per-connection caches, instead of per-message metadata. But we do support _both_ use-cases, so we don't impose any policy. We still believe "live"-metadata is a crucial feature of kdbus, despite the known performance penalties.

Thanks
David

#40 lore raw
Quoted 12 lines · David Herrmann

Hi

Quoted 8 lines · Andy Lutomirski
Quoted 3 lines · David Herrmann

metadata handling is local to the connection that sends the message. It does not affect the overall performance of other bus operations in parallel.

Sure it does if it writes to shared cachelines. Given that you're incrementing refcounts, I'm reasonable sure that you're touching lots of shared cachelines.

Quoted 5 lines

Ok, sure, but it's still mostly local to the sending task. We take locks and ref-counts on the task-struct and mm, which is for most parts local to the CPU the task runs on. But this is inherent to accessing this kind of data, which is the fundamental difference in our views here, as seen below..

You're also refcounting the struct cred, and there's no good reason for that to be local. (It might be a bit more local than intended because of the absurd things that the key subsystem does to struct cred, but IMO users should turn that off or the kernel should fix it.)

Even more globally, I think you're touching init_user_ns's refcount in most scenarios. That's about as global as it gets.

(Also, is there an easy benchmark to see how much time it takes to send and receive metadata? I tried to get the kdbus test to do this, and I failed. I probably did it wrong.)

Quoted 15 lines
Quoted 9 lines
Quoted 8 lines

Furthermore, it's way faster than collecting the "same" data via /proc, so I don't think it slows down the overall transaction at all. If a receiver doesn't want metadata, it should not request it (by setting the receiver-metadata-mask). If a sender doesn't like the overhead, it should not send the metadata (by setting the sender-metadata-mask). Only if both peers set the metadata mask, it will be transmitted.

Quoted 5 lines

But you're comparing to the wrong thing, IMO. Of course it's much faster than /proc hackery, but it's probably much slower to do the metadata operation once per message than to do it when you connect to the endpoint. (Gah! It's a "bus" that could easily have tons of users but a single "endpoint". I'm still not used to it.)

Quoted 5 lines

Yes, of course your assumption is right if you compare against per-connection caches, instead of per-message metadata. But we do support _both_ use-cases, so we don't impose any policy. We still believe "live"-metadata is a crucial feature of kdbus, despite the known performance penalties.

And you still have not described a single use case for which it's better than per-connection metadata.

I'm against adding a feature to the kernel (per-message metadata) if the primary reason it's being added is to support what appears to be a misfeature in *new* userspace that we have no obligation whatsoever to be ABI-compatible with. This is especially true if that feature is slower than the alternatives. This is even more true if this feature is *inconsistent* with legacy userspace (i.e. userspace dbus).

I could be wrong about the lack of use cases. If so, please enlighten me.

--Andy

#41 lore raw
Quoted 28 lines · Andy Lutomirski
Quoted 11 lines · David Herrmann

Hi

Quoted 7 lines · Andy Lutomirski

On Thu, Mar 19, 2015 at 4:26 AM, David Herrmann <[email protected]> wrote:

But you're comparing to the wrong thing, IMO. Of course it's much faster than /proc hackery, but it's probably much slower to do the metadata operation once per message than to do it when you connect to the endpoint. (Gah! It's a "bus" that could easily have tons of users but a single "endpoint". I'm still not used to it.)

Quoted 5 lines

Yes, of course your assumption is right if you compare against per-connection caches, instead of per-message metadata. But we do support _both_ use-cases, so we don't impose any policy. We still believe "live"-metadata is a crucial feature of kdbus, despite the known performance penalties.

And you still have not described a single use case for which it's better than per-connection metadata.

I'm against adding a feature to the kernel (per-message metadata) if the primary reason it's being added is to support what appears to be a misfeature in *new* userspace that we have no obligation whatsoever to be ABI-compatible with. This is especially true if that feature is slower than the alternatives. This is even more true if this feature is *inconsistent* with legacy userspace (i.e. userspace dbus).

Quoted 2 lines

I could be wrong about the lack of use cases. If so, please enlighten me.

Please. I asked this same question on the first revision of this code to go up for review and I was told that there are no existing users of dbus that cares.

Right now this looks like a case of the deplorable habit of getting review comments, and then resubmitting a patch with trivial changes and ignoring the substantial review comments. Again and again and again until the reviewers run out of energy to object.

That seems like a very poor way to add a new ABI that to the kernel that we will have to support for the next 20 years, because huge swaths of userspace are going to be using it.

Not to mention that per message meta-data is known to be both a performance problem but also that it tends to turn in to a security problem. It is the kind of information that is easy to mess up and hard to support long term.

So I agree with Andy that we really need something better than it would be nice to have.

Eric

#42 lore raw
In reply to Andy Lutomirski

Hi

Quoted 11 lines · Andy Lutomirski
Quoted 10 lines · David Herrmann
Quoted 8 lines · Andy Lutomirski
Quoted 3 lines · David Herrmann

metadata handling is local to the connection that sends the message. It does not affect the overall performance of other bus operations in parallel.

Sure it does if it writes to shared cachelines. Given that you're incrementing refcounts, I'm reasonable sure that you're touching lots of shared cachelines.

Quoted 7 lines
Quoted 5 lines

Ok, sure, but it's still mostly local to the sending task. We take locks and ref-counts on the task-struct and mm, which is for most parts local to the CPU the task runs on. But this is inherent to accessing this kind of data, which is the fundamental difference in our views here, as seen below..

You're also refcounting the struct cred

No?

We do ref-count the group-info, but that is actually redundant as we just copy the IDs. We should drop this, since group-info of 'current' can be accessed right away. I noted it down.

Quoted 7 lines

and there's no good reason
for that to be local. (It might be a bit more local than intended because of the absurd things that the key subsystem does to struct cred, but IMO users should turn that off or the kernel should fix it.)

Even more globally, I think you're touching init_user_ns's refcount in most scenarios. That's about as global as it gets.

get_user_ns() in metadata.c is a workaround (as the comment there explains). With better export-helpers for caps, we can simply drop it. It's conditional on KDBUS_ATTACH_CAPS, anyway.

Quoted 3 lines

(Also, is there an easy benchmark to see how much time it takes to send and receive metadata? I tried to get the kdbus test to do this, and I failed. I probably did it wrong.)

patch for out-of-tree kdbus:
https://gist.github.com/dvdhrm/3ac4339bf94fadc13b98

Update it to pass _KDBUS_ATTACH_ALL for both arguments of kdbus_conn_update_attach_flags().

Quoted 19 lines
Quoted 19 lines
Quoted 8 lines
Quoted 7 lines

Furthermore, it's way faster than collecting the "same" data via /proc, so I don't think it slows down the overall transaction at all. If a receiver doesn't want metadata, it should not request it (by setting the receiver-metadata-mask). If a sender doesn't like the overhead, it should not send the metadata (by setting the sender-metadata-mask). Only if both peers set the metadata mask, it will be transmitted.

Quoted 5 lines

But you're comparing to the wrong thing, IMO. Of course it's much faster than /proc hackery, but it's probably much slower to do the metadata operation once per message than to do it when you connect to the endpoint. (Gah! It's a "bus" that could easily have tons of users but a single "endpoint". I'm still not used to it.)

Yes, of course your assumption is right if you compare against per-connection caches, instead of per-message metadata. But we do support _both_ use-cases, so we don't impose any policy. We still believe "live"-metadata is a crucial feature of kdbus, despite the known performance penalties.

[...]

Quoted 2 lines

This is even more true if this feature
is *inconsistent* with legacy userspace (i.e. userspace dbus).

Live metadata is already supported on UDS via SCM_CREDENTIALS, we just extend it to other metadata items. It's not a new invention by us. Debian code-search on SO_PASSCRED and SCM_CREDENTIALS gives lots of results.

Netlink, as a major example of an existing bus API, already uses SCM_CREDENTIALS as primary way to transmit metadata.

Quoted 1 line

I could be wrong about the lack of use cases. If so, please enlighten me.

We have several dbus APIs that allow clients to register as a special handler/controller/etc. (eg., see systemd-logind TakeControl()). The API provider checks the privileges of a client on registration and then just tracks the client ID. This way, the client can be privileged when asking for special access, then drop privileges and still use the interface. You cannot re-connect in between, as the API provider tracks your bus ID. Without message-metadata, all your (other) calls on this bus would always be treated as privileged. We *really* want to avoid this.

Another example is logging, where we want exact data at the time a message is logged. Otherwise, the data is useless. With message-metadata, you can figure out the exact situation a process was in when a specific message was logged. Furthermore, it is impossible to read such data from /proc, as the process might already be dead. Which is a _real_ problem right now!
Similarly, system monitoring wants message-metadata for the same reasons. And it needs to be reliable, you don't want malicious sandboxes to mess with your logs.

kdbus is a _bus_, not a p2p channel. Thus, a peer may talk to multiple destinations, and it may want to look different to each of them. DBus method-calls allow 'syscall'-ish behavior when calling into other processes. We *want* to be able to drop privileges after doing process setup. We want further bus-calls to no longer be treated privileged.

Furthermore, DBus was designed to allow peers to track other peers (which is why it always had the NameOwnerChanged signal). This is an essential feature, that simplifies access-management considerably, as you can cache it together with the unique name of a peer. We only open a single connection to a bus. glib, libdbus, efl, ell, qt, sd-bus, and others use cached bus-connections that are shared by all code of a single thread. Hence, the bus connection is kinda part of the process itself, like stdin/stdout. Without message-metadata, it is impossible to ever drop privileges on a bus, without losing all state.

Thanks
David

#43 lore raw
Quoted 20 lines · David Herrmann

Hi

Quoted 17 lines · Andy Lutomirski
Quoted 15 lines · David Herrmann
Quoted 8 lines · Andy Lutomirski
Quoted 3 lines · David Herrmann

metadata handling is local to the connection that sends the message. It does not affect the overall performance of other bus operations in parallel.

Sure it does if it writes to shared cachelines. Given that you're incrementing refcounts, I'm reasonable sure that you're touching lots of shared cachelines.

Ok, sure, but it's still mostly local to the sending task. We take locks and ref-counts on the task-struct and mm, which is for most parts local to the CPU the task runs on. But this is inherent to accessing this kind of data, which is the fundamental difference in our views here, as seen below..

Quoted 7 lines
Quoted 1 line

You're also refcounting the struct cred

No?

We do ref-count the group-info, but that is actually redundant as we just copy the IDs. We should drop this, since group-info of 'current' can be accessed right away. I noted it down.

OK

Quoted 6 lines
Quoted 6 lines

and there's no good reason
for that to be local. (It might be a bit more local than intended because of the absurd things that the key subsystem does to struct cred, but IMO users should turn that off or the kernel should fix it.)

Quoted 6 lines
Quoted 2 lines

Even more globally, I think you're touching init_user_ns's refcount in most scenarios. That's about as global as it gets.

get_user_ns() in metadata.c is a workaround (as the comment there explains). With better export-helpers for caps, we can simply drop it. It's conditional on KDBUS_ATTACH_CAPS, anyway.

Fair enough.

Quoted 44 lines
Quoted 4 lines

(Also, is there an easy benchmark to see how much time it takes to send and receive metadata? I tried to get the kdbus test to do this, and I failed. I probably did it wrong.)

patch for out-of-tree kdbus:
https://gist.github.com/dvdhrm/3ac4339bf94fadc13b98

Update it to pass _KDBUS_ATTACH_ALL for both arguments of kdbus_conn_update_attach_flags().

Quoted 19 lines
Quoted 19 lines
Quoted 8 lines
Quoted 7 lines

Furthermore, it's way faster than collecting the "same" data via /proc, so I don't think it slows down the overall transaction at all. If a receiver doesn't want metadata, it should not request it (by setting the receiver-metadata-mask). If a sender doesn't like the overhead, it should not send the metadata (by setting the sender-metadata-mask). Only if both peers set the metadata mask, it will be transmitted.

Quoted 5 lines

But you're comparing to the wrong thing, IMO. Of course it's much faster than /proc hackery, but it's probably much slower to do the metadata operation once per message than to do it when you connect to the endpoint. (Gah! It's a "bus" that could easily have tons of users but a single "endpoint". I'm still not used to it.)

Yes, of course your assumption is right if you compare against per-connection caches, instead of per-message metadata. But we do support _both_ use-cases, so we don't impose any policy. We still believe "live"-metadata is a crucial feature of kdbus, despite the known performance penalties.

[...]

Quoted 2 lines

This is even more true if this feature
is *inconsistent* with legacy userspace (i.e. userspace dbus).

Live metadata is already supported on UDS via SCM_CREDENTIALS, we just extend it to other metadata items. It's not a new invention by us. Debian code-search on SO_PASSCRED and SCM_CREDENTIALS gives lots of results.

Netlink, as a major example of an existing bus API, already uses SCM_CREDENTIALS as primary way to transmit metadata.

Quoted 1 line

I could be wrong about the lack of use cases. If so, please enlighten me.

Quoted 9 lines

We have several dbus APIs that allow clients to register as a special handler/controller/etc. (eg., see systemd-logind TakeControl()). The API provider checks the privileges of a client on registration and then just tracks the client ID. This way, the client can be privileged when asking for special access, then drop privileges and still use the interface. You cannot re-connect in between, as the API provider tracks your bus ID. Without message-metadata, all your (other) calls on this bus would always be treated as privileged. We *really* want to avoid this.

Connect twice?

You *already* have to reconnect or connect twice because you have per-connection metadata. That's part of my problem with this scheme -- you support *both styles*, which seems like it'll give you most of the downsides of both without the upsides.

Quoted 3 lines

Another example is logging, where we want exact data at the time a message is logged. Otherwise, the data is useless.

Why?

No, really, why is exact data at the time of logging so important? It sounds nice, but I really don't see it.

Quoted 8 lines

With
message-metadata, you can figure out the exact situation a process was in when a specific message was logged. Furthermore, it is impossible to read such data from /proc, as the process might already be dead. Which is a _real_ problem right now!
Similarly, system monitoring wants message-metadata for the same reasons. And it needs to be reliable, you don't want malicious sandboxes to mess with your logs.

Huh? A "malicious sandbox" can always impersonate itself, whether by connecting and handing off a connection or simply by relaying mesages.

Quoted 6 lines

kdbus is a _bus_, not a p2p channel. Thus, a peer may talk to multiple destinations, and it may want to look different to each of them. DBus method-calls allow 'syscall'-ish behavior when calling into other processes. We *want* to be able to drop privileges after doing process setup. We want further bus-calls to no longer be treated privileged.

You could have an IOCTL that re-captures your connection metata.

Quoted 10 lines

Furthermore, DBus was designed to allow peers to track other peers (which is why it always had the NameOwnerChanged signal). This is an essential feature, that simplifies access-management considerably, as you can cache it together with the unique name of a peer. We only open a single connection to a bus. glib, libdbus, efl, ell, qt, sd-bus, and others use cached bus-connections that are shared by all code of a single thread. Hence, the bus connection is kinda part of the process itself, like stdin/stdout. Without message-metadata, it is impossible to ever drop privileges on a bus, without losing all state.

See above about an IOCTL that re-captures your connection metadata.

Again, you seem to be arguing that per-connection metadata is bad, but you still have an implementation of per-connection metadata, so you still have all these problems.

I'm actually okay with per-message metadata in principle, but I'd like to see evidence (with numbers, please) that a send+recv of per-message metadata is *not* significantly slower than a recv of already-captured per-connection metadata. If this is in fact the case, then maybe you should trash per-connection metadata instead and the legacy compatibility code can figure out a way to deal with it. IMO that would be a pretty nice outcome, since you would never have to worry whether your connection to the bus is inadvertantly privileged.

(Also, FWIW, it seems like what you really want is a capability model, in which you grab a handle to some service and that handle captures all your privileges wrt that service. Per-message metadata is even farther from this than per-connection-to-the-bus metadata, but neither one is particularly close.)

Quoted 3 lines

Thanks
David

Signature · 2 lines
Andy Lutomirski
AMA Capital Management, LLC
#44 lore raw

Hi

Quoted 1 line · Andy Lutomirski

On Wed, Mar 25, 2015 at 10:29 AM, David Herrmann <[email protected]> wrote:

[...]

Quoted 12 lines
Quoted 11 lines
Quoted 1 line

I could be wrong about the lack of use cases. If so, please enlighten me.

We have several dbus APIs that allow clients to register as a special handler/controller/etc. (eg., see systemd-logind TakeControl()). The API provider checks the privileges of a client on registration and then just tracks the client ID. This way, the client can be privileged when asking for special access, then drop privileges and still use the interface. You cannot re-connect in between, as the API provider tracks your bus ID. Without message-metadata, all your (other) calls on this bus would always be treated as privileged. We *really* want to avoid this.

Quoted 1 line

Connect twice?

The remote peer might cache your connection ID to track you. You have to use the same connection to talk to that peer, in this given scenario. That's how D-Bus1 is used today, and we have to follow the semantics.

Quoted 4 lines

You *already* have to reconnect or connect twice because you have per-connection metadata. That's part of my problem with this scheme -- you support *both styles*, which seems like it'll give you most of the downsides of both without the upsides.

Not necessarily. Connection metadata describes the state at the time you connected to the bus. If someone ask for this information, they will get exactly that. In this model, you cannot drop privileges, if you need to be privileged during setup.
If someone asks for per-message metadata, they better ought not ask for per-connection creds. It's not the information they're looking for, so it will not match the data that at the time the message was sent.

There is no immediate need to make both match. For security decisions, we mandate per-message creds. Per-connection creds are for backwards-compatibility to dbus1 and for passive introspection of bus connections.

Quoted 8 lines
Quoted 3 lines

Another example is logging, where we want exact data at the time a message is logged. Otherwise, the data is useless.

Why?

No, really, why is exact data at the time of logging so important? It sounds nice, but I really don't see it.

Example: If you don't have message-metadata, you don't know the thread which sent a log-message. In a multi-threaded application, that's incredibly useful information.

After all, logging is all about correct data. Logging creds that were not effective at the time the message was sent is futile.

Quoted 8 lines
Quoted 6 lines

kdbus is a _bus_, not a p2p channel. Thus, a peer may talk to multiple destinations, and it may want to look different to each of them. DBus method-calls allow 'syscall'-ish behavior when calling into other processes. We *want* to be able to drop privileges after doing process setup. We want further bus-calls to no longer be treated privileged.

You could have an IOCTL that re-captures your connection metata.

I don't see how this makes the model easier, or more predictable. On the contrary, per-connection metadata is no longer compatible to UDS / dbus1, nor is a per-message metadata concept reliable.

Quoted 11 lines
Quoted 10 lines

Furthermore, DBus was designed to allow peers to track other peers (which is why it always had the NameOwnerChanged signal). This is an essential feature, that simplifies access-management considerably, as you can cache it together with the unique name of a peer. We only open a single connection to a bus. glib, libdbus, efl, ell, qt, sd-bus, and others use cached bus-connections that are shared by all code of a single thread. Hence, the bus connection is kinda part of the process itself, like stdin/stdout. Without message-metadata, it is impossible to ever drop privileges on a bus, without losing all state.

Quoted 5 lines

See above about an IOCTL that re-captures your connection metadata.

Again, you seem to be arguing that per-connection metadata is bad, but you still have an implementation of per-connection metadata, so you still have all these problems.

I don't see why we get the problems of per-connection metadata. Just because you _can_ use it, doesn't mean you should use it for all imaginable use-cases. The same goes for reading information from /proc. There are valid use-cases to do so, but also a lot of cases where it will not provide the information you want.

Quoted 8 lines

I'm actually okay with per-message metadata in principle, but I'd like to see evidence (with numbers, please) that a send+recv of per-message metadata is *not* significantly slower than a recv of already-captured per-connection metadata. If this is in fact the case, then maybe you should trash per-connection metadata instead and the legacy compatibility code can figure out a way to deal with it. IMO that would be a pretty nice outcome, since you would never have to worry whether your connection to the bus is inadvertantly privileged.

Per-message metadata makes SEND about 25% slower, if you transmit the full set of all possible information. Just 3% if you only use PIDs+UIDs. The expensive metadata is cgroup-path and exe-path. If a service needs that information, however, and if that information is not guaranteed to be up-to-date, the service _will_ go and look it up in /proc or somewhere else, which is certainly a whole lot more expensive than the code in kdbus.

In general, there seems to be a number of misconception in this thread about what kdbus is supposed to be. We're not inventing something new here with a clean slate, but we're moving parts of an existing implementation that has tons of users into the kernel, in order to fix issues that cannot be fixed otherwise in userspace (most notably, the race gaps that exist when retrieving per-message metadata). Therefore, we have to keep existing semantics stable, otherwise the exercise is somewhat pointless.

Thanks
David

#45 lore raw
Quoted 17 lines · David Herrmann

Hi

Quoted 1 line · Andy Lutomirski

On Wed, Mar 25, 2015 at 10:29 AM, David Herrmann <[email protected]> wrote:

[...]

Quoted 12 lines
Quoted 11 lines
Quoted 1 line

I could be wrong about the lack of use cases. If so, please enlighten me.

We have several dbus APIs that allow clients to register as a special handler/controller/etc. (eg., see systemd-logind TakeControl()). The API provider checks the privileges of a client on registration and then just tracks the client ID. This way, the client can be privileged when asking for special access, then drop privileges and still use the interface. You cannot re-connect in between, as the API provider tracks your bus ID. Without message-metadata, all your (other) calls on this bus would always be treated as privileged. We *really* want to avoid this.

Quoted 6 lines
Quoted 1 line

Connect twice?

The remote peer might cache your connection ID to track you. You have to use the same connection to talk to that peer, in this given scenario. That's how D-Bus1 is used today, and we have to follow the semantics.

That's yet another reason that you really ought to disconnect and reconnect after a privilege change -- the remote peer might remember you.

The "might" will be a big problem. Users of kdbus can't rely on any particular concept of privilege because you have too many of them.

Quoted 15 lines
Quoted 5 lines

You *already* have to reconnect or connect twice because you have per-connection metadata. That's part of my problem with this scheme -- you support *both styles*, which seems like it'll give you most of the downsides of both without the upsides.

Not necessarily. Connection metadata describes the state at the time you connected to the bus. If someone ask for this information, they will get exactly that. In this model, you cannot drop privileges, if you need to be privileged during setup.
If someone asks for per-message metadata, they better ought not ask for per-connection creds. It's not the information they're looking for, so it will not match the data that at the time the message was sent.

Quoted 4 lines

There is no immediate need to make both match. For security decisions, we mandate per-message creds. Per-connection creds are for backwards-compatibility to dbus1 and for passive introspection of bus connections.

Backwards compatibility doesn't magically exempt security considerations. If new code is insecure when talking to a legacy service, it's still insecure.

[...]

Quoted 10 lines
Quoted 3 lines

Again, you seem to be arguing that per-connection metadata is bad, but you still have an implementation of per-connection metadata, so you still have all these problems.

I don't see why we get the problems of per-connection metadata. Just because you _can_ use it, doesn't mean you should use it for all imaginable use-cases. The same goes for reading information from /proc. There are valid use-cases to do so, but also a lot of cases where it will not provide the information you want.

Then you'll need to document really carefully which metadata is used for which service. This actually seems impossible to do, since some services will exist in legacy and kdbus forms.

Quoted 9 lines
Quoted 8 lines

I'm actually okay with per-message metadata in principle, but I'd like to see evidence (with numbers, please) that a send+recv of per-message metadata is *not* significantly slower than a recv of already-captured per-connection metadata. If this is in fact the case, then maybe you should trash per-connection metadata instead and the legacy compatibility code can figure out a way to deal with it. IMO that would be a pretty nice outcome, since you would never have to worry whether your connection to the bus is inadvertantly privileged.

Quoted 7 lines

Per-message metadata makes SEND about 25% slower, if you transmit the full set of all possible information. Just 3% if you only use PIDs+UIDs. The expensive metadata is cgroup-path and exe-path. If a service needs that information, however, and if that information is not guaranteed to be up-to-date, the service _will_ go and look it up in /proc or somewhere else, which is certainly a whole lot more expensive than the code in kdbus.

Can you give actual numbers, in ns or cycles, of how much overhead metadata adds?

Quoted 9 lines

In general, there seems to be a number of misconception in this thread about what kdbus is supposed to be. We're not inventing something new here with a clean slate, but we're moving parts of an existing implementation that has tons of users into the kernel, in order to fix issues that cannot be fixed otherwise in userspace (most notably, the race gaps that exist when retrieving per-message metadata). Therefore, we have to keep existing semantics stable, otherwise the exercise is somewhat pointless.

IOW you're taking something that you dislike aspects of and shoving most of it in the kernel. That guarantees us an API in the kernel that even the creators don't really like. This is, IMO, very unfortunate.

Have you considered porting the kdbus per-message metadata mechanism to UDS?

--Andy

#46 lore raw
Quoted 47 lines · Andy Lutomirski
Quoted 17 lines · David Herrmann

Hi

Quoted 1 line · Andy Lutomirski

On Wed, Mar 25, 2015 at 10:29 AM, David Herrmann <[email protected]> wrote:

[...]

Quoted 12 lines
Quoted 11 lines
Quoted 1 line

I could be wrong about the lack of use cases. If so, please enlighten me.

We have several dbus APIs that allow clients to register as a special handler/controller/etc. (eg., see systemd-logind TakeControl()). The API provider checks the privileges of a client on registration and then just tracks the client ID. This way, the client can be privileged when asking for special access, then drop privileges and still use the interface. You cannot re-connect in between, as the API provider tracks your bus ID. Without message-metadata, all your (other) calls on this bus would always be treated as privileged. We *really* want to avoid this.

Quoted 6 lines
Quoted 1 line

Connect twice?

The remote peer might cache your connection ID to track you. You have to use the same connection to talk to that peer, in this given scenario. That's how D-Bus1 is used today, and we have to follow the semantics.

That's yet another reason that you really ought to disconnect and reconnect after a privilege change -- the remote peer might remember you.

The "might" will be a big problem. Users of kdbus can't rely on any particular concept of privilege because you have too many of them.

Quoted 15 lines
Quoted 5 lines

You *already* have to reconnect or connect twice because you have per-connection metadata. That's part of my problem with this scheme -- you support *both styles*, which seems like it'll give you most of the downsides of both without the upsides.

Not necessarily. Connection metadata describes the state at the time you connected to the bus. If someone ask for this information, they will get exactly that. In this model, you cannot drop privileges, if you need to be privileged during setup.
If someone asks for per-message metadata, they better ought not ask for per-connection creds. It's not the information they're looking for, so it will not match the data that at the time the message was sent.

Quoted 7 lines
Quoted 4 lines

There is no immediate need to make both match. For security decisions, we mandate per-message creds. Per-connection creds are for backwards-compatibility to dbus1 and for passive introspection of bus connections.

Backwards compatibility doesn't magically exempt security considerations.

No one is arguing that.

Quoted 50 lines

If new code is insecure when talking to a legacy
service, it's still insecure.

[...]

Quoted 10 lines
Quoted 3 lines

Again, you seem to be arguing that per-connection metadata is bad, but you still have an implementation of per-connection metadata, so you still have all these problems.

I don't see why we get the problems of per-connection metadata. Just because you _can_ use it, doesn't mean you should use it for all imaginable use-cases. The same goes for reading information from /proc. There are valid use-cases to do so, but also a lot of cases where it will not provide the information you want.

Then you'll need to document really carefully which metadata is used for which service. This actually seems impossible to do, since some services will exist in legacy and kdbus forms.

Quoted 9 lines
Quoted 8 lines

I'm actually okay with per-message metadata in principle, but I'd like to see evidence (with numbers, please) that a send+recv of per-message metadata is *not* significantly slower than a recv of already-captured per-connection metadata. If this is in fact the case, then maybe you should trash per-connection metadata instead and the legacy compatibility code can figure out a way to deal with it. IMO that would be a pretty nice outcome, since you would never have to worry whether your connection to the bus is inadvertantly privileged.

Quoted 7 lines

Per-message metadata makes SEND about 25% slower, if you transmit the full set of all possible information. Just 3% if you only use PIDs+UIDs. The expensive metadata is cgroup-path and exe-path. If a service needs that information, however, and if that information is not guaranteed to be up-to-date, the service _will_ go and look it up in /proc or somewhere else, which is certainly a whole lot more expensive than the code in kdbus.

Can you give actual numbers, in ns or cycles, of how much overhead metadata adds?

Quoted 9 lines

In general, there seems to be a number of misconception in this thread about what kdbus is supposed to be. We're not inventing something new here with a clean slate, but we're moving parts of an existing implementation that has tons of users into the kernel, in order to fix issues that cannot be fixed otherwise in userspace (most notably, the race gaps that exist when retrieving per-message metadata). Therefore, we have to keep existing semantics stable, otherwise the exercise is somewhat pointless.

Quoted 4 lines

IOW you're taking something that you dislike aspects of and shoving most of it in the kernel. That guarantees us an API in the kernel that even the creators don't really like. This is, IMO, very unfortunate.

This is a misrepresentation of what David wrote. We do want this API regardless of dbus1 compatibility, but compatibility is by itself a sufficient motivation. A further motivation is reliable introspection, since this meta-data allows listing current peers on the bus and showing their identities. That's hugely useful to make the bus transparent to admins.

Quoted 1 line

Have you considered porting the kdbus per-message metadata mechanism to UDS?

As outlined before, enhancing UDS by porting the metadata mechanism from kdbus would not be sufficient to solve all the problems we need to solve, so it is not something we are currently working on.

Cheers,

Tom

#47 lore raw
Quoted 7 lines · Tom Gundersen
Quoted 5 lines · Andy Lutomirski

IOW you're taking something that you dislike aspects of and shoving most of it in the kernel. That guarantees us an API in the kernel that even the creators don't really like. This is, IMO, very unfortunate.

Quoted 6 lines

This is a misrepresentation of what David wrote. We do want this API regardless of dbus1 compatibility, but compatibility is by itself a sufficient motivation. A further motivation is reliable introspection, since this meta-data allows listing current peers on the bus and showing their identities. That's hugely useful to make the bus transparent to admins.

I've heard the following use cases for per-connection metadata:

- Authenticating to dbus1 services.

- Identifying connected users for admin diagnostics.

I've heard the following use cases for per-message metadata:

- Logging.

- Authenticating to kdbus services that want this style of authentication.

The only reasonable conclusion I've been able to draw is that the dbus community intends to use *both* per-connection and per-message metadata for authentication. This means that, as a general rule, dropping privileges while you have an open kdbus connection has poorly defined effects.

It's particularly alarming that, when I express a concern about logging, the kdbus authors cite authentication as an alternate justification, and, when I cite a concern about authentication, the kdbus authors cite logging as an alternative justificaiton.

This is simply not okay for a modern interface, and in my opinion the kernel should not carry code to support new APIs with weakly defined security semantics. It's important that one be able to tell what the security implications of one's code is without cross-referencing with the implementation of the server's you're interacting with.

To top that off, the kdbus policy mechanism has truly bizarre effects with respect to services that have unique ids and well-known names. That, too, is apparently for compatibility.

This all feels to me like a total of about four people are going to understand the tangle that is kdbus security, and that's bad. I think that the kernel should insist that new security-critical mechanisms make sense and be hard to misuse. The current design of kdbus, in contrast, feel like it will be very hard to use correctly.

--Andy

#48 lore raw

Hi

Quoted 15 lines · Andy Lutomirski
Quoted 7 lines · Tom Gundersen
Quoted 5 lines · Andy Lutomirski

IOW you're taking something that you dislike aspects of and shoving most of it in the kernel. That guarantees us an API in the kernel that even the creators don't really like. This is, IMO, very unfortunate.

Quoted 6 lines

This is a misrepresentation of what David wrote. We do want this API regardless of dbus1 compatibility, but compatibility is by itself a sufficient motivation. A further motivation is reliable introspection, since this meta-data allows listing current peers on the bus and showing their identities. That's hugely useful to make the bus transparent to admins.

Quoted 3 lines

I've heard the following use cases for per-connection metadata:

- Authenticating to dbus1 services.

Not necessarily authentication, but we need to support the legacy API, for whatever reason it was used by old applications. But..

Quoted 7 lines

- Identifying connected users for admin diagnostics.

I've heard the following use cases for per-message metadata:

- Logging.

- Authenticating to kdbus services that want this style of authentication.

..please note that authentication on DBus has always been done with per-message metadata (see polkit history). However, this had to be reverted some years ago as it is racy (it used /proc for that, which can be exploited by exec'ing setuid binaries). However, the per-message metadata authentication worked very well for _years_ (minus the race..), so this is already a well-established scheme. With kdbus we can finally implement this in a race-free manner.

[...]

Quoted 5 lines

This is simply not okay for a modern interface, and in my opinion the kernel should not carry code to support new APIs with weakly defined security semantics. It's important that one be able to tell what the security implications of one's code is without cross-referencing with the implementation of the server's you're interacting with.

Again, I disagree. Our concepts are established and used on UDS and DBus for decades.

Yes, we provide two ways to retrieve metadata, but the kernel offers several more paths to gather that information. Just because those APIs are available does not mean they should be used for authentication. We mandate per-message metadata. If applications use per-connection metadata, /proc, netlink, or random data, they're doing it wrong.

Furthermore, dbus provides pretty complete and straightforward libraries which hide that from you. If you use glib, qt or sd-bus, you don't even need to deal with all that.

Quoted 9 lines

To top that off, the kdbus policy mechanism has truly bizarre effects with respect to services that have unique ids and well-known names. That, too, is apparently for compatibility.

This all feels to me like a total of about four people are going to understand the tangle that is kdbus security, and that's bad. I think that the kernel should insist that new security-critical mechanisms make sense and be hard to misuse. The current design of kdbus, in contrast, feel like it will be very hard to use correctly.

Native kdbus clients are authenticated with their credentials at time of method call. Legacy clients will always have their credentials at time of connect in effect. No fallbacks, no choices. It's a simple question whether it's a legacy client or not.
Sounds simple to me.

Thanks
David

Nacked-by NAK
#49 lore raw
Quoted 76 lines · David Herrmann

Hi

Quoted 2 lines · Andy Lutomirski
Quoted 1 line · Tom Gundersen

On Tue, Mar 31, 2015 at 3:58 PM, Andy Lutomirski

Quoted 9 lines · <[email protected]> wrote:
Quoted 9 lines
Quoted 5 lines

IOW you're taking something that you dislike aspects of and shoving most of it in the kernel. That guarantees us an API in the kernel that even the creators don't really like. This is, IMO, very unfortunate.

This is a misrepresentation of what David wrote. We do want this API regardless of dbus1 compatibility, but compatibility is by itself a sufficient motivation. A further motivation is reliable

introspection,

Quoted 7 lines
Quoted 3 lines

since this meta-data allows listing current peers on the bus and showing their identities. That's hugely useful to make the bus transparent to admins.

I've heard the following use cases for per-connection metadata:

- Authenticating to dbus1 services.

Not necessarily authentication, but we need to support the legacy API, for whatever reason it was used by old applications. But..

Quoted 7 lines

- Identifying connected users for admin diagnostics.

I've heard the following use cases for per-message metadata:

- Logging.

- Authenticating to kdbus services that want this style of

authentication.

..please note that authentication on DBus has always been done with per-message metadata (see polkit history). However, this had to be reverted some years ago as it is racy (it used /proc for that, which can be exploited by exec'ing setuid binaries). However, the per-message metadata authentication worked very well for _years_ (minus the race..), so this is already a well-established scheme. With kdbus we can finally implement this in a race-free manner.

[...]

Quoted 5 lines

This is simply not okay for a modern interface, and in my opinion the kernel should not carry code to support new APIs with weakly defined security semantics. It's important that one be able to tell what the security implications of one's code is without cross-referencing with the implementation of the server's you're interacting with.

Again, I disagree. Our concepts are established and used on UDS and DBus for decades.

Yes, we provide two ways to retrieve metadata, but the kernel offers several more paths to gather that information. Just because those APIs are available does not mean they should be used for authentication. We mandate per-message metadata. If applications use per-connection metadata, /proc, netlink, or random data, they're doing it wrong.

Furthermore, dbus provides pretty complete and straightforward libraries which hide that from you. If you use glib, qt or sd-bus, you don't even need to deal with all that.

Quoted 6 lines

To top that off, the kdbus policy mechanism has truly bizarre effects with respect to services that have unique ids and well-known names. That, too, is apparently for compatibility.

This all feels to me like a total of about four people are going to understand the tangle that is kdbus security, and that's bad. I

think

Quoted 3 lines

that the kernel should insist that new security-critical mechanisms make sense and be hard to misuse. The current design of kdbus, in contrast, feel like it will be very hard to use correctly.

Quoted 5 lines

Native kdbus clients are authenticated with their credentials at time of method call. Legacy clients will always have their credentials at time of connect in effect. No fallbacks, no choices. It's a simple question whether it's a legacy client or not.
Sounds simple to me.

So I just took a slightly deeper look and the user namespace bits are wrong. Both in implementation and in design.

Passing "capabilities" to user space for reasons of authentication is wrong and a maintenance nightmare. Further the capabilities maintainer Serge Hallyn has not been copied.

There are several other pieces of information in your meta data like cmdline that I have similar concerns about, but are I am less familiar with, and have looked at less.

Which leads my to conclude that in its current form kdbus is inappropriate for inclusion in the kernel.

The code is dangerously and inappropriately wrong and comes with a huge maintenance obligation to people outside of kdbus.

The only way I can see this code being responsibly merged is for all if the metadata to be thrown out. The basics merged and then one small piece at a time with copious review and explanation the metadata be added back in.

If you can not throw out the meta data the kdbus code is too broken in concept to warrant serious consideration.

Eric

#50 lore raw
Quoted 85 lines · Eric W. Biederman
Quoted 76 lines · David Herrmann

Hi

Quoted 2 lines · Andy Lutomirski
Quoted 1 line · Tom Gundersen

On Tue, Mar 31, 2015 at 3:58 PM, Andy Lutomirski

Quoted 9 lines · <[email protected]> wrote:
Quoted 9 lines
Quoted 5 lines

IOW you're taking something that you dislike aspects of and shoving most of it in the kernel. That guarantees us an API in the kernel that even the creators don't really like. This is, IMO, very unfortunate.

This is a misrepresentation of what David wrote. We do want this API regardless of dbus1 compatibility, but compatibility is by itself a sufficient motivation. A further motivation is reliable

introspection,

Quoted 7 lines
Quoted 3 lines

since this meta-data allows listing current peers on the bus and showing their identities. That's hugely useful to make the bus transparent to admins.

I've heard the following use cases for per-connection metadata:

- Authenticating to dbus1 services.

Not necessarily authentication, but we need to support the legacy API, for whatever reason it was used by old applications. But..

Quoted 7 lines

- Identifying connected users for admin diagnostics.

I've heard the following use cases for per-message metadata:

- Logging.

- Authenticating to kdbus services that want this style of

authentication.

..please note that authentication on DBus has always been done with per-message metadata (see polkit history). However, this had to be reverted some years ago as it is racy (it used /proc for that, which can be exploited by exec'ing setuid binaries). However, the per-message metadata authentication worked very well for _years_ (minus the race..), so this is already a well-established scheme. With kdbus we can finally implement this in a race-free manner.

[...]

Quoted 5 lines

This is simply not okay for a modern interface, and in my opinion the kernel should not carry code to support new APIs with weakly defined security semantics. It's important that one be able to tell what the security implications of one's code is without cross-referencing with the implementation of the server's you're interacting with.

Again, I disagree. Our concepts are established and used on UDS and DBus for decades.

Yes, we provide two ways to retrieve metadata, but the kernel offers several more paths to gather that information. Just because those APIs are available does not mean they should be used for authentication. We mandate per-message metadata. If applications use per-connection metadata, /proc, netlink, or random data, they're doing it wrong.

Furthermore, dbus provides pretty complete and straightforward libraries which hide that from you. If you use glib, qt or sd-bus, you don't even need to deal with all that.

Quoted 6 lines

To top that off, the kdbus policy mechanism has truly bizarre effects with respect to services that have unique ids and well-known names. That, too, is apparently for compatibility.

This all feels to me like a total of about four people are going to understand the tangle that is kdbus security, and that's bad. I

think

Quoted 3 lines

that the kernel should insist that new security-critical mechanisms make sense and be hard to misuse. The current design of kdbus, in contrast, feel like it will be very hard to use correctly.

Quoted 5 lines

Native kdbus clients are authenticated with their credentials at time of method call. Legacy clients will always have their credentials at time of connect in effect. No fallbacks, no choices. It's a simple question whether it's a legacy client or not.
Sounds simple to me.

Quoted 4 lines

So I just took a slightly deeper look and the user namespace bits are wrong. Both in implementation and in design.

Passing "capabilities" to user space for reasons of authentication is wrong and a maintenance nightmare. Further the capabilities maintainer Serge Hallyn has not been copied.

I don't understand, where are passed that are not already exported today through /proc/ already? kdbus gathers this information in a race-free way, unlike having to dig this out of proc and hope that nothing has changed underneath you.

Quoted 1 line

There are several other pieces of information in your meta data like cmdline that I have similar concerns about, but are I am less familiar with, and have looked at less.

Again, cmdline is also exported today, why is passing that somehow not acceptable?

Quoted 1 line

Which leads my to conclude that in its current form kdbus is inappropriate for inclusion in the kernel.

Ah, so we should also remove those fields from /proc/ today as well, and just break all of userspace that relies on it today? Again, kdbus is just doing the same thing that userspace is doing today, but in a race-free manner.

Quoted 1 line

The code is dangerously and inappropriately wrong and comes with a huge maintenance obligation to people outside of kdbus.

How so? Please explain.

Oh, and please wrap your email properly, reading it this way is a horrible experience, you know better than that...

greg k-h

#51 lore raw
In reply to David Herrmann

[Trying again due to HTML mail goof. Trimming and responding better, too.]

Quoted 42 lines · David Herrmann

Hi

Quoted 15 lines · Andy Lutomirski
Quoted 7 lines · Tom Gundersen
Quoted 5 lines · Andy Lutomirski

IOW you're taking something that you dislike aspects of and shoving most of it in the kernel. That guarantees us an API in the kernel that even the creators don't really like. This is, IMO, very unfortunate.

Quoted 6 lines

This is a misrepresentation of what David wrote. We do want this API regardless of dbus1 compatibility, but compatibility is by itself a sufficient motivation. A further motivation is reliable introspection, since this meta-data allows listing current peers on the bus and showing their identities. That's hugely useful to make the bus transparent to admins.

Quoted 3 lines

I've heard the following use cases for per-connection metadata:

- Authenticating to dbus1 services.

Not necessarily authentication, but we need to support the legacy API, for whatever reason it was used by old applications. But..

Quoted 7 lines

- Identifying connected users for admin diagnostics.

I've heard the following use cases for per-message metadata:

- Logging.

- Authenticating to kdbus services that want this style of authentication.

..please note that authentication on DBus has always been done with per-message metadata (see polkit history). However, this had to be reverted some years ago as it is racy (it used /proc for that, which can be exploited by exec'ing setuid binaries). However, the per-message metadata authentication worked very well for _years_ (minus the race..), so this is already a well-established scheme. With kdbus we can finally implement this in a race-free manner.

[...]

Quoted 8 lines
Quoted 5 lines

This is simply not okay for a modern interface, and in my opinion the kernel should not carry code to support new APIs with weakly defined security semantics. It's important that one be able to tell what the security implications of one's code is without cross-referencing with the implementation of the server's you're interacting with.

Again, I disagree. Our concepts are established and used on UDS and DBus for decades.

SO_PASSCRED does not justify anything in my book. It was a mistake and it remains a minor disaster. Please don't use it as justification for something being a good idea.

Similarly, the fact that the *receiver* chooses which of SO_PASSCRED and SO_PEERCRED to use is awful.

Quoted 6 lines

Yes, we provide two ways to retrieve metadata, but the kernel offers several more paths to gather that information. Just because those APIs are available does not mean they should be used for authentication. We mandate per-message metadata. If applications use per-connection metadata, /proc, netlink, or random data, they're doing it wrong.

ISTM kdbus is trying to add three more interfaces, at least two of which are also doing it wrong. (Which two is debatable.)

Quoted 4 lines

Furthermore, dbus provides pretty complete and straightforward libraries which hide that from you. If you use glib, qt or sd-bus, you don't even need to deal with all that.

Libraries can't hide the issue of whether:

init_my_favorite_library();
connect_to_thingy();
setresuid(nobody);

is secure. It is either secure, insecure, or ambiguous. Unfortunately, kdbus as currently proposed is aiming for ambiguous.

Quoted 11 lines
Quoted 10 lines

To top that off, the kdbus policy mechanism has truly bizarre effects with respect to services that have unique ids and well-known names. That, too, is apparently for compatibility.

This all feels to me like a total of about four people are going to understand the tangle that is kdbus security, and that's bad. I think that the kernel should insist that new security-critical mechanisms make sense and be hard to misuse. The current design of kdbus, in contrast, feel like it will be very hard to use correctly.

Quoted 5 lines

Native kdbus clients are authenticated with their credentials at time of method call. Legacy clients will always have their credentials at time of connect in effect. No fallbacks, no choices. It's a simple question whether it's a legacy client or not.
Sounds simple to me.

I had the distinct impression that the kdbus-client-to-dbus1-server proxy used kdbus clients' connection metadata. I could be wrong here.

--Andy