diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-12-23 15:08:02 -0800 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-12-23 15:08:02 -0800 |
commit | 56b7f5d90856ead67c6619772f7e1e739cba1597 (patch) | |
tree | 427ca5cbf6b61a31253475dc85bcc8be6cb2406d | |
parent | e348a2398970c8fa7443cabacfdc9c576b62639f (diff) | |
download | patches-56b7f5d90856ead67c6619772f7e1e739cba1597.tar.gz |
delte kdbus patches
-rw-r--r-- | 0001-kdbus-add-documentation.patch | 1839 | ||||
-rw-r--r-- | 0002-kdbus-add-header-file.patch | 941 | ||||
-rw-r--r-- | 0003-kdbus-add-driver-skeleton-ioctl-entry-points-and-uti.patch | 1684 | ||||
-rw-r--r-- | 0004-kdbus-add-connection-pool-implementation.patch | 812 | ||||
-rw-r--r-- | 0005-kdbus-add-connection-queue-handling-and-message-vali.patch | 3470 | ||||
-rw-r--r-- | 0006-kdbus-add-code-to-gather-metadata.patch | 708 | ||||
-rw-r--r-- | 0007-kdbus-add-code-for-notifications-and-matches.patch | 858 | ||||
-rw-r--r-- | 0008-kdbus-add-code-for-buses-domains-and-endpoints.patch | 1873 | ||||
-rw-r--r-- | 0009-kdbus-add-name-registry-implementation.patch | 1031 | ||||
-rw-r--r-- | 0010-kdbus-add-policy-database-implementation.patch | 715 | ||||
-rw-r--r-- | 0011-kdbus-add-Makefile-Kconfig-and-MAINTAINERS-entry.patch | 94 | ||||
-rw-r--r-- | 0012-kdbus-add-selftests.patch | 7855 | ||||
-rw-r--r-- | d01.patch | 10 | ||||
-rw-r--r-- | pci-fix-name-of-enable-sysfs-file.patch | 57 | ||||
-rw-r--r-- | series | 13 |
15 files changed, 5 insertions, 21955 deletions
diff --git a/0001-kdbus-add-documentation.patch b/0001-kdbus-add-documentation.patch deleted file mode 100644 index a4c8025219c760..00000000000000 --- a/0001-kdbus-add-documentation.patch +++ /dev/null @@ -1,1839 +0,0 @@ -From 53f7cb9ca49d8a62cfc4c9740ffb34068c1599a6 Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 21:50:47 +0200 -Subject: [PATCH 01/12] kdbus: add documentation - -From: Daniel Mack <daniel@zonque.org> - -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 through ioctls -on /dev nodes. This patch adds detailed documentation about the kernel -level API design. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - Documentation/kdbus.txt | 1815 ++++++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 1815 insertions(+) - create mode 100644 Documentation/kdbus.txt - ---- /dev/null -+++ b/Documentation/kdbus.txt -@@ -0,0 +1,1815 @@ -+D-Bus is a system for powerful, easy to use interprocess communication (IPC). -+ -+The focus of this document is an overview of the low-level, native kernel D-Bus -+transport called kdbus. Kdbus in the kernel acts similar to a device driver, -+all communication between processes take place over special character device -+nodes in /dev/kdbus/. -+ -+For the general D-Bus protocol specification, the payload format, the -+marshaling, and the communication semantics, please refer to: -+ http://dbus.freedesktop.org/doc/dbus-specification.html -+ -+For a kdbus specific userspace library implementation please refer to: -+ http://cgit.freedesktop.org/systemd/systemd/tree/src/systemd/sd-bus.h -+ -+Articles about D-Bus and kdbus: -+ http://lwn.net/Articles/580194/ -+ -+ -+1. Terminology -+=============================================================================== -+ -+ Domain: -+ A domain is a named object containing a number of buses. A system -+ container that contains its own init system and users usually also -+ runs in its own kdbus domain. The /dev/kdbus/domain/<container-name>/ -+ directory shows up inside the domain as /dev/kdbus/. Every domain offers -+ its own "control" device node to create new buses or new sub-domains. -+ Domains have no connection to each other and cannot see nor talk to -+ each other. See section 5 for more details. -+ -+ 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 -+ /dev/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. See section 5 for more details. -+ -+ 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 section 5 for more details. -+ -+ Connection: -+ A connection to a bus is created by opening an endpoint device node of -+ a bus and becoming an active client with the HELLO exchange. Every -+ connected client connection has a unique identifier on the bus and can -+ address messages to every other connection on the same bus by using -+ the peer's connection id as the destination. -+ See section 6 for more details. -+ -+ Pool: -+ Each connection allocates a piece of shmem-backed memory that is used -+ to receive messages and answers to ioctl command from the kernel. It is -+ never used to send anything to the kernel. In order to access that memory, -+ userspace must mmap() it into its task. -+ See section 12 for more details. -+ -+ Well-known Name: -+ A connection can, in addition to its implicit unique connection id, request -+ the ownership of a textual well-known name. Well-known names are noted in -+ reverse-domain notation, such as com.example.service1. Connections offering -+ a service on a bus are usually reached by its well-known name. The analogy -+ of connection id and well-known name is an IP address and a DNS name -+ associated with that address. -+ -+ Message: -+ Connections can exchange messages with other connections by addressing -+ the peers with their connection id or well-known name. A message consists -+ of a message header with kernel-specific information on how to route the -+ message, and the message payload, which is a logical byte stream of -+ arbitrary size. Messages can carry additional file descriptors to be passed -+ from one connection to another. Every connection can specify which set of -+ metadata the kernel should attach to the message when it is delivered -+ to the receiving connection. Metadata contains information like: system -+ timestamps, uid, gid, tid, proc-starttime, well-known-names, process comm, -+ process exe, process argv, cgroup, capabilities, seclabel, audit session, -+ loginuid and the connection's human-readable name. -+ See section 7 and 13 for more details. -+ -+ Item: -+ The API of kdbus implements a notion of items, submitted through and -+ returned by most ioctls, and stored inside data structures in the -+ connection's pool. See section 4 for more details. -+ -+ Broadcast and Match: -+ Broadcast messages are potentially sent to all connections of a bus. By -+ default, the connections will not actually receive any of the sent -+ broadcast messages; only after installing a match for specific message -+ properties, a broadcast message passes this filter. -+ See section 10 for more details. -+ -+ Policy: -+ A policy is a set of rules that define which connections can see, talk to, -+ or register a well-know name on the bus. A policy is attached to buses and -+ custom endpoints, and modified by policy holder connection or owners of -+ custom endpoints. See section 11 for more details. -+ -+ Access rules to allow who can see a name on the bus are only checked on -+ custom endpoints. Policies may be defined with names that end with '.*'. -+ When matching a well-known name against such a wildcard entry, the last -+ part of the name is ignored and checked against the wildcard name without -+ the trailing '.*'. See section 11 for more details. -+ -+ Privileged bus users: -+ A user connecting to the bus is considered privileged if it is either the -+ creator of the bus, or if it has the CAP_IPC_OWNER capability flag set. -+ -+ -+2. Device Node Layout -+=============================================================================== -+ -+The kdbus interface is exposed through device nodes in /dev. -+ -+ /sys/bus/kdbus -+ `-- devices -+ |-- kdbus!0-system!bus -> ../../../devices/virtual/kdbus/kdbus!0-system!bus -+ |-- kdbus!2702-user!bus -> ../../../devices/virtual/kdbus/kdbus!2702-user!bus -+ |-- kdbus!2702-user!ep.app -> ../../../devices/virtual/kdbus/kdbus!2702-user!ep.app -+ `-- kdbus!control -> ../../../devices/kdbus!control -+ -+ /dev/kdbus -+ |-- control -+ |-- 0-system -+ | |-- bus -+ | `-- ep.apache -+ |-- 1000-user -+ | `-- bus -+ |-- 2702-user -+ | |-- bus -+ | `-- ep.app -+ `-- domain -+ |-- fedoracontainer -+ | |-- control -+ | |-- 0-system -+ | | `-- bus -+ | `-- 1000-user -+ | `-- bus -+ `-- mydebiancontainer -+ |-- control -+ `-- 0-system -+ `-- bus -+ -+Note: -+ The device node subdirectory layout is arranged that a future version of -+ kdbus could be implemented as a file system with a separate instance mounted -+ for each domain. For any future changes, this always needs to be kept -+ in mind. Also the dependency on udev's userspace hookups or sysfs attribute -+ use should be limited to the absolute minimum for the same reason. -+ -+ -+3. Data Structures and flags -+=============================================================================== -+ -+3.1 Data structures and interconnections -+---------------------------------------- -+ -+ +-------------------------------------------------------------------------+ -+ | Domain (Init Domain) | -+ | /dev/kdbus/control | -+ | +---------------------------------------------------------------------+ | -+ | | Bus (System Bus) | | -+ | | /dev/kdbus/0-system/ | | -+ | | +-------------------------------+ +-------------------------------+ | | -+ | | | Endpoint | | Endpoint | | | -+ | | | /dev/kdbus/0-system/bus | | /dev/kdbus/0-system/ep.app | | | -+ | | +-------------------------------+ +-------------------------------+ | | -+ | | +--------------+ +--------------+ +--------------+ +--------------+ | | -+ | | | Connection | | Connection | | Connection | | Connection | | | -+ | | | :1.22 | | :1.25 | | :1.55 | | :1.81 | | | -+ | | +--------------+ +--------------+ +--------------+ +--------------+ | | -+ | +---------------------------------------------------------------------+ | -+ | | -+ | +---------------------------------------------------------------------+ | -+ | | Bus (User Bus for UID 2702) | | -+ | | /dev/kdbus/2702-user/ | | -+ | | +-------------------------------+ +-------------------------------+ | | -+ | | | Endpoint | | Endpoint | | | -+ | | | /dev/kdbus/2702-user/bus | | /dev/kdbus/2702-user/ep.app | | | -+ | | +-------------------------------+ +-------------------------------+ | | -+ | | +--------------+ +--------------+ +--------------+ +--------------+ | | -+ | | | Connection | | Connection | | Connection | | Connection | | | -+ | | | :1.22 | | :1.25 | | :1.55 | | :1.81 | | | -+ | | +--------------+ +--------------+ +-------------------------------+ | | -+ | +---------------------------------------------------------------------+ | -+ | | -+ | +---------------------------------------------------------------------+ | -+ | | Domain (Container; inside it, fedoracontainer/ becomes /dev/kdbus/) | | -+ | | /dev/kdbus/domain/fedoracontainer/control | | -+ | | +-----------------------------------------------------------------+ | | -+ | | | Bus (System Bus of "fedoracontainer") | | | -+ | | | /dev/kdbus/domain/fedoracontainer/0-system/ | | | -+ | | | +-----------------------------+ | | | -+ | | | | Endpoint | | | | -+ | | | | /dev/.../0-system/bus | | | | -+ | | | +-----------------------------+ | | | -+ | | | +-------------+ +-------------+ | | | -+ | | | | Connection | | Connection | | | | -+ | | | | :1.22 | | :1.25 | | | | -+ | | | +-------------+ +-------------+ | | | -+ | | +-----------------------------------------------------------------+ | | -+ | | | | -+ | | +-----------------------------------------------------------------+ | | -+ | | | Bus (User Bus for UID 270 of "fedoracontainer") | | | -+ | | | /dev/kdbus/domain/fedoracontainer/2702-user/ | | | -+ | | | +-----------------------------+ | | | -+ | | | | Endpoint | | | | -+ | | | | /dev/.../2702-user/bus | | | | -+ | | | +-----------------------------+ | | | -+ | | | +-------------+ +-------------+ | | | -+ | | | | Connection | | Connection | | | | -+ | | | | :1.22 | | :1.25 | | | | -+ | | | +-------------+ +-------------+ | | | -+ | | +-----------------------------------------------------------------+ | | -+ | +---------------------------------------------------------------------+ | -+ +-------------------------------------------------------------------------+ -+ -+The above description uses the D-Bus notation of unique connection names that -+adds a ":1." prefix to the connection's unique ID. kbus itself doesn't -+use that notation, neither internally nor externally. However, libraries and -+other usespace code that aims for compatibility to D-Bus might. -+ -+3.2 Flags -+--------- -+ -+All ioctls used in the communication with the driver contain two 64-bit fields, -+'flags' and 'kernel_flags'. In 'flags', the behavior of the command can be -+tweaked, whereas in 'kernel_flags', the kernel driver writes back the mask of -+supported bits upon each call, and sets the KDBUS_FLAGS_KERNEL bit. This is a -+way to probe possible kernel features and make code forward and backward -+compatible. -+ -+All bits that are not recognized by the kernel in 'flags' are rejected, and the -+ioctl fails with -EINVAL. -+ -+ -+4. Items -+=============================================================================== -+ -+To flexibly augment transport structures used by kdbus, data blobs of type -+struct kdbus_item are used. An item has a fixed-sized header that only stores -+the type of the item and the overall size. The total size is variable and is -+in some cases defined by the item type, in other cases, they can be of -+arbitrary length (for instance, a string). -+ -+In the external kernel API, items are used for many ioctls to transport -+optional information from userspace to kernelspace. They are also used for -+information stored in a connection's pool, such as messages, name lists or -+requested connection information. -+ -+In all such occasions where items are used as part of the kdbus kernel API, -+they are embedded in structs that have an overall size of their own, so there -+can be many of them. -+ -+The kernel expects all items to be aligned to 8-byte boundaries. -+ -+A simple iterator in userspace would iterate over the items until the items -+have reached the embedding structure's overall size. An example implementation -+of such an iterator can be found in tools/testing/selftests/kdbus/kdbus-util.h. -+ -+ -+5. Creation of new domains, buses and endpoints -+=============================================================================== -+ -+The initial kdbus domain is unconditionally created by the kernel module. A -+domain contains a "control" device node which allows to create a new bus or -+domain. New domains do not have any buses created by default. -+ -+ -+5.1 Domains and buses -+--------------------- -+ -+Opening the control device node returns a file descriptor, it accepts the -+ioctls KDBUS_CMD_BUS_MAKE and KDBUS_CMD_DOMAIN_MAKE which specify the name of -+the new bus or domain to create. The control file descriptor needs to be kept -+open for the entire life-time of the created bus or domain, closing it will -+immediately cleanup the entire bus or domain and all its associated -+resources and connections. Every control file descriptor can only be used once -+to create a new bus or domain; from that point, it is not used for any -+further communication until the final close(). -+ -+Each bus will generate a random, 128-bit UUID upon creation. It will be -+returned to the creators of connections through kdbus_cmd_hello.id128 and can -+be used by userspace to uniquely identify buses, even across different machines -+or containers. The UUID will have its its variant bits set to 'DCE', and denote -+version 4 (random). -+ -+When a new domain is created, its structure in /dev/kdbus/<name>/ is a -+replication of what's initially created in /dev/kdbus. In fact, internally, -+a dummy default domain is set up when the driver is loaded. This allows -+userspace to bind-mount domain subtrees of /dev/kdbus into a container's -+filesystem view, and hence achieve complete isolation from the host's domain -+and those of other containers. -+ -+ -+5.2 Endpoints -+------------- -+ -+Endpoints are entry points to a bus. By default, each bus has a default -+endpoint called 'bus'. The bus owner has the ability to create custom -+endpoints with specific names, permissions, and policy databases (see below). -+ -+To create a custom endpoint, use the KDBUS_CMD_ENDPOINT_MAKE ioctl with struct -+kdbus_cmd_make. Custom endpoints always have a policy db that, by default, -+does not allow anything. Everything that users of this new endpoint should be -+able to do has to be explicitly specified through KDBUS_ITEM_NAME and -+KDBUS_ITEM_POLICY_ACCESS items. -+ -+5.3 Creating domains, buses and endpoints -+----------------------------------------- -+ -+KDBUS_CMD_BUS_MAKE, KDBUS_CMD_DOMAIN_MAKE and KDBUS_CMD_ENDPOINT_MAKE take a -+struct kdbus_cmd_make argument. -+ -+struct kdbus_cmd_make { -+ __u64 size; -+ The overall size of the struct, including its items. -+ -+ __u64 flags; -+ The flags for creation. -+ -+ KDBUS_MAKE_ACCESS_GROUP -+ Make the device node group-accessible -+ -+ KDBUS_MAKE_ACCESS_WORLD -+ Make the device node world-accessible -+ -+ __u64 kernel_flags; -+ Valid flags for this command, returned by the kernel upon each call. -+ -+ struct kdbus_item items[0]; -+ A list of items, only used for creating custom endpoints. Ignored for -+ buses and domains. -+}; -+ -+ -+6. Connections -+=============================================================================== -+ -+ -+6.1 Connection IDs and well-known connection names -+-------------------------------------------------- -+ -+Connections are identified by their connection id, internally implemented as a -+uint64_t counter. The IDs of every newly created bus start at 1, and every new -+connection will increment the counter by 1. The ids are not reused. -+ -+In higher level tools, the user visible representation of a connection is -+defined by the D-Bus protocol specification as ":1.<id>". -+ -+Messages with a specific uint64_t destination id are directly delivered to -+the connection with the corresponding id. Messages with the special destination -+id KDBUS_DST_ID_BROADCAST are broadcast messages and are potentially delivered -+to all known connections on the bus; clients interested in broadcast messages -+need to subscribe to the specific messages they are interested though, before -+any broadcast message reaches them. -+ -+Messages synthesized and sent directly by the kernel will carry the special -+source id KDBUS_SRC_ID_KERNEL (0). -+ -+In addition to the unique uint64_t connection id, established connections can -+request the ownership of well-known names, under which they can be found and -+addressed by other bus clients. A well-known name is associated with one and -+only one connection at a time. See section 8 on name acquisition and the -+name registry, and the validity of names. -+ -+Messages can specify the special destination id 0 and carry a well-known name -+in the message data. Such a message is delivered to the destination connection -+which owns that well-known name. -+ -+ +-------------------------------------------------------------------------+ -+ | +---------------+ +---------------------------+ | -+ | | Connection | | Message | -----------------+ | -+ | | :1.22 | --> | src: 22 | | | -+ | | | | dst: 25 | | | -+ | | | | | | | -+ | | | | | | | -+ | | | +---------------------------+ | | -+ | | | | | -+ | | | <--------------------------------------+ | | -+ | +---------------+ | | | -+ | | | | -+ | +---------------+ +---------------------------+ | | | -+ | | Connection | | Message | -----+ | | -+ | | :1.25 | --> | src: 25 | | | -+ | | | | dst: 0xffffffffffffffff | -------------+ | | -+ | | | | (KDBUS_DST_ID_BROADCAST) | | | | -+ | | | | | ---------+ | | | -+ | | | +---------------------------+ | | | | -+ | | | | | | | -+ | | | <--------------------------------------------------+ | -+ | +---------------+ | | | -+ | | | | -+ | +---------------+ +---------------------------+ | | | -+ | | Connection | | Message | --+ | | | -+ | | :1.55 | --> | src: 55 | | | | | -+ | | | | dst: 0 / org.foo.bar | | | | | -+ | | | | | | | | | -+ | | | | | | | | | -+ | | | +---------------------------+ | | | | -+ | | | | | | | -+ | | | <------------------------------------------+ | | -+ | +---------------+ | | | -+ | | | | -+ | +---------------+ | | | -+ | | Connection | | | | -+ | | :1.81 | | | | -+ | | org.foo.bar | | | | -+ | | | | | | -+ | | | | | | -+ | | | <-----------------------------------+ | | -+ | | | | | -+ | | | <----------------------------------------------+ | -+ | +---------------+ | -+ +-------------------------------------------------------------------------+ -+ -+ -+6.2 Creating connections -+------------------------ -+ -+A connection to a bus is created by opening an endpoint device node of -+a bus and becoming an active client with the KDBUS_CMD_HELLO ioctl. Every -+connected client connection has a unique identifier on the bus and can -+address messages to every other connection on the same bus by using -+the peer's connection id as the destination. -+ -+The KDBUS_CMD_HELLO ioctl takes the following struct as argument. -+ -+struct kdbus_cmd_hello { -+ __u64 size; -+ The overall size of the struct, including all attached items. -+ -+ __u64 conn_flags; -+ Flags to apply to this connection: -+ -+ KDBUS_HELLO_ACCEPT_FD -+ When this flag is set, the connection can be sent file descriptors -+ as message payload. If it's not set, any attempt of doing so will -+ result in -ECOMM on the sender's side. -+ -+ KDBUS_HELLO_ACTIVATOR -+ Make this connection an activator (see below). With this bit set, -+ an item of type KDBUS_ITEM_NAME has to be attached which describes -+ the well-known name this connection should be an activator for. -+ -+ KDBUS_HELLO_POLICY_HOLDER -+ Make this connection a policy holder (see below). With this bit set, -+ an item of type KDBUS_ITEM_NAME has to be attached which describes -+ the well-known name this connection should hold a policy for. -+ -+ KDBUS_HELLO_MONITOR -+ Make this connection an eaves-dropping connection that receives all -+ unicast messages sent on the bus. To also receive broadcast messages, -+ the connection has to upload appropriate matches as well. -+ This flag is only valid for privileged bus connections. -+ -+ __u64 attach_flags; -+ Request the attachment of metadata for each message received by this -+ connection. The metadata actually attached may actually augment the list -+ of requested items. See section 13 for more details. -+ -+ __u64 bus_flags; -+ Upon successful completion of the ioctl, this member will contain the -+ flags of the bus it connected to. -+ -+ __u64 id; -+ Upon successful completion of the ioctl, this member will contain the -+ id of the new connection. -+ -+ __u64 pool_size; -+ The size of the communication pool, in bytes. The pool can be accessed -+ by calling mmap() on the file descriptor that was used to issue the -+ KDBUS_CMD_HELLO ioctl. -+ -+ struct kdbus_bloom_parameter bloom; -+ Bloom filter parameter (see below). -+ -+ __u8 id128[16]; -+ Upon successful completion of the ioctl, this member will contain the -+ 128 bit wide UUID of the connected bus. -+ -+ struct kdbus_item items[0]; -+ Variable list of items to add optional additional information. The -+ following items are currently expected/valid: -+ -+ KDBUS_ITEM_CONN_NAME -+ Contains a string to describes this connection's name, so it can be -+ identified later. -+ -+ KDBUS_ITEM_NAME -+ KDBUS_ITEM_POLICY_ACCESS -+ For activators and policy holders only, combinations of these two -+ items describe policy access entries (see section about policy db). -+ -+ KDBUS_ITEM_CREDS -+ KDBUS_ITEM_SECLABEL -+ Privileged bus users may submit these types in order to create -+ connections with faked credentials. The only real use case for this -+ is a proxy service which acts on behalf of some other tasks. For a -+ connection that runs in that mode, the message's metadata items will -+ be limited to what's specified here. See section 13 for more -+ information. -+ -+ Items of other types are silently ignored. -+}; -+ -+ -+6.3 Activator and policy holder connection -+------------------------------------------ -+ -+An activator connection is a placeholder for a well-known name. Messages sent -+to such a connection can be used by userspace to start an implementor -+connection, which will then get all the messages from the activator copied -+over. An activator connection cannot be used to send any message. -+ -+A policy holder connection only installs a policy for one or more names. -+These policy entries are kept active as long as the connection is alive, and -+are removed once it terminates. Such a policy connection type can be used to -+deploy restrictions for names that are not yet active on the bus. A policy -+holder connection cannot be used to send any message. -+ -+The creation of activator, policy holder or monitor connections is an operation -+restricted to privileged users on the bus (see section "Terminology"). -+ -+ -+6.4 Retrieving information on a connection -+------------------------------------------ -+ -+The KDBUS_CMD_CONN_INFO ioctl can be used to retrieve credentials and -+properties of the initial creator of a connection. This ioctl uses the -+following struct: -+ -+struct kdbus_cmd_info { -+ __u64 size; -+ The overall size of the struct, including the name with its 0-byte string -+ terminator. -+ -+ __u64 flags; -+ Specify which items should be attached to the answer. -+ The following flags can be used: -+ -+ KDBUS_ATTACH_NAMES -+ Add an item to the answer containing all the names the connection -+ currently owns. -+ -+ KDBUS_ATTACH_CONN_NAME -+ Add an item to the answer containing the connection's name. -+ -+ After the ioctl returns, this field will contain the current metadata -+ attach flags of the connection. -+ -+ __u64 kernel_flags; -+ Valid flags for this command, returned by the kernel upon each call. -+ -+ __u64 id; -+ The connection's numerical ID to retrieve information for. If set to -+ non-zero value, the 'name' field is ignored. -+ -+ __u64 offset; -+ When the ioctl returns, this value will yield the offset of the connection -+ information inside the caller's pool. -+ -+ struct kdbus_item items[0]; -+ The optional item list, containing the well-known name to look up as -+ a KDBUS_ITEM_NAME. Only required if the 'id' field is set to 0. -+ All other items are currently ignored. -+}; -+ -+After the ioctl returns, the following struct will be stored in the caller's -+pool at 'offset'. -+ -+struct kdbus_info { -+ __u64 size; -+ The overall size of the struct, including all its items. -+ -+ __u64 id; -+ The connection's unique ID. -+ -+ __u64 flags; -+ The connection's flags as specified when it was created. -+ -+ __u64 kernel_flags; -+ Valid flags for this command, returned by the kernel upon each call. -+ -+ struct kdbus_item items[0]; -+ Depending on the 'flags' field in struct kdbus_cmd_info, items of -+ types KDBUS_ITEM_NAME and KDBUS_ITEM_CONN_NAME are followed here. -+}; -+ -+Once the caller is finished with parsing the return buffer, it needs to call -+KDBUS_CMD_FREE for the offset. -+ -+ -+6.5 Getting information about a connection's bus creator -+-------------------------------------------------------- -+ -+The KDBUS_CMD_BUS_CREATOR_INFO ioctl takes the same struct as -+KDBUS_CMD_CONN_INFO but is used to retrieve information about the creator of -+the bus the connection is attached to. The metadata returned by this call is -+collected during the creation of the bus and is never altered afterwards, so -+it provides pristine information on the task that created the bus, at the -+moment when it did so. -+ -+In response to this call, a slice in the connection's pool is allocated and -+filled with an object of type struct kdbus_info, pointed to by the ioctl's -+'offset' field. -+ -+struct kdbus_info { -+ __u64 size; -+ The overall size of the struct, including all its items. -+ -+ __u64 id; -+ The bus' ID -+ -+ __u64 flags; -+ The bus' flags as specified when it was created. -+ -+ __u64 kernel_flags; -+ Valid flags for this command, returned by the kernel upon each call. -+ -+ struct kdbus_item items[0]; -+ Metadata information is stored in items here. -+}; -+ -+Once the caller is finished with parsing the return buffer, it needs to call -+KDBUS_CMD_FREE for the offset. -+ -+ -+6.6 Updating connection details -+------------------------------- -+ -+Some of a connection's details can be updated with the KDBUS_CMD_CONN_UPDATE -+ioctl, using the file descriptor that was used to create the connection. -+The update command uses the following struct. -+ -+struct kdbus_cmd_update { -+ __u64 size; -+ The overall size of the struct, including all its items. -+ -+ struct kdbus_item items[0]; -+ Items to describe the connection details to be updated. The following item -+ types are supported: -+ -+ KDBUS_ITEM_ATTACH_FLAGS -+ Supply a new set of items to be attached to each message. -+ -+ KDBUS_ITEM_NAME -+ KDBUS_ITEM_POLICY_ACCESS -+ Policy holder connections may supply a new set of policy information -+ with these items. For other connection types, -EOPNOTSUPP is returned. -+}; -+ -+ -+6.6 Termination -+--------------- -+ -+A connection can be terminated by simply closing the file descriptor that was -+used to start the connection. All pending incoming messages will be discarded, -+and the memory in the pool will be freed. -+ -+An alternative way of way of closing down a connection is calling the -+KDBUS_CMD_BYEBYE ioctl on it, which will only succeed if the message queue -+of the connection is empty at the time of closing, otherwise, -EBUSY is -+returned. -+ -+When this ioctl returns successfully, the connection has been terminated and -+won't accept any new messages from remote peers. This way, a connection can -+be terminated race-free, without losing any messages. -+ -+ -+7. Messages -+=============================================================================== -+ -+Messages consist of a fixed-size header followed directly by a list of -+variable-sized data 'items'. The overall message size is specified in the -+header of the message. The chain of data items can contain well-defined -+message metadata fields, raw data, references to data, or file descriptors. -+ -+ -+7.1 Sending messages -+-------------------- -+ -+Messages are passed to the kernel with the KDBUS_CMD_MSG_SEND ioctl. Depending -+on the the destination address of the message, the kernel delivers the message -+to the specific destination connection or to all connections on the same bus. -+Sending messages across buses is not possible. Messages are always queued in -+the memory pool of the destination connection (see below). -+ -+The KDBUS_CMD_MSG_SEND ioctl uses struct kdbus_msg to describe the message to -+be sent. -+ -+struct kdbus_msg { -+ __u64 size; -+ The over all size of the struct, including the attached items. -+ -+ __u64 flags; -+ Flags for message delivery: -+ -+ KDBUS_MSG_FLAGS_EXPECT_REPLY -+ Expect a reply from the remote peer to this message. With this bit set, -+ the timeout_ns field must be set to a non-zero number of nanoseconds in -+ which the receiving peer is expected to reply. If such a reply is not -+ received in time, the sender will be notified with a timeout message -+ (see below). The value must be an absolute value, in nanoseconds and -+ based on CLOCK_MONOTONIC. -+ -+ For a message to be accepted as reply, it must be a direct message to -+ the original sender (not a broadcast), and its kdbus_msg.reply_cookie -+ must match the previous message's kdbus_msg.cookie. -+ -+ Expected replies also temporarily open the policy of the sending -+ connection, so the other peer is allowed to respond within the given -+ time window. -+ -+ KDBUS_MSG_FLAGS_SYNC_REPLY -+ By default, all calls to kdbus are considered asynchronous, -+ non-blocking. However, as there are many use cases that need to wait -+ for a remote peer to answer a method call, there's a way to send a -+ message and wait for a reply in a synchronous fashion. This is what -+ the KDBUS_MSG_FLAGS_SYNC_REPLY controls. The KDBUS_CMD_MSG_SEND ioctl -+ will block until the reply has arrived, the timeout limit is reached, -+ in case the remote connection was shut down, or if interrupted by -+ a signal before any reply; see signal(7). -+ -+ The offset of the reply message in the sender's pool is stored in -+ in 'offset_reply' when the ioctl has returned without error. Hence, -+ there is no need for another KDBUS_CMD_MSG_RECV ioctl or anything else -+ to receive the reply. -+ -+ KDBUS_MSG_FLAGS_NO_AUTO_START -+ By default, when a message is sent to an activator connection, the -+ activator notified and will start an implementor. This flag inhibits -+ that behavior. With this bit set, and the remote being an activator, -+ -EADDRNOTAVAIL is returned from the ioctl. -+ -+ __u64 kernel_flags; -+ Valid flags for this command, returned by the kernel upon each call of -+ KDBUS_MSG_SEND. -+ -+ __s64 priority; -+ The priority of this message. Receiving messages (see below) may -+ optionally be constrained to messages of a minimal priority. This -+ allows for use cases where timing critical data is interleaved with -+ control data on the same connection. If unused, the priority should be -+ set to zero. -+ -+ __u64 dst_id; -+ The numeric ID of the destination connection, or KDBUS_DST_ID_BROADCAST -+ (~0ULL) to address every peer on the bus, or KDBUS_DST_ID_NAME (0) to look -+ it up dynamically from the bus' name registry. In the latter case, an item -+ of type KDBUS_ITEM_DST_NAME is mandatory. -+ -+ __u64 src_id; -+ Upon return of the ioctl, this member will contain the sending -+ connection's numerical ID. Should be 0 at send time. -+ -+ __u64 payload_type; -+ Type of the payload in the actual data records. Currently, only -+ KDBUS_PAYLOAD_DBUS is accepted as input value of this field. When -+ receiving messages that are generated by the kernel (notifications), -+ this field will yield KDBUS_PAYLOAD_KERNEL. -+ -+ __u64 cookie; -+ Cookie of this message, for later recognition. Also, when replying -+ to a message (see above), the cookie_reply field must match this value. -+ -+ __u64 timeout_ns; -+ If the message sent requires a reply from the remote peer (see above), -+ this field contains the timeout in absolute nanoseconds based on -+ CLOCK_MONOTONIC. -+ -+ __u64 cookie_reply; -+ If the message sent is a reply to another message, this field must -+ match the cookie of the formerly received message. -+ -+ __u64 offset_reply; -+ If the message successfully got a synchronous reply (see above), this -+ field will yield the offset of the reply message in the sender's pool. -+ Is is what KDBUS_CMD_MSG_RECV usually does for asynchronous messages. -+ -+ struct kdbus_item items[0]; -+ A dynamically sized list of items to contain additional information. -+ The following items are expected/valid: -+ -+ KDBUS_ITEM_PAYLOAD_VEC -+ KDBUS_ITEM_PAYLOAD_MEMFD -+ KDBUS_ITEM_FDS -+ Actual data records containing the payload. See section "Passing of -+ Payload Data". -+ -+ KDBUS_ITEM_BLOOM_FILTER -+ Bloom filter for matches (see below). -+ -+ KDBUS_ITEM_DST_NAME -+ Well-known name to send this message to. Required if dst_id is set -+ to KDBUS_DST_ID_NAME. If a connection holding the given name can't -+ be found, -ESRCH is returned. -+ For messages to a unique name (ID), this item is optional. If present, -+ the kernel will make sure the name owner matches the given unique name. -+ This allows userspace tie the message sending to the condition that a -+ name is currently owned by a certain unique name. -+}; -+ -+The message will be augmented by the requested metadata items when queued into -+the receiver's pool. See also section 13.1 ("Metadata and namespaces"). -+ -+ -+7.2 Message layout -+------------------ -+ -+The layout of a message is shown below. -+ -+ +-------------------------------------------------------------------------+ -+ | Message | -+ | +---------------------------------------------------------------------+ | -+ | | Header | | -+ | | size: overall message size, including the data records | | -+ | | destination: connection id of the receiver | | -+ | | source: connection id of the sender (set by kernel) | | -+ | | payload_type: "DBusDBus" textual identifier stored as uint64_t | | -+ | +---------------------------------------------------------------------+ | -+ | +---------------------------------------------------------------------+ | -+ | | Data Record | | -+ | | size: overall record size (without padding) | | -+ | | type: type of data | | -+ | | data: reference to data (address or file descriptor) | | -+ | +---------------------------------------------------------------------+ | -+ | +---------------------------------------------------------------------+ | -+ | | padding bytes to the next 8 byte alignment | | -+ | +---------------------------------------------------------------------+ | -+ | +---------------------------------------------------------------------+ | -+ | | Data Record | | -+ | | size: overall record size (without padding) | | -+ | | ... | | -+ | +---------------------------------------------------------------------+ | -+ | +---------------------------------------------------------------------+ | -+ | | padding bytes to the next 8 byte alignment | | -+ | +---------------------------------------------------------------------+ | -+ | +---------------------------------------------------------------------+ | -+ | | Data Record | | -+ | | size: overall record size | | -+ | | ... | | -+ | +---------------------------------------------------------------------+ | -+ | +---------------------------------------------------------------------+ | -+ | | padding bytes to the next 8 byte alignment | | -+ | +---------------------------------------------------------------------+ | -+ +-------------------------------------------------------------------------+ -+ -+ -+7.3 Passing of Payload Data -+--------------------------- -+ -+When connecting to the bus, receivers request a memory pool of a given size, -+large enough to carry all backlog of data enqueued for the connection. The -+pool is internally backed by a shared memory file which can be mmap()ed by -+the receiver. -+ -+KDBUS_MSG_PAYLOAD_VEC: -+ Messages are directly copied by the sending process into the receiver's pool, -+ that way two peers can exchange data by effectively doing a single-copy from -+ one process to another, the kernel will not buffer the data anywhere else. -+ -+KDBUS_MSG_PAYLOAD_MEMFD: -+ Messages can reference memfd files which contain the data. -+ memfd files are tmpfs-backed files that allow sealing of the content of the -+ file, which prevents all writable access to the file content. -+ Only sealed memfd files are accepted as payload data, which enforces -+ reliable passing of data; the receiver can assume that neither the sender nor -+ anyone else can alter the content after the message is sent. -+ -+Apart from the sender filling-in the content into memfd files, the data will -+be passed as zero-copy from one process to another, read-only, shared between -+the peers. -+ -+ -+7.4 Receiving messages -+---------------------- -+ -+Messages are received by the client with the KDBUS_CMD_MSG_RECV ioctl. The -+endpoint device node of the bus supports poll() to wake up the receiving -+process when new messages are queued up to be received. -+ -+With the KDBUS_CMD_MSG_RECV ioctl, a struct kdbus_cmd_recv is used. -+ -+struct kdbus_cmd_recv { -+ __u64 flags; -+ Flags to control the receive command. -+ -+ KDBUS_RECV_PEEK -+ Just return the location of the next message. Do not install file -+ descriptors or anything else. This is usually used to determine the -+ sender of the next queued message. -+ -+ KDBUS_RECV_DROP -+ Drop the next message without doing anything else with it, and free the -+ pool slice. This a short-cut for KDBUS_RECV_PEEK and KDBUS_CMD_FREE. -+ -+ KDBUS_RECV_USE_PRIORITY -+ Use the priority field (see below). -+ -+ __u64 kernel_flags; -+ Valid flags for this command, returned by the kernel upon each call. -+ -+ __s64 priority; -+ With KDBUS_RECV_USE_PRIORITY set in flags, receive the next message in -+ the queue with at least the given priority. If no such message is waiting -+ in the queue, -ENOMSG is returned. -+ -+ __u64 offset; -+ Upon return of the ioctl, this field contains the offset in the -+ receiver's memory pool. -+}; -+ -+Unless KDBUS_RECV_DROP was passed, and given that the ioctl succeeded, the -+offset field contains the location of the new message inside the receiver's -+pool. The message is stored as struct kdbus_msg at this offset, and can be -+interpreted with the semantics described above. -+ -+Also, if the connection allowed for file descriptor to be passed -+(KDBUS_HELLO_ACCEPT_FD), and if the message contained any, they will be -+installed into the receiving process after the KDBUS_CMD_MSG_RECV ioctl -+returns. The receiving task is obliged to close all of them appropriately. -+ -+The caller is obliged to call KDBUS_CMD_FREE with the returned offset when -+the memory is no longer needed. -+ -+ -+7.5 Canceling messages synchronously waiting for replies -+-------------------------------------------------------- -+ -+When a connection sends a message with KDBUS_MSG_FLAGS_SYNC_REPLY and -+blocks while waiting for the reply, the KDBUS_CMD_MSG_CANCEL ioctl can be -+used on the same file descriptor to cancel the message, based on its cookie. -+If there are multiple messages with the same cookie that are all synchronously -+waiting for a reply, all of them will be canceled. Obviously, this is only -+possible in multi-threaded applications. -+ -+ -+8. Name registry -+=============================================================================== -+ -+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. -+ -+All of the below is subject to policy rules for SEE and OWN permissions. -+ -+ -+8.1 Name validity -+----------------- -+ -+A name has to comply to the following rules to be considered valid: -+ -+ - 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 contain at least one '.' (period) character -+ (and thus at least two elements) -+ - The name must not begin with a '.' (period) character -+ - The name must not exceed KDBUS_NAME_MAX_LEN (255) -+ -+ -+8.2 Acquiring a name -+-------------------- -+ -+To acquire a name, a client uses the KDBUS_CMD_NAME_ACQUIRE ioctl with the -+following data structure. -+ -+struct kdbus_cmd_name { -+ __u64 size; -+ The overall size of this struct, including the name with its 0-byte string -+ terminator. -+ -+ __u64 flags; -+ Flags to control details in the name acquisition. -+ -+ KDBUS_NAME_REPLACE_EXISTING -+ Acquiring a name that is already present usually fails, unless this flag -+ is set in the call, and KDBUS_NAME_ALLOW_REPLACEMENT or (see below) was -+ set when the current owner of the name acquired it, or if the current -+ owner is an activator connection (see below). -+ -+ KDBUS_NAME_ALLOW_REPLACEMENT -+ Allow other connections to take over this name. When this happens, the -+ former owner of the connection will be notified of the name loss. -+ -+ KDBUS_NAME_QUEUE (acquire) -+ A name that is already acquired by a connection, and which wasn't -+ requested with the KDBUS_NAME_ALLOW_REPLACEMENT flag set can not be -+ acquired again. However, a connection can put itself in a queue of -+ connections waiting for the name to be released. Once that happens, the -+ first connection in that queue becomes the new owner and is notified -+ accordingly. -+ -+ __u64 kernel_flags; -+ Valid flags for this command, returned by the kernel upon each call. -+ -+ struct kdbus_item items[0]; -+ Items to submit the name. Currently, one one item of type KDBUS_ITEM_NAME -+ is expected and allowed, and the contained string must be a valid bus name. -+}; -+ -+ -+8.3 Releasing a name -+-------------------- -+ -+A connection may release a name explicitly with the KDBUS_CMD_NAME_RELEASE -+ioctl. If the connection was an implementor of an activatable name, its -+pending messages are moved back to the activator. If there are any connections -+queued up as waiters for the name, the oldest one of them will become the new -+owner. The same happens implicitly for all names once a connection terminates. -+ -+The KDBUS_CMD_NAME_RELEASE ioctl uses the same data structure as the -+acquisition call, but with slightly different field usage. -+ -+struct kdbus_cmd_name { -+ __u64 size; -+ The overall size of this struct, including the name with its 0-byte string -+ terminator. -+ -+ __u64 flags; -+ -+ struct kdbus_item items[0]; -+ Items to submit the name. Currently, one one item of type KDBUS_ITEM_NAME -+ is expected and allowed, and the contained string must be a valid bus name. -+}; -+ -+ -+8.4 Dumping the name registry -+----------------------------- -+ -+A connection may request a complete or filtered dump of currently active bus -+names with the KDBUS_CMD_NAME_LIST ioctl, which takes a struct -+kdbus_cmd_name_list as argument. -+ -+struct kdbus_cmd_name_list { -+ __u64 flags; -+ Any combination of flags to specify which names should be dumped. -+ -+ KDBUS_NAME_LIST_UNIQUE -+ List the unique (numeric) IDs of the connection, whether it owns a name -+ or not. -+ -+ KDBUS_NAME_LIST_NAMES -+ List well-known names stored in the database which are actively owned by -+ a real connection (not an activator). -+ -+ KDBUS_NAME_LIST_ACTIVATORS -+ List names that are owned by an activator. -+ -+ KDBUS_NAME_LIST_QUEUED -+ List connections that are not yet owning a name but are waiting for it -+ to become available. -+ -+ __u64 offset; -+ When the ioctl returns successfully, the offset to the name registry dump -+ inside the connection's pool will be stored in this field. -+}; -+ -+The returned list of names is stored in a struct kdbus_name_list that in turn -+contains a dynamic number of struct kdbus_cmd_name that carry the actual -+information. The fields inside that struct kdbus_cmd_name is described next. -+ -+struct kdbus_name_info { -+ __u64 size; -+ The overall size of this struct, including the name with its 0-byte string -+ terminator. -+ -+ __u64 flags; -+ The current flags for this name. Can be any combination of -+ -+ KDBUS_NAME_ALLOW_REPLACEMENT -+ -+ KDBUS_NAME_IN_QUEUE (list) -+ When retrieving a list of currently acquired name in the registry, this -+ flag indicates whether the connection actually owns the name or is -+ currently waiting for it to become available. -+ -+ KDBUS_NAME_ACTIVATOR (list) -+ An activator connection owns a name as a placeholder for an implementor, -+ which is started on demand as soon as the first message arrives. There's -+ some more information on this topic below. In contrast to -+ KDBUS_NAME_REPLACE_EXISTING, when a name is taken over from an activator -+ connection, all the messages that have been queued in the activator -+ connection will be moved over to the new owner. The activator connection -+ will still be tracked for the name and will take control again if the -+ implementor connection terminates. -+ This flag can not be used when acquiring a name, but is implicitly set -+ through KDBUS_CMD_HELLO with KDBUS_HELLO_ACTIVATOR set in -+ kdbus_cmd_hello.conn_flags. -+ -+ __u64 owner_id; -+ The owning connection's unique ID. -+ -+ __u64 conn_flags; -+ The flags of the owning connection. -+ -+ struct kdbus_item items[0]; -+ Items containing the actual name. Currently, one one item of type -+ KDBUS_ITEM_NAME will be attached. -+}; -+ -+The returned buffer must be freed with the KDBUS_CMD_FREE ioctl when the user -+is finished with it. -+ -+ -+9. Notifications -+=============================================================================== -+ -+The kernel will notify its users of the following events. -+ -+ * When connection A is terminated while connection B is waiting for a reply -+ from it, connection B is notified with a message with an item of type -+ KDBUS_ITEM_REPLY_DEAD. -+ -+ * When connection A does not receive a reply from connection B within the -+ specified timeout window, connection A will receive a message with an item -+ of type KDBUS_ITEM_REPLY_TIMEOUT. -+ -+ * When a connection is created on or removed from a bus, messages with an -+ item of type KDBUS_ITEM_ID_ADD or KDBUS_ITEM_ID_REMOVE, respectively, are -+ sent to all bus members that match these messages through their match -+ database. -+ -+ * When a connection owns or loses a name, or a name is moved from one -+ connection to another, messages with an item of type KDBUS_ITEM_NAME_ADD, -+ KDBUS_ITEM_NAME_REMOVE or KDBUS_ITEM_NAME_CHANGE are sent to all bus -+ members that match these messages through their match database. -+ -+A kernel notification is a regular kdbus message with the following details. -+ -+ * kdbus_msg.src_id == KDBUS_SRC_ID_KERNEL -+ * kdbus_msg.dst_id == KDBUS_DST_ID_BROADCAST -+ * kdbus_msg.payload_type == KDBUS_PAYLOAD_KERNEL -+ * Has exactly one of the aforementioned items attached -+ -+ -+10. Message Matching, Bloom filters -+=============================================================================== -+ -+10.1 Matches for broadcast messages from other connections -+---------------------------------------------------------- -+ -+A message addressed at the connection ID KDBUS_DST_ID_BROADCAST (~0ULL) is a -+broadcast message, delivered to all connected peers which installed a rule to -+match certain properties of the message. Without any rules installed in the -+connection, no broadcast message or kernel-side notifications will be delivered -+to the connection. Broadcast messages are subject to policy rules and TALK -+access checks. -+ -+See section 11 for details on policies, and section 11.5 for more -+details on implicit policies. -+ -+Matches for messages from other connections (not kernel notifications) are -+implemented as bloom filters. The sender adds certain properties of the message -+as elements to a bloom filter bit field, and sends that along with the -+broadcast message. -+ -+The connection adds the message properties it is interested as elements to a -+bloom mask bit field, and uploads the mask to the match rules of the -+connection. -+ -+The kernel will match the broadcast message's bloom filter against the -+connections bloom mask (simply by &-ing it), and decide whether the message -+should be delivered to the connection. -+ -+The kernel has no notion of any specific properties of the message, all it -+sees are the bit fields of the bloom filter and mask to match against. The -+use of bloom filters allows simple and efficient matching, without exposing -+any message properties or internals to the kernel side. Clients need to deal -+with the fact that they might receive broadcasts which they did not subscribe -+to, as the bloom filter might allow false-positives to pass the filter. -+ -+To allow the future extension of the set of elements in the bloom filter, the -+filter specifies a "generation" number. A later generation must always contain -+all elements of the set of the previous generation, but can add new elements -+to the set. The match rules mask can carry an array with all previous -+generations of masks individually stored. When the filter and mask are matched -+by the kernel, the mask with the closest matching "generation" is selected -+as the index into the mask array. -+ -+ -+10.2 Matches for kernel notifications -+------------------------------------ -+ -+To receive kernel generated notifications (see section 9), a connection must -+install special match rules that are different from the bloom filter matches -+described in the section above. They can be filtered by a sender connection's -+ID, by one of the name the sender connection owns at the time of sending the -+message, or by type of the notification (id/name add/remove/change). -+ -+10.3 Adding a match -+------------------- -+ -+To add a match, the KDBUS_CMD_MATCH_ADD ioctl is used, which takes a struct -+of the struct described below. -+ -+Note that each of the items attached to this command will internally create -+one match 'rule', and the collection of them, which is submitted as one block -+via the ioctl is called a 'match'. To allow a message to pass, all rules of a -+match have to be satisfied. Hence, adding more items to the command will only -+narrow the possibility of a match to effectively let the message pass, and will -+cause the connection's user space process to wake up less likely. -+ -+Multiple matches can be installed per connection. As long as one of it has a -+set of rules which allows the message to pass, this one will be decisive. -+ -+struct kdbus_cmd_match { -+ __u64 size; -+ The overall size of the struct, including its items. -+ -+ __u64 cookie; -+ A cookie which identifies the match, so it can be referred to at removal -+ time. -+ -+ __u64 flags; -+ Flags to control the behavior of the ioctl. -+ -+ KDBUS_MATCH_REPLACE: -+ Remove all entries with the given cookie before installing the new one. -+ This allows for race-free replacement of matches. -+ -+ struct kdbus_item items[0]; -+ Items to define the actual rules of the matches. The following item types -+ are expected. Each item will cause one new match rule to be created. -+ -+ KDBUS_ITEM_BLOOM_MASK -+ An item that carries the bloom filter mask to match against in its -+ data field. The payload size must match the bloom filter size that -+ was specified when the bus was created. -+ See section 10.4 for more information. -+ -+ KDBUS_ITEM_NAME -+ Specify a name that a sending connection must own at a time of sending -+ a broadcast message in order to match this rule. -+ -+ KDBUS_ITEM_ID -+ Specify a sender connection's ID that will match this rule. -+ -+ KDBUS_ITEM_NAME_ADD -+ KDBUS_ITEM_NAME_REMOVE -+ KDBUS_ITEM_NAME_CHANGE -+ These items request delivery of broadcast messages that describe a name -+ acquisition, loss, or change. The details are stored in the item's -+ kdbus_notify_name_change member. All information specified must be -+ matched in order to make the message pass. Use KDBUS_MATCH_ID_ANY to -+ match against any unique connection ID. -+ -+ KDBUS_ITEM_ID_ADD -+ KDBUS_ITEM_ID_REMOVE -+ These items request delivery of broadcast messages that are generated -+ when a connection is created or terminated. struct kdbus_notify_id_change -+ is used to store the actual match information. This item can be used to -+ monitor one particular connection ID, or, when the id field is set to -+ KDBUS_MATCH_ID_ANY, all of them. -+ -+ Other item types are ignored. -+}; -+ -+ -+10.4 Bloom filters -+------------------ -+ -+Bloom filters allow checking whether a given word is present in a dictionary. -+This allows connections to set up a mask for information it is interested in, -+and will be delivered broadcast messages that have a matching filter. -+ -+For general information on bloom filters, see -+ -+ https://en.wikipedia.org/wiki/Bloom_filter -+ -+The size of the bloom filter is defined per bus when it is created, in -+kdbus_bloom_parameter.size. All bloom filters attached to broadcast messages -+on the bus must match this size, and all bloom filter matches uploaded by -+connections must also match the size, or a multiple thereof (see below). -+ -+The calculation of the mask has to be done on the userspace side. The kernel -+just checks the bitmasks to decide whether or not to let the message pass. All -+bits in the mask must match the filter in and bit-wise AND logic, but the -+mask may have more bits set than the filter. Consequently, false positive -+matches are expected to happen, and userspace must deal with that fact. -+ -+Masks are entities that are always passed to the kernel as part of a match -+(with an item of type KDBUS_ITEM_BLOOM_MASK), and filters can be attached to -+broadcast messages (with an item of type KDBUS_ITEM_BLOOM_FILTER). -+ -+For a broadcast to match, all set bits in the filter have to be set in the -+installed match mask as well. For example, consider a bus has a bloom size -+of 8 bytes, and the following mask/filter combinations: -+ -+ filter 0x0101010101010101 -+ mask 0x0101010101010101 -+ -> matches -+ -+ filter 0x0303030303030303 -+ mask 0x0101010101010101 -+ -> doesn't match -+ -+ filter 0x0101010101010101 -+ mask 0x0303030303030303 -+ -> matches -+ -+Hence, in order to catch all messages, a mask filled with 0xff bytes can be -+installed as a wildcard match rule. -+ -+Uploaded matches may contain multiple masks, each of which in the size of the -+bloom size defined by the bus. Each block of a mask is called a 'generation', -+starting at index 0. -+ -+At match time, when a broadcast message is about to be delivered, a bloom -+mask generation is passed, which denotes which of the bloom masks the filter -+should be matched against. This allows userspace to provide backward compatible -+masks at upload time, while older clients can still match against older -+versions of filters. -+ -+ -+10.5 Removing a match -+-------------------- -+ -+Matches can be removed through the KDBUS_CMD_MATCH_REMOVE ioctl, which again -+takes struct kdbus_cmd_match as argument, but its fields are used slightly -+differently. -+ -+struct kdbus_cmd_match { -+ __u64 size; -+ The overall size of the struct. As it has no items in this use case, the -+ value should yield 16. -+ -+ __u64 cookie; -+ The cookie of the match, as it was passed when the match was added. -+ All matches that have this cookie will be removed. -+ -+ __u64 flags; -+ Unused for this use case, -+ -+ __u64 kernel_flags; -+ Valid flags for this command, returned by the kernel upon each call. -+ -+ struct kdbus_item items[0]; -+ Unused for this use case. -+}; -+ -+ -+11. Policy -+=============================================================================== -+ -+A policy databases restrict 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. -+ -+See section 8.1 for more details on the validity of well-known names. -+ -+Default endpoints of buses always have a policy database. The default -+policy is to deny all operations except for operations that are covered by -+implicit policies. Custom endpoints always have a policy, and by default, -+a policy database is empty. Therefore, unless policy rules are added, all -+operations will also be denied by default. -+ -+See section 11.5 for more details on implicit policies. -+ -+A set of policy rules is described by a name and multiple access rules, defined -+by the following struct. -+ -+struct kdbus_policy_access { -+ __u64 type; /* USER, GROUP, WORLD */ -+ One of the following. -+ -+ KDBUS_POLICY_ACCESS_USER -+ Grant access to a user with the uid stored in the 'id' field. -+ -+ KDBUS_POLICY_ACCESS_GROUP -+ Grant access to a user with the gid stored in the 'id' field. -+ -+ KDBUS_POLICY_ACCESS_WORLD -+ Grant access to everyone. The 'id' field is ignored. -+ -+ __u64 access; /* OWN, TALK, SEE */ -+ The access to grant. -+ -+ KDBUS_POLICY_SEE -+ Allow the name to be seen. -+ -+ KDBUS_POLICY_TALK -+ Allow the name to be talked to. -+ -+ KDBUS_POLICY_OWN -+ Allow the name to be owned. -+ -+ __u64 id; -+ For KDBUS_POLICY_ACCESS_USER, stores the uid. -+ For KDBUS_POLICY_ACCESS_GROUP, stores the gid. -+}; -+ -+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_ENDPOINT_MAKE (creating a custom endpoint) or -+KDBUS_CMD_ENDPOINT_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. For this transport, the following rules apply. -+ -+ * An item of type KDBUS_ITEM_NAME must be followed by at least one -+ KDBUS_ITEM_POLICY_ACCESS item -+ * An item of type KDBUS_ITEM_NAME can be followed by an arbitrary number of -+ KDBUS_ITEM_POLICY_ACCESS items -+ * An arbitrary number of groups of names and access levels can be passed -+ -+uids and gids are internally always stored in the kernel's view of global ids, -+and are translated back and forth on the ioctl level accordingly. -+ -+ -+11.2 Wildcard names -+------------------- -+ -+Policy holder connections may upload names that contain the wildcard suffix -+(".*"). That way, a policy can be uploaded that is effective for every -+well-kwown name that extends the provided name by exactly one more level. -+ -+For example, if an item of a set up uploaded policy rules contains the name -+"foo.bar.*", both "foo.bar.baz" and "foo.bar.bazbaz" are valid, but -+"foo.bar.baz.baz" is not. -+ -+This allows connections to take control over multiple names that the policy -+holder doesn't need to know about when uploading the policy. -+ -+Such wildcard entries are not allowed for custom endpoints. -+ -+ -+11.3 Policy example -+------------------- -+ -+For example, a set of policy rules may look like this: -+ -+ KDBUS_ITEM_NAME: str='org.foo.bar' -+ KDBUS_ITEM_POLICY_ACCESS: type=USER, access=OWN, id=1000 -+ KDBUS_ITEM_POLICY_ACCESS: type=USER, access=TALK, id=1001 -+ KDBUS_ITEM_POLICY_ACCESS: type=WORLD, access=SEE -+ KDBUS_ITEM_NAME: str='org.blah.baz' -+ KDBUS_ITEM_POLICY_ACCESS: type=USER, access=OWN, id=0 -+ KDBUS_ITEM_POLICY_ACCESS: type=WORLD, access=TALK -+ -+That means that 'org.foo.bar' may only be owned by uid 1000, but every user on -+the bus is allowed to see the name. However, only uid 1001 may actually send -+a message to the connection and receive a reply from it. -+ -+The second rule allows 'org.blah.baz' to be owned by uid 0 only, but every user -+may talk to it. -+ -+ -+11.4 TALK access and multiple well-known names per connection -+------------------------------------------------------------- -+ -+Note that TALK access is checked against all names of a connection. -+For example, if a connection owns both 'org.foo.bar' and 'org.blah.baz', and -+the policy database allows 'org.blah.baz' to be talked to by WORLD, then this -+permission is also granted to 'org.foo.bar'. That might sound illogical, but -+after all, we allow messages to be directed to either the name or a well-known -+name, and policy is applied to the connection, not the name. In other words, -+the effective TALK policy for a connection is the most permissive of all names -+the connection owns. -+ -+If a policy database exists for a bus (because a policy holder created one on -+demand) or for a custom endpoint (which always has one), each one is consulted -+during name registry listing, name owning or message delivery. If either one -+fails, the operation is failed with -EPERM. -+ -+For best practices, connections that own names with a restricted TALK -+access should not install matches. This avoids cases where the sent -+message may pass the bloom filter due to false-positives and may also -+satisfy the policy rules. -+ -+11.5 Implicit policies -+---------------------- -+ -+Depending on the type of the endpoint, a set of implicit rules might be -+enforced. On default endpoints, the following set is enforced: -+ -+ * Privileged connections always override any installed policy. Those -+ connections could easily install their own policies, so there is no -+ reason to enforce installed policies. -+ * Connections can always talk to connections of the same user. This -+ includes broadcast messages. -+ * Connections that own names might send broadcast messages to other -+ connections that belong to a different user, but only if that -+ destination connection does not own any name. -+ -+Custom endpoints have stricter policies. The following rules apply: -+ -+ * Policy rules are always enforced, even if the connection is a privileged -+ connection. -+ * Policy rules are always enforced for TALK access, even if both ends are -+ running under the same user. This includes broadcast messages. -+ * To restrict the set of names that can be seen, endpoint policies can -+ install "SEE" policies. -+ -+ -+12. Pool -+=============================================================================== -+ -+A pool for data received from the kernel is installed for every connection of -+the bus, and is sized according to kdbus_cmd_hello.pool_size. 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 -+ -+Internally, the pool is organized in slices, stored in an rb-tree. The offsets -+returned by either one of the aforementioned ioctls describe offsets inside the -+pool. 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, like -+this: -+ -+ /* -+ * POOL_SIZE has to be a multiple of PAGE_SIZE, and it must match the -+ * value that was previously passed in the .pool_size field of struct -+ * kdbus_cmd_hello. -+ */ -+ -+ buf = mmap(NULL, POOL_SIZE, PROT_READ, MAP_PRIVATE, conn_fd, 0); -+ -+ -+13. Metadata -+=============================================================================== -+ -+When a message is delivered to a receiver connection, it is augmented by -+metadata items in accordance to the destination's current attach flags. The -+information stored in those metadata items refer to the sender task at the -+time of sending the message, so even if any detail of the sender task has -+already changed upon message reception (or if the sender task does not exist -+anymore), the information is still preserved and won't be modfied until the -+message is freed. -+ -+Note that there are two exceptions to the above rules: -+ -+ a) Kernel generated messages don't have a source connection, so they won't be -+ augmented. -+ -+ b) If a connection was created with faked credentials (see section 6.2), -+ the only attached metadata items are the ones provided by the connection -+ itself. The destination's attach_flags won't be looked at in such cases. -+ -+Also, there are two things to be considered by userspace programs regarding -+those metadata items: -+ -+ a) Userspace must cope with the fact that it might get more metadata than -+ they requested. That happens, for example, when a broadcast message is -+ sent and receivers have different attach flags. Items that haven't been -+ requested should hence be silently ignored. -+ -+ b) Userspace might not always get all requested metadata items that it -+ requested. That is because some of those items are only added if a -+ corresponding kernel feature has been enabled. Also, the two exceptions -+ described above will as well lead to less items be attached than -+ requested. -+ -+ -+13.1 Known item types -+--------------------- -+ -+The following attach flags are currently supported. -+ -+ KDBUS_ATTACH_TIMESTAMP -+ Attaches an item of type KDBUS_ITEM_TIMESTAMP which contains both the -+ monotonic and the realtime timestamp, taken when the message was -+ processed on the kernel side. -+ -+ KDBUS_ATTACH_CREDS -+ Attaches an item of type KDBUS_ITEM_CREDS, containing credentials as -+ described in kdbus_creds: the uid, gid, pid, tid and starttime of the task. -+ -+ KDBUS_ATTACH_AUXGROUPS -+ Attaches an item of type KDBUS_ITEM_AUXGROUPS, containing a dynamic -+ number of auxiliary groups the sending task was a member of. -+ -+ KDBUS_ATTACH_NAMES -+ Attaches items of type KDBUS_ITEM_NAME, one for each name the sending -+ connection currently owns. The name is stored in kdbus_item.str for each -+ of them. -+ -+ KDBUS_ATTACH_COMM -+ Attaches an items of type KDBUS_ITEM_PID_COMM and KDBUS_ITEM_TID_COMM, -+ both transporting the sending task's 'comm', for both the pid and the tid. -+ The strings are stored in kdbus_item.str. -+ -+ KDBUS_ATTACH_EXE -+ Attaches an item of type KDBUS_ITEM_EXE, containing the path to the -+ executable of the sending task, stored in kdbus_item.str. -+ -+ KDBUS_ATTACH_CMDLINE -+ Attaches an item of type KDBUS_ITEM_CMDLINE, containing the command line -+ arguments of the sending task, as an array of strings, stored in -+ kdbus_item.str. -+ -+ KDBUS_ATTACH_CGROUP -+ Attaches an item of type KDBUS_ITEM_CGROUP with the task's cgroup path. -+ -+ KDBUS_ATTACH_CAPS -+ Attaches an item of type KDBUS_ITEM_CAPS, carrying sets of capabilities -+ that should be accessed via kdbus_item.caps.caps. Also, userspace should -+ be written in a way that it takes kdbus_item.caps.last_cap into account, -+ and derive the number of sets and rows from the item size and the reported -+ number of valid capability bits. -+ -+ KDBUS_ATTACH_SECLABEL -+ Attaches an item of type KDBUS_ITEM_SECLABEL, which contains the SELinux -+ security label of the sending task. Access via kdbus_item->str. -+ -+ KDBUS_ATTACH_AUDIT -+ Attaches an item of type KDBUS_ITEM_AUDIT, which contains the audio label -+ of the sending taskj. Access via kdbus_item->str. -+ -+ KDBUS_ATTACH_CONN_NAME -+ Attaches an item of type KDBUS_ITEM_CONN_NAME that contain's the -+ sending's connection current name in kdbus_item.str. -+ -+ -+13.1 Metadata and namespaces -+---------------------------- -+Note that if the user or PID namespaces of a connection at the time of sending -+differ from those that were active then the connection was created -+(KDBUS_CMD_HELLO), data structures such as messages will not have any metadata -+attached to prevent leaking security-relevant information. -+ -+ -+14. Error codes -+=============================================================================== -+ -+Below is a list of error codes that might be returned by the individual -+ioctl commands. The list focuses on the return values from kdbus code itself, -+and might not cover those of all kernel internal functions. -+ -+For all ioctls: -+ -+ -ENOMEM The kernel memory is exhausted -+ -ENOTTY Illegal ioctl command issued for the file descriptor -+ -ENOSYS The requested functionality is not available -+ -+For all ioctls that carry a struct as payload: -+ -+ -EFAULT The supplied data pointer was not 64-bit aligned, or was -+ inaccessible from the kernel side. -+ -EINVAL The size inside the supplied struct was smaller than expected -+ -EMSGSIZE The size inside the supplied struct was bigger than expected -+ -ENAMETOOLONG A supplied name is larger than the allowed maximum size -+ -+For KDBUS_CMD_BUS_MAKE: -+ -+ -EINVAL The flags supplied in the kdbus_cmd_make struct are invalid or -+ the supplied name does not start with the current uid and a '-' -+ -EEXIST A bus of that name already exists -+ -ESHUTDOWN The domain for the bus is already shut down -+ -EMFILE The maximum number of buses for the current user is exhausted -+ -+For KDBUS_CMD_DOMAIN_MAKE: -+ -+ -EPERM The calling user does not have CAP_IPC_OWNER set, or -+ -EINVAL The flags supplied in the kdbus_cmd_make struct are invalid, or -+ no name supplied for top-level domain -+ -EEXIST A domain of that name already exists -+ -+For KDBUS_CMD_ENDPOINT_MAKE: -+ -+ -EPERM The calling user is not privileged (see Terminology) -+ -EINVAL The flags supplied in the kdbus_cmd_make struct are invalid -+ -EEXIST An endpoint of that name already exists -+ -+For KDBUS_CMD_HELLO: -+ -+ -EFAULT The supplied pool size was 0 or not a multiple of the page size -+ -EINVAL The flags supplied in the kdbus_cmd_make struct are invalid, or -+ an illegal combination of KDBUS_HELLO_MONITOR, -+ KDBUS_HELLO_ACTIVATOR and KDBUS_HELLO_POLICY_HOLDER was passed -+ in the flags, or an invalid set of items was supplied -+ -EPERM An KDBUS_ITEM_CREDS items was supplied, but the current user is -+ not privileged -+ -ESHUTDOWN The bus has already been shut down -+ -EMFILE The maximum number of connection on the bus has been reached -+ -+For KDBUS_CMD_BYEBYE: -+ -+ -EALREADY The connection has already been shut down -+ -EBUSY There are still messages queued up in the connection's pool -+ -+For KDBUS_CMD_MSG_SEND: -+ -+ -EOPNOTSUPP The connection is unconnected, or a fd was passed that is -+ either a kdbus handle itself or a unix domain socket. Both is -+ currently unsupported. -+ -EINVAL The submitted payload type is KDBUS_PAYLOAD_KERNEL, -+ KDBUS_MSG_FLAGS_EXPECT_REPLY was set without a timeout value, -+ KDBUS_MSG_FLAGS_SYNC_REPLY was set without -+ KDBUS_MSG_FLAGS_EXPECT_REPLY, an invalid item was supplied, -+ src_id was != 0 and different from the current connection's ID, -+ a supplied memfd had a size of 0, a string was not properly -+ nul-terminated -+ -ENOTUNIQ KDBUS_MSG_FLAGS_EXPECT_REPLY was set, but the dst_id is set -+ to KDBUS_DST_ID_BROADCAST -+ -E2BIG Too many items -+ -EMSGSIZE A payload vector was too big, and the current user is -+ unprivileged. -+ -ENOTUNIQ A fd or memfd payload was passed in a broadcast message, or -+ a timeout was given for a broadcast message -+ -EEXIST Multiple KDBUS_ITEM_FDS or KDBUS_ITEM_BLOOM_FILTER, -+ KDBUS_ITEM_DST_NAME were supplied -+ -EBADF A memfd item contained an illegal fd -+ -EMEDIUMTYPE A file descriptor which is not a kdbus memfd was -+ refused to send as KDBUS_MSG_PAYLOAD_MEMFD. -+ -EMFILE Too many file descriptors inside a KDBUS_ITEM_FDS -+ -EBADMSG An item had illegal size, both a dst_id and a -+ KDBUS_ITEM_DST_NAME was given, or both a name and a bloom -+ filter was given -+ -ETXTBSY A kdbus memfd file cannot be sealed or the seal removed, -+ because it is shared with other processes or still mmap()ed -+ -ECOMM A peer does not accept the file descriptors addressed to it -+ -EFAULT The supplied bloom filter size was not 64-bit aligned -+ -EDOM The supplied bloom filter size did not match the bloom filter -+ size of the bus -+ -EDESTADDRREQ dst_id was set to KDBUS_DST_ID_NAME, but no KDBUS_ITEM_DST_NAME -+ was attached -+ -ESRCH The name to look up was not found in the name registry -+ -EADDRNOTAVAIL KDBUS_MSG_FLAGS_NO_AUTO_START was given but the destination -+ connection is an activator. -+ -ENXIO The passed numeric destination connection ID couldn't be found, -+ or is not connected -+ -ECONNRESET The destination connection is no longer active -+ -ETIMEDOUT Timeout while synchronously waiting for a reply -+ -EINTR System call interrupted while synchronously waiting for a reply -+ -EPIPE When sending a message, a synchronous reply from the receiving -+ connection was expected but the connection died before -+ answering -+ -ECANCELED A synchronous message sending was cancelled -+ -ENOBUFS Too many pending messages on the receiver side -+ -EREMCHG Both a well-known name and a unique name (ID) was given, but -+ the name is not currently owned by that connection. -+ -+For KDBUS_CMD_MSG_RECV: -+ -+ -EINVAL Invalid flags or offset -+ -EAGAIN No message found in the queue -+ -ENOMSG No message of the requested priority found -+ -+For KDBUS_CMD_MSG_CANCEL: -+ -+ -EINVAL Invalid flags -+ -ENOENT Pending message with the supplied cookie not found -+ -+For KDBUS_CMD_FREE: -+ -+ -ENXIO No pool slice found at given offset -+ -EINVAL Invalid flags provided, the offset is valid, but the user is -+ not allowed to free the slice. This happens, for example, if -+ the offset was retrieved with KDBUS_RECV_PEEK. -+ -+For KDBUS_CMD_NAME_ACQUIRE: -+ -+ -EINVAL Illegal command flags, illegal name provided, or an activator -+ tried to acquire a second name -+ -EPERM Policy prohibited name ownership -+ -EALREADY Connection already owns that name -+ -EEXIST The name already exists and can not be taken over -+ -ECONNRESET The connection was reset during the call -+ -+For KDBUS_CMD_NAME_RELEASE: -+ -+ -EINVAL Invalid command flags, or invalid name provided -+ -ESRCH Name is not found found in the registry -+ -EADDRINUSE Name is owned by a different connection and can't be released -+ -+For KDBUS_CMD_NAME_LIST: -+ -+ -EINVAL Invalid flags -+ -ENOBUFS No available memory in the connection's pool. -+ -+For KDBUS_CMD_CONN_INFO: -+ -+ -EINVAL Invalid flags, or neither an ID nor a name was provided, -+ or the name is invalid. -+ -ESRCH Connection lookup by name failed -+ -ENXIO No connection with the provided number connection ID found -+ -+For KDBUS_CMD_CONN_UPDATE: -+ -+ -EINVAL Illegal flags or items -+ -EOPNOTSUPP Operation not supported by connection. -+ -E2BIG Too many policy items attached -+ -EINVAL Wildcards submitted in policy entries, or illegal sequence -+ of policy items -+ -+For KDBUS_CMD_ENDPOINT_UPDATE: -+ -+ -E2BIG Too many policy items attached -+ -EINVAL Invalid flags, or wildcards submitted in policy entries, -+ or illegal sequence of policy items -+ -+For KDBUS_CMD_MATCH_ADD: -+ -+ -EINVAL Illegal flags or items -+ -EDOM Illegal bloom filter size -+ -EMFILE Too many matches for this connection -+ -+For KDBUS_CMD_MATCH_REMOVE: -+ -+ -EINVAL Illegal flags -+ -ENOENT A match entry with the given cookie could not be found. diff --git a/0002-kdbus-add-header-file.patch b/0002-kdbus-add-header-file.patch deleted file mode 100644 index bbd9f72d48dca3..00000000000000 --- a/0002-kdbus-add-header-file.patch +++ /dev/null @@ -1,941 +0,0 @@ -From bd31337869d4716d16eff656f67fec7617801bbc Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 18:38:06 +0200 -Subject: [PATCH 02/12] kdbus: add header file - -From: Daniel Mack <daniel@zonque.org> - -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 definitionsi, enums and #defines -for constants. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - include/uapi/linux/kdbus.h | 918 +++++++++++++++++++++++++++++++++++++++++++++ - 1 file changed, 918 insertions(+) - create mode 100644 include/uapi/linux/kdbus.h - ---- /dev/null -+++ b/include/uapi/linux/kdbus.h -@@ -0,0 +1,918 @@ -+/* -+ * 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_UAPI_H_ -+#define _KDBUS_UAPI_H_ -+ -+#include <linux/ioctl.h> -+#include <linux/types.h> -+ -+#define KDBUS_IOCTL_MAGIC 0x95 -+#define KDBUS_SRC_ID_KERNEL (0) -+#define KDBUS_DST_ID_NAME (0) -+#define KDBUS_MATCH_ID_ANY (~0ULL) -+#define KDBUS_DST_ID_BROADCAST (~0ULL) -+#define KDBUS_FLAG_KERNEL (1ULL << 63) -+ -+/** -+ * struct kdbus_notify_id_change - name registry change message -+ * @id: New or former owner of the name -+ * @flags: flags field from KDBUS_HELLO_* -+ * -+ * Sent from kernel to userspace when the owner or activator of -+ * a well-known name changes. -+ * -+ * Attached to: -+ * KDBUS_ITEM_ID_ADD -+ * KDBUS_ITEM_ID_REMOVE -+ */ -+struct kdbus_notify_id_change { -+ __u64 id; -+ __u64 flags; -+}; -+ -+/** -+ * struct kdbus_notify_name_change - name registry change message -+ * @old_id: ID and flags of former owner of a name -+ * @new_id: ID and flags of new owner of a name -+ * @name: Well-known name -+ * -+ * Sent from kernel to userspace when the owner or activator of -+ * a well-known name changes. -+ * -+ * Attached to: -+ * KDBUS_ITEM_NAME_ADD -+ * KDBUS_ITEM_NAME_REMOVE -+ * KDBUS_ITEM_NAME_CHANGE -+ */ -+struct kdbus_notify_name_change { -+ struct kdbus_notify_id_change old_id; -+ struct kdbus_notify_id_change new_id; -+ char name[0]; -+}; -+ -+/** -+ * struct kdbus_creds - process credentials -+ * @uid: User ID -+ * @gid: Group ID -+ * @pid: Process ID -+ * @tid: Thread ID -+ * @starttime: Starttime of the process -+ * -+ * The starttime of the process PID. This is useful to detect PID overruns -+ * from the client side. i.e. if you use the PID to look something up in -+ * /proc/$PID/ you can afterwards check the starttime field of it, to ensure -+ * you didn't run into a PID overrun. -+ * -+ * Attached to: -+ * KDBUS_ITEM_CREDS -+ */ -+struct kdbus_creds { -+ __u64 uid; -+ __u64 gid; -+ __u64 pid; -+ __u64 tid; -+ __u64 starttime; -+}; -+ -+/** -+ * struct kdbus_caps - process capabilities -+ * @last_cap: Highest currently known capability bit -+ * @caps: Variable number of 32-bit capabilities flags -+ * -+ * Contains a variable number of 32-bit capabilities flags. -+ * -+ * Attached to: -+ * KDBUS_ITEM_CAPS -+ */ -+struct kdbus_caps { -+ __u32 last_cap; -+ __u32 caps[0]; -+}; -+ -+/** -+ * struct kdbus_audit - audit information -+ * @sessionid: The audit session ID -+ * @loginuid: The audit login uid -+ * -+ * Attached to: -+ * KDBUS_ITEM_AUDIT -+ */ -+struct kdbus_audit { -+ __u64 sessionid; -+ __u64 loginuid; -+}; -+ -+/** -+ * struct kdbus_timestamp -+ * @seqnum: Global per-domain message sequence number -+ * @monotonic_ns: Monotonic timestamp, in nanoseconds -+ * @realtime_ns: Realtime timestamp, in nanoseconds -+ * -+ * Attached to: -+ * KDBUS_ITEM_TIMESTAMP -+ */ -+struct kdbus_timestamp { -+ __u64 seqnum; -+ __u64 monotonic_ns; -+ __u64 realtime_ns; -+}; -+ -+/** -+ * struct kdbus_vec - I/O vector for kdbus payload items -+ * @size: The size of the vector -+ * @address: Memory address of data buffer -+ * @offset: Offset in the in-message payload memory, -+ * relative to the message head -+ * -+ * Attached to: -+ * KDBUS_ITEM_PAYLOAD_VEC, KDBUS_ITEM_PAYLOAD_OFF -+ */ -+struct kdbus_vec { -+ __u64 size; -+ union { -+ __u64 address; -+ __u64 offset; -+ }; -+}; -+ -+/** -+ * struct kdbus_bloom_parameter - bus-wide bloom parameters -+ * @size: Size of the bit field in bytes (m / 8) -+ * @n_hash: Number of hash functions used (k) -+ */ -+struct kdbus_bloom_parameter { -+ __u64 size; -+ __u64 n_hash; -+}; -+ -+/** -+ * struct kdbus_bloom_filter - bloom filter containing n elements -+ * @generation: Generation of the element set in the filter -+ * @data: Bit field, multiple of 8 bytes -+ */ -+struct kdbus_bloom_filter { -+ __u64 generation; -+ __u64 data[0]; -+}; -+ -+/** -+ * struct kdbus_memfd - a kdbus memfd -+ * @size: The memfd's size -+ * @fd: The file descriptor number -+ * @__pad: Padding to ensure proper alignment and size -+ * -+ * Attached to: -+ * KDBUS_ITEM_PAYLOAD_MEMFD -+ */ -+struct kdbus_memfd { -+ __u64 size; -+ int fd; -+ __u32 __pad; -+}; -+ -+/** -+ * struct kdbus_name - a registered well-known name with its flags -+ * @flags: Flags from KDBUS_NAME_* -+ * @name: Well-known name -+ * -+ * Attached to: -+ * KDBUS_ITEM_NAME -+ */ -+struct kdbus_name { -+ __u64 flags; -+ char name[0]; -+}; -+ -+/** -+ * struct kdbus_policy_access - policy access item -+ * @type: One of KDBUS_POLICY_ACCESS_* types -+ * @access: Access to grant -+ * @id: For KDBUS_POLICY_ACCESS_USER, the uid -+ * For KDBUS_POLICY_ACCESS_GROUP, the gid -+ */ -+struct kdbus_policy_access { -+ __u64 type; /* USER, GROUP, WORLD */ -+ __u64 access; /* OWN, TALK, SEE */ -+ __u64 id; /* uid, gid, 0 */ -+}; -+ -+/** -+ * enum kdbus_item_type - item types to chain data in a list -+ * @_KDBUS_ITEM_NULL: Uninitialized/invalid -+ * @_KDBUS_ITEM_USER_BASE: Start of user items -+ * @KDBUS_ITEM_PAYLOAD_VEC: Vector to data -+ * @KDBUS_ITEM_PAYLOAD_OFF: Data at returned offset to message head -+ * @KDBUS_ITEM_PAYLOAD_MEMFD: Data as sealed memfd -+ * @KDBUS_ITEM_FDS: Attached file descriptors -+ * @KDBUS_ITEM_BLOOM_PARAMETER: Bus-wide bloom parameters, used with -+ * KDBUS_CMD_BUS_MAKE, carries a -+ * struct kdbus_bloom_parameter -+ * @KDBUS_ITEM_BLOOM_FILTER: Bloom filter carried with a message, used to -+ * match against a bloom mask of a connection, -+ * carries a struct kdbus_bloom_filter -+ * @KDBUS_ITEM_BLOOM_MASK: Bloom mask used to match against a message's -+ * bloom filter -+ * @KDBUS_ITEM_DST_NAME: Destination's well-known name -+ * @KDBUS_ITEM_MAKE_NAME: Name of domain, bus, endpoint -+ * @KDBUS_ITEM_ATTACH_FLAGS: Attach-flags, used for updating which metadata -+ * a connection subscribes to -+ * @_KDBUS_ITEM_ATTACH_BASE: Start of metadata attach items -+ * @KDBUS_ITEM_NAME: Well-know name with flags -+ * @KDBUS_ITEM_ID: Connection ID -+ * @KDBUS_ITEM_TIMESTAMP: Timestamp -+ * @KDBUS_ITEM_CREDS: Process credential -+ * @KDBUS_ITEM_AUXGROUPS: Auxiliary process groups -+ * @KDBUS_ITEM_PID_COMM: Process ID "comm" identifier -+ * @KDBUS_ITEM_TID_COMM: Thread ID "comm" identifier -+ * @KDBUS_ITEM_EXE: The path of the executable -+ * @KDBUS_ITEM_CMDLINE: The process command line -+ * @KDBUS_ITEM_CGROUP: The croup membership -+ * @KDBUS_ITEM_CAPS: The process capabilities -+ * @KDBUS_ITEM_SECLABEL: The security label -+ * @KDBUS_ITEM_AUDIT: The audit IDs -+ * @KDBUS_ITEM_CONN_NAME: The connection's human-readable name (debugging) -+ * @_KDBUS_ITEM_POLICY_BASE: Start of policy items -+ * @KDBUS_ITEM_POLICY_ACCESS: Policy access block -+ * @_KDBUS_ITEM_KERNEL_BASE: Start of kernel-generated message items -+ * @KDBUS_ITEM_NAME_ADD: Notify in struct kdbus_notify_name_change -+ * @KDBUS_ITEM_NAME_REMOVE: Notify in struct kdbus_notify_name_change -+ * @KDBUS_ITEM_NAME_CHANGE: Notify in struct kdbus_notify_name_change -+ * @KDBUS_ITEM_ID_ADD: Notify in struct kdbus_notify_id_change -+ * @KDBUS_ITEM_ID_REMOVE: Notify in struct kdbus_notify_id_change -+ * @KDBUS_ITEM_REPLY_TIMEOUT: Timeout has been reached -+ * @KDBUS_ITEM_REPLY_DEAD: Destination died -+ */ -+enum kdbus_item_type { -+ _KDBUS_ITEM_NULL, -+ _KDBUS_ITEM_USER_BASE, -+ KDBUS_ITEM_PAYLOAD_VEC = _KDBUS_ITEM_USER_BASE, -+ KDBUS_ITEM_PAYLOAD_OFF, -+ KDBUS_ITEM_PAYLOAD_MEMFD, -+ KDBUS_ITEM_FDS, -+ KDBUS_ITEM_BLOOM_PARAMETER, -+ KDBUS_ITEM_BLOOM_FILTER, -+ KDBUS_ITEM_BLOOM_MASK, -+ KDBUS_ITEM_DST_NAME, -+ KDBUS_ITEM_MAKE_NAME, -+ KDBUS_ITEM_ATTACH_FLAGS, -+ -+ _KDBUS_ITEM_ATTACH_BASE = 0x1000, -+ KDBUS_ITEM_NAME = _KDBUS_ITEM_ATTACH_BASE, -+ KDBUS_ITEM_ID, -+ KDBUS_ITEM_TIMESTAMP, -+ KDBUS_ITEM_CREDS, -+ KDBUS_ITEM_AUXGROUPS, -+ KDBUS_ITEM_PID_COMM, -+ KDBUS_ITEM_TID_COMM, -+ KDBUS_ITEM_EXE, -+ KDBUS_ITEM_CMDLINE, -+ KDBUS_ITEM_CGROUP, -+ KDBUS_ITEM_CAPS, -+ KDBUS_ITEM_SECLABEL, -+ KDBUS_ITEM_AUDIT, -+ KDBUS_ITEM_CONN_NAME, -+ -+ _KDBUS_ITEM_POLICY_BASE = 0x2000, -+ KDBUS_ITEM_POLICY_ACCESS = _KDBUS_ITEM_POLICY_BASE, -+ -+ _KDBUS_ITEM_KERNEL_BASE = 0x8000, -+ KDBUS_ITEM_NAME_ADD = _KDBUS_ITEM_KERNEL_BASE, -+ KDBUS_ITEM_NAME_REMOVE, -+ KDBUS_ITEM_NAME_CHANGE, -+ KDBUS_ITEM_ID_ADD, -+ KDBUS_ITEM_ID_REMOVE, -+ KDBUS_ITEM_REPLY_TIMEOUT, -+ KDBUS_ITEM_REPLY_DEAD, -+}; -+ -+/** -+ * struct kdbus_item - chain of data blocks -+ * @size: Overall data record size -+ * @type: Kdbus_item type of data -+ * @data: Generic bytes -+ * @data32: Generic 32 bit array -+ * @data64: Generic 64 bit array -+ * @str: Generic string -+ * @id: Connection ID -+ * @vec: KDBUS_ITEM_PAYLOAD_VEC -+ * @creds: KDBUS_ITEM_CREDS -+ * @audit: KDBUS_ITEM_AUDIT -+ * @timestamp: KDBUS_ITEM_TIMESTAMP -+ * @name: KDBUS_ITEM_NAME -+ * @bloom_parameter: KDBUS_ITEM_BLOOM_PARAMETER -+ * @bloom_filter: KDBUS_ITEM_BLOOM_FILTER -+ * @memfd: KDBUS_ITEM_PAYLOAD_MEMFD -+ * @name_change: KDBUS_ITEM_NAME_ADD -+ * KDBUS_ITEM_NAME_REMOVE -+ * KDBUS_ITEM_NAME_CHANGE -+ * @id_change: KDBUS_ITEM_ID_ADD -+ * KDBUS_ITEM_ID_REMOVE -+ * @policy: KDBUS_ITEM_POLICY_ACCESS -+ */ -+struct kdbus_item { -+ __u64 size; -+ __u64 type; -+ union { -+ __u8 data[0]; -+ __u32 data32[0]; -+ __u64 data64[0]; -+ char str[0]; -+ -+ __u64 id; -+ struct kdbus_vec vec; -+ struct kdbus_creds creds; -+ struct kdbus_audit audit; -+ struct kdbus_caps caps; -+ struct kdbus_timestamp timestamp; -+ struct kdbus_name name; -+ struct kdbus_bloom_parameter bloom_parameter; -+ struct kdbus_bloom_filter bloom_filter; -+ struct kdbus_memfd memfd; -+ int fds[0]; -+ struct kdbus_notify_name_change name_change; -+ struct kdbus_notify_id_change id_change; -+ struct kdbus_policy_access policy_access; -+ }; -+}; -+ -+/** -+ * enum kdbus_msg_flags - type of message -+ * @KDBUS_MSG_FLAGS_EXPECT_REPLY: Expect a reply message, used for -+ * method calls. The userspace-supplied -+ * cookie identifies the message and the -+ * respective reply carries the cookie -+ * in cookie_reply -+ * @KDBUS_MSG_FLAGS_SYNC_REPLY: Wait for destination connection to -+ * reply to this message. The -+ * KDBUS_CMD_MSG_SEND ioctl() will block -+ * until the reply is received, and -+ * offset_reply in struct kdbus_msg will -+ * yield the offset in the sender's pool -+ * where the reply can be found. -+ * This flag is only valid if -+ * @KDBUS_MSG_FLAGS_EXPECT_REPLY is set as -+ * well. -+ * @KDBUS_MSG_FLAGS_NO_AUTO_START: Do not start a service, if the addressed -+ * name is not currently active -+ */ -+enum kdbus_msg_flags { -+ KDBUS_MSG_FLAGS_EXPECT_REPLY = 1ULL << 0, -+ KDBUS_MSG_FLAGS_SYNC_REPLY = 1ULL << 1, -+ KDBUS_MSG_FLAGS_NO_AUTO_START = 1ULL << 2, -+}; -+ -+/** -+ * enum kdbus_payload_type - type of payload carried by message -+ * @KDBUS_PAYLOAD_KERNEL: Kernel-generated simple message -+ * @KDBUS_PAYLOAD_DBUS: D-Bus marshalling "DBusDBus" -+ */ -+enum kdbus_payload_type { -+ KDBUS_PAYLOAD_KERNEL, -+ KDBUS_PAYLOAD_DBUS = 0x4442757344427573ULL, -+}; -+ -+/** -+ * struct kdbus_msg - the representation of a kdbus message -+ * @size: Total size of the message -+ * @flags: Message flags (KDBUS_MSG_FLAGS_*), userspace → kernel -+ * @kernel_flags: Supported message flags, kernel → userspace -+ * @priority: Message queue priority value -+ * @dst_id: 64-bit ID of the destination connection -+ * @src_id: 64-bit ID of the source connection -+ * @payload_type: Payload type (KDBUS_PAYLOAD_*) -+ * @cookie: Userspace-supplied cookie, for the connection -+ * to identify its messages -+ * @timeout_ns: The time to wait for a message reply from the peer. -+ * If there is no reply, a kernel-generated message -+ * with an attached KDBUS_ITEM_REPLY_TIMEOUT item -+ * is sent to @src_id. The timeout is expected in -+ * nanoseconds and as absolute CLOCK_MONOTONIC value. -+ * @cookie_reply: A reply to the requesting message with the same -+ * cookie. The requesting connection can match its -+ * request and the reply with this value -+ * @offset_reply: If KDBUS_MSG_FLAGS_EXPECT_REPLY, this field will -+ * contain the offset in the sender's pool where the -+ * reply is stored. -+ * @items: A list of kdbus_items containing the message payload -+ */ -+struct kdbus_msg { -+ __u64 size; -+ __u64 flags; -+ __u64 kernel_flags; -+ __s64 priority; -+ __u64 dst_id; -+ __u64 src_id; -+ __u64 payload_type; -+ __u64 cookie; -+ union { -+ __u64 timeout_ns; -+ __u64 cookie_reply; -+ __u64 offset_reply; -+ }; -+ struct kdbus_item items[0]; -+} __attribute__((aligned(8))); -+ -+/** -+ * enum kdbus_recv_flags - flags for de-queuing messages -+ * @KDBUS_RECV_PEEK: Return the next queued message without -+ * actually de-queuing it, and without installing -+ * any file descriptors or other resources. It is -+ * usually used to determine the activating -+ * connection of a bus name. -+ * @KDBUS_RECV_DROP: Drop and free the next queued message and all -+ * its resources without actually receiving it. -+ * @KDBUS_RECV_USE_PRIORITY: Only de-queue messages with the specified or -+ * higher priority (lowest values); if not set, -+ * the priority value is ignored. -+ */ -+enum kdbus_recv_flags { -+ KDBUS_RECV_PEEK = 1ULL << 0, -+ KDBUS_RECV_DROP = 1ULL << 1, -+ KDBUS_RECV_USE_PRIORITY = 1ULL << 2, -+}; -+ -+/** -+ * struct kdbus_cmd_recv - struct to de-queue a buffered message -+ * @flags: KDBUS_RECV_* flags, userspace → kernel -+ * @kernel_flags: Supported KDBUS_RECV_* flags, kernel → userspace -+ * @priority: Minimum priority of the messages to de-queue. Lowest -+ * values have the highest priority. -+ * @offset: Returned offset in the pool where the message is -+ * stored. The user must use KDBUS_CMD_FREE to free -+ * the allocated memory. -+ * -+ * This struct is used with the KDBUS_CMD_MSG_RECV ioctl. -+ */ -+struct kdbus_cmd_recv { -+ __u64 flags; -+ __u64 kernel_flags; -+ __s64 priority; -+ __u64 offset; -+} __attribute__((aligned(8))); -+ -+/** -+ * struct kdbus_cmd_cancel - struct to cancel a synchronously pending message -+ * @cookie The cookie of the pending message -+ * @flags Flags for the free command. Currently unused. -+ * -+ * This struct is used with the KDBUS_CMD_CANCEL ioctl. -+ */ -+struct kdbus_cmd_cancel { -+ __u64 cookie; -+ __u64 flags; -+} __attribute__((aligned(8))); -+ -+/** -+ * struct kdbus_cmd_free - struct to free a slice of memory in the pool -+ * @offset: The offset of the memory slice, as returned by other -+ * ioctls -+ * @flags: Flags for the free command, userspace → kernel -+ * @kernel_flags: Supported flags of the free command, userspace → kernel -+ * -+ * This struct is used with the KDBUS_CMD_FREE ioctl. -+ */ -+struct kdbus_cmd_free { -+ __u64 offset; -+ __u64 flags; -+ __u64 kernel_flags; -+} __attribute__((aligned(8))); -+ -+/** -+ * enum kdbus_policy_access_type - permissions of a policy record -+ * @_KDBUS_POLICY_ACCESS_NULL: Uninitialized/invalid -+ * @KDBUS_POLICY_ACCESS_USER: Grant access to a uid -+ * @KDBUS_POLICY_ACCESS_GROUP: Grant access to gid -+ * @KDBUS_POLICY_ACCESS_WORLD: World-accessible -+ */ -+enum kdbus_policy_access_type { -+ _KDBUS_POLICY_ACCESS_NULL, -+ KDBUS_POLICY_ACCESS_USER, -+ KDBUS_POLICY_ACCESS_GROUP, -+ KDBUS_POLICY_ACCESS_WORLD, -+}; -+ -+/** -+ * enum kdbus_policy_access_flags - mode flags -+ * @KDBUS_POLICY_OWN: Allow to own a well-known name -+ * Implies KDBUS_POLICY_TALK and KDBUS_POLICY_SEE -+ * @KDBUS_POLICY_TALK: Allow communication to a well-known name -+ * Implies KDBUS_POLICY_SEE -+ * @KDBUS_POLICY_SEE: Allow to see a well-known name -+ */ -+enum kdbus_policy_type { -+ KDBUS_POLICY_SEE = 0, -+ KDBUS_POLICY_TALK, -+ KDBUS_POLICY_OWN, -+}; -+ -+/** -+ * enum kdbus_hello_flags - flags for struct kdbus_cmd_hello -+ * @KDBUS_HELLO_ACCEPT_FD: The connection allows the reception of -+ * any passed file descriptors -+ * @KDBUS_HELLO_ACTIVATOR: Special-purpose connection which registers -+ * a well-know name for a process to be started -+ * when traffic arrives -+ * @KDBUS_HELLO_POLICY_HOLDER: Special-purpose connection which registers -+ * policy entries for a name. The provided name -+ * is not activated and not registered with the -+ * name database, it only allows unprivileged -+ * connections to aquire a name, talk or discover -+ * a service -+ * @KDBUS_HELLO_MONITOR: Special-purpose connection to monitor -+ * bus traffic -+ */ -+enum kdbus_hello_flags { -+ KDBUS_HELLO_ACCEPT_FD = 1ULL << 0, -+ KDBUS_HELLO_ACTIVATOR = 1ULL << 1, -+ KDBUS_HELLO_POLICY_HOLDER = 1ULL << 2, -+ KDBUS_HELLO_MONITOR = 1ULL << 3, -+}; -+ -+/** -+ * enum kdbus_attach_flags - flags for metadata attachments -+ * @KDBUS_ATTACH_TIMESTAMP: Timestamp -+ * @KDBUS_ATTACH_CREDS: Credentials -+ * @KDBUS_ATTACH_AUXGROUPS: Auxiliary groups -+ * @KDBUS_ATTACH_NAMES: Well-known names -+ * @KDBUS_ATTACH_COMM_TID: The "comm" process identifier of the TID -+ * @KDBUS_ATTACH_COMM_PID: The "comm" process identifier of the PID -+ * @KDBUS_ATTACH_EXE: The path of the executable -+ * @KDBUS_ATTACH_CMDLINE: The process command line -+ * @KDBUS_ATTACH_CGROUP: The croup membership -+ * @KDBUS_ATTACH_CAPS: The process capabilities -+ * @KDBUS_ATTACH_SECLABEL: The security label -+ * @KDBUS_ATTACH_AUDIT: The audit IDs -+ * @KDBUS_ATTACH_CONN_NAME: The human-readable connection name -+ * @_KDBUS_ATTACH_ALL: All of the above -+ */ -+enum kdbus_attach_flags { -+ KDBUS_ATTACH_TIMESTAMP = 1ULL << 0, -+ KDBUS_ATTACH_CREDS = 1ULL << 1, -+ KDBUS_ATTACH_AUXGROUPS = 1ULL << 2, -+ KDBUS_ATTACH_NAMES = 1ULL << 3, -+ KDBUS_ATTACH_TID_COMM = 1ULL << 4, -+ KDBUS_ATTACH_PID_COMM = 1ULL << 5, -+ KDBUS_ATTACH_EXE = 1ULL << 6, -+ KDBUS_ATTACH_CMDLINE = 1ULL << 7, -+ KDBUS_ATTACH_CGROUP = 1ULL << 8, -+ KDBUS_ATTACH_CAPS = 1ULL << 9, -+ KDBUS_ATTACH_SECLABEL = 1ULL << 10, -+ KDBUS_ATTACH_AUDIT = 1ULL << 11, -+ KDBUS_ATTACH_CONN_NAME = 1ULL << 12, -+ _KDBUS_ATTACH_ALL = (1ULL << 13) - 1, -+}; -+ -+/** -+ * struct kdbus_cmd_hello - struct to say hello to kdbus -+ * @size: The total size of the structure -+ * @flags: Connection flags (KDBUS_HELLO_*), userspace → kernel -+ * @kernel_flags: Supported connection flags, kernel → userspace -+ * @attach_flags: Mask of metadata to attach to each message sent -+ * (KDBUS_ATTACH_*) -+ * @bus_flags: The flags field copied verbatim from the original -+ * KDBUS_CMD_BUS_MAKE ioctl. It's intended to be useful -+ * to do negotiation of features of the payload that is -+ * transferred (kernel → userspace) -+ * @id: The ID of this connection (kernel → userspace) -+ * @pool_size: Size of the connection's buffer where the received -+ * messages are placed -+ * @bloom: The bloom properties of the bus, specified -+ * by the bus creator (kernel → userspace) -+ * @id128: Unique 128-bit ID of the bus (kernel → userspace) -+ * @items: A list of items -+ * -+ * This struct is used with the KDBUS_CMD_HELLO ioctl. -+ */ -+struct kdbus_cmd_hello { -+ __u64 size; -+ __u64 flags; -+ __u64 kernel_flags; -+ __u64 attach_flags; -+ __u64 bus_flags; -+ __u64 id; -+ __u64 pool_size; -+ struct kdbus_bloom_parameter bloom; -+ __u8 id128[16]; -+ struct kdbus_item items[0]; -+} __attribute__((aligned(8))); -+ -+/** -+ * enum kdbus_make_flags - Flags for KDBUS_CMD_{BUS,EP,NS}_MAKE -+ * @KDBUS_MAKE_ACCESS_GROUP: Make the device node group-accessible -+ * @KDBUS_MAKE_ACCESS_WORLD: Make the device node world-accessible -+ */ -+enum kdbus_make_flags { -+ KDBUS_MAKE_ACCESS_GROUP = 1ULL << 0, -+ KDBUS_MAKE_ACCESS_WORLD = 1ULL << 1, -+}; -+ -+/** -+ * struct kdbus_cmd_make - struct to make a bus, an endpoint or a domain -+ * @size: The total size of the struct -+ * @flags: Properties for the bus/ep/domain to create, -+ * userspace → kernel -+ * @kernel_flags: Supported flags for the used command, kernel → userspace -+ * @items: Items describing details -+ * -+ * This structure is used with the KDBUS_CMD_BUS_MAKE, KDBUS_CMD_ENDPOINT_MAKE -+ * and KDBUS_CMD_DOMAIN_MAKE ioctls. -+ */ -+struct kdbus_cmd_make { -+ __u64 size; -+ __u64 flags; -+ __u64 kernel_flags; -+ struct kdbus_item items[0]; -+} __attribute__((aligned(8))); -+ -+/** -+ * enum kdbus_name_flags - properties of a well-known name -+ * @KDBUS_NAME_REPLACE_EXISTING: Try to replace name of other connections -+ * @KDBUS_NAME_ALLOW_REPLACEMENT: Allow the replacement of the name -+ * @KDBUS_NAME_QUEUE: Name should be queued if busy -+ * @KDBUS_NAME_IN_QUEUE: Name is queued -+ * @KDBUS_NAME_ACTIVATOR: Name is owned by a activator connection -+ */ -+enum kdbus_name_flags { -+ KDBUS_NAME_REPLACE_EXISTING = 1ULL << 0, -+ KDBUS_NAME_ALLOW_REPLACEMENT = 1ULL << 1, -+ KDBUS_NAME_QUEUE = 1ULL << 2, -+ KDBUS_NAME_IN_QUEUE = 1ULL << 3, -+ KDBUS_NAME_ACTIVATOR = 1ULL << 4, -+}; -+ -+/** -+ * struct kdbus_cmd_name - struct to describe a well-known name -+ * @size: The total size of the struct -+ * @flags: Flags for a name entry (KDBUS_NAME_*), -+ * userspace → kernel, kernel → userspace -+ * @kernel_flags: Supported flags for a name entry, kernel → userspace -+ * @items: Item list, containing the well-known name as -+ * KDBUS_ITEM_NAME -+ * -+ * This structure is used with the KDBUS_CMD_NAME_ACQUIRE ioctl. -+ */ -+struct kdbus_cmd_name { -+ __u64 size; -+ __u64 flags; -+ __u64 kernel_flags; -+ struct kdbus_item items[0]; -+} __attribute__((aligned(8))); -+ -+/** -+ * struct kdbus_name_info - struct to describe a well-known name -+ * @size: The total size of the struct -+ * @flags: Flags for a name entry (KDBUS_NAME_*), -+ * @conn_flags: The flags of the owning connection (KDBUS_HELLO_*) -+ * @owner_id: The current owner of the name -+ * @items: Item list, containing the well-known name as -+ * KDBUS_ITEM_NAME -+ * -+ * This structure is used as return struct for the KDBUS_CMD_NAME_LIST ioctl. -+ */ -+struct kdbus_name_info { -+ __u64 size; -+ __u64 flags; -+ __u64 conn_flags; -+ __u64 owner_id; -+ struct kdbus_item items[0]; -+} __attribute__((aligned(8))); -+ -+/** -+ * enum kdbus_name_list_flags - what to include into the returned list -+ * @KDBUS_NAME_LIST_UNIQUE: All active connections -+ * @KDBUS_NAME_LIST_NAMES: All known well-known names -+ * @KDBUS_NAME_LIST_ACTIVATORS: All activator connections -+ * @KDBUS_NAME_LIST_QUEUED: All queued-up names -+ */ -+enum kdbus_name_list_flags { -+ KDBUS_NAME_LIST_UNIQUE = 1ULL << 0, -+ KDBUS_NAME_LIST_NAMES = 1ULL << 1, -+ KDBUS_NAME_LIST_ACTIVATORS = 1ULL << 2, -+ KDBUS_NAME_LIST_QUEUED = 1ULL << 3, -+}; -+ -+/** -+ * struct kdbus_cmd_name_list - request a list of name entries -+ * @flags: Flags for the query (KDBUS_NAME_LIST_*), -+ * userspace → kernel -+ * @kernel_flags: Supported flags for queries, kernel → userspace -+ * @offset: The returned offset in the caller's pool buffer. -+ * The user must use KDBUS_CMD_FREE to free the -+ * allocated memory. -+ * -+ * This structure is used with the KDBUS_CMD_NAME_LIST ioctl. -+ */ -+struct kdbus_cmd_name_list { -+ __u64 flags; -+ __u64 kernel_flags; -+ __u64 offset; -+} __attribute__((aligned(8))); -+ -+/** -+ * struct kdbus_name_list - information returned by KDBUS_CMD_NAME_LIST -+ * @size: The total size of the structure -+ * @names: A list of names -+ * -+ * Note that the user is responsible for freeing the allocated memory with -+ * the KDBUS_CMD_FREE ioctl. -+ */ -+struct kdbus_name_list { -+ __u64 size; -+ struct kdbus_name_info names[0]; -+}; -+ -+/** -+ * struct kdbus_cmd_info - struct used for KDBUS_CMD_CONN_INFO ioctl -+ * @size: The total size of the struct -+ * @flags: KDBUS_ATTACH_* flags, userspace → kernel -+ * @kernel_flags: Supported KDBUS_ATTACH_* flags, kernel → userspace -+ * @id: The 64-bit ID of the connection. If set to zero, passing -+ * @name is required. kdbus will look up the name to -+ * determine the ID in this case. -+ * @offset: Returned offset in the caller's pool buffer where the -+ * kdbus_info struct result is stored. The user must -+ * use KDBUS_CMD_FREE to free the allocated memory. -+ * @items: The optional item list, containing the -+ * well-known name to look up as a KDBUS_ITEM_NAME. -+ * Only needed in case @id is zero. -+ * -+ * On success, the KDBUS_CMD_CONN_INFO ioctl will return 0 and @offset will -+ * tell the user the offset in the connection pool buffer at which to find the -+ * result in a struct kdbus_info. -+ */ -+struct kdbus_cmd_info { -+ __u64 size; -+ __u64 flags; -+ __u64 kernel_flags; -+ __u64 id; -+ __u64 offset; -+ struct kdbus_item items[0]; -+} __attribute__((aligned(8))); -+ -+/** -+ * struct kdbus_info - information returned by KDBUS_CMD_*_INFO -+ * @size: The total size of the struct -+ * @id: The connection's or bus' 64-bit ID -+ * @flags: The connection's or bus' flags -+ * @items: A list of struct kdbus_item -+ * -+ * Note that the user is responsible for freeing the allocated memory with -+ * the KDBUS_CMD_FREE ioctl. -+ */ -+struct kdbus_info { -+ __u64 size; -+ __u64 id; -+ __u64 flags; -+ struct kdbus_item items[0]; -+}; -+ -+/** -+ * struct kdbus_cmd_update - update flags of a connection -+ * @size: The total size of the struct -+ * @flags: Flags for the update command, userspace → kernel -+ * @kernel_flags: Supported flags for this command, kernel → userspace -+ * @items: A list of struct kdbus_item -+ * -+ * This struct is used with the KDBUS_CMD_CONN_UPDATE ioctl. -+ */ -+struct kdbus_cmd_update { -+ __u64 size; -+ __u64 flags; -+ __u64 kernel_flags; -+ struct kdbus_item items[0]; -+} __attribute__((aligned(8))); -+ -+/** -+ * enum kdbus_cmd_match_flags - flags to control the KDBUS_CMD_MATCH_ADD ioctl -+ * @KDBUS_MATCH_REPLACE: If entries with the supplied cookie already -+ * exists, remove them before installing the new -+ * matches. -+ */ -+enum kdbus_cmd_match_flags { -+ KDBUS_MATCH_REPLACE = 1ULL << 0, -+}; -+ -+/** -+ * struct kdbus_cmd_match - struct to add or remove matches -+ * @size: The total size of the struct -+ * @cookie: Userspace supplied cookie. When removing, the cookie -+ * identifies the match to remove -+ * @flags: Flags for match command (KDBUS_MATCH_*), -+ * userspace → kernel -+ * @kernel_flags: Supported flags of the used command, kernel → userspace -+ * @items: A list of items for additional information -+ * -+ * This structure is used with the KDBUS_CMD_MATCH_ADD and -+ * KDBUS_CMD_MATCH_REMOVE ioctl. -+ */ -+struct kdbus_cmd_match { -+ __u64 size; -+ __u64 cookie; -+ __u64 flags; -+ __u64 kernel_flags; -+ struct kdbus_item items[0]; -+} __attribute__((aligned(8))); -+ -+/** -+ * enum kdbus_ioctl_type - Ioctl API -+ * @KDBUS_CMD_BUS_MAKE: After opening the "control" device node, this -+ * command creates a new bus with the specified -+ * name. The bus is immediately shut down and -+ * cleaned up when the opened "control" device node -+ * is closed. -+ * @KDBUS_CMD_DOMAIN_MAKE: Similar to KDBUS_CMD_BUS_MAKE, but it creates a -+ * new kdbus domain. -+ * @KDBUS_CMD_ENDPOINT_MAKE: Creates a new named special endpoint to talk to -+ * the bus. Such endpoints usually carry a more -+ * restrictive policy and grant restricted access -+ * to specific applications. -+ * @KDBUS_CMD_HELLO: By opening the bus device node a connection is -+ * created. After a HELLO the opened connection -+ * becomes an active peer on the bus. -+ * @KDBUS_CMD_BYEBYE: Disconnect a connection. If there are no -+ * messages queued up in the connection's pool, -+ * the call succeeds, and the handle is rendered -+ * unusable. Otherwise, -EBUSY is returned without -+ * any further side-effects. -+ * @KDBUS_CMD_MSG_SEND: Send a message and pass data from userspace to -+ * the kernel. -+ * @KDBUS_CMD_MSG_RECV: Receive a message from the kernel which is -+ * placed in the receiver's pool. -+ * @KDBUS_CMD_MSG_CANCEL: Cancel a pending request of a message that -+ * blocks while waiting for a reply. The parameter -+ * denotes the cookie of the message in flight. -+ * @KDBUS_CMD_FREE: Release the allocated memory in the receiver's -+ * pool. -+ * @KDBUS_CMD_NAME_ACQUIRE: Request a well-known bus name to associate with -+ * the connection. Well-known names are used to -+ * address a peer on the bus. -+ * @KDBUS_CMD_NAME_RELEASE: Release a well-known name the connection -+ * currently owns. -+ * @KDBUS_CMD_NAME_LIST: Retrieve the list of all currently registered -+ * well-known and unique names. -+ * @KDBUS_CMD_CONN_INFO: Retrieve credentials and properties of the -+ * initial creator of the connection. The data was -+ * stored at registration time and does not -+ * necessarily represent the connected process or -+ * the actual state of the process. -+ * @KDBUS_CMD_CONN_UPDATE: Update the properties of a connection. Used to -+ * update the metadata subscription mask and -+ * policy. -+ * @KDBUS_CMD_BUS_CREATOR_INFO: Retrieve information of the creator of the bus -+ * a connection is attached to. -+ * @KDBUS_CMD_ENDPOINT_UPDATE: Update the properties of a custom enpoint. Used -+ * to update the policy. -+ * @KDBUS_CMD_MATCH_ADD: Install a match which broadcast messages should -+ * be delivered to the connection. -+ * @KDBUS_CMD_MATCH_REMOVE: Remove a current match for broadcast messages. -+ */ -+enum kdbus_ioctl_type { -+ KDBUS_CMD_BUS_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x00, -+ struct kdbus_cmd_make), -+ KDBUS_CMD_DOMAIN_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x10, -+ struct kdbus_cmd_make), -+ KDBUS_CMD_ENDPOINT_MAKE = _IOW(KDBUS_IOCTL_MAGIC, 0x20, -+ struct kdbus_cmd_make), -+ -+ KDBUS_CMD_HELLO = _IOWR(KDBUS_IOCTL_MAGIC, 0x30, -+ struct kdbus_cmd_hello), -+ KDBUS_CMD_BYEBYE = _IO(KDBUS_IOCTL_MAGIC, 0x31), -+ -+ KDBUS_CMD_MSG_SEND = _IOWR(KDBUS_IOCTL_MAGIC, 0x40, -+ struct kdbus_msg), -+ KDBUS_CMD_MSG_RECV = _IOWR(KDBUS_IOCTL_MAGIC, 0x41, -+ struct kdbus_cmd_recv), -+ KDBUS_CMD_MSG_CANCEL = _IOW(KDBUS_IOCTL_MAGIC, 0x42, -+ struct kdbus_cmd_cancel), -+ KDBUS_CMD_FREE = _IOW(KDBUS_IOCTL_MAGIC, 0x43, -+ struct kdbus_cmd_free), -+ -+ KDBUS_CMD_NAME_ACQUIRE = _IOWR(KDBUS_IOCTL_MAGIC, 0x50, -+ struct kdbus_cmd_name), -+ KDBUS_CMD_NAME_RELEASE = _IOW(KDBUS_IOCTL_MAGIC, 0x51, -+ struct kdbus_cmd_name), -+ KDBUS_CMD_NAME_LIST = _IOWR(KDBUS_IOCTL_MAGIC, 0x52, -+ struct kdbus_cmd_name_list), -+ -+ KDBUS_CMD_CONN_INFO = _IOWR(KDBUS_IOCTL_MAGIC, 0x60, -+ struct kdbus_cmd_info), -+ KDBUS_CMD_CONN_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x61, -+ struct kdbus_cmd_update), -+ KDBUS_CMD_BUS_CREATOR_INFO = _IOWR(KDBUS_IOCTL_MAGIC, 0x62, -+ struct kdbus_cmd_info), -+ -+ KDBUS_CMD_ENDPOINT_UPDATE = _IOW(KDBUS_IOCTL_MAGIC, 0x71, -+ struct kdbus_cmd_update), -+ -+ KDBUS_CMD_MATCH_ADD = _IOW(KDBUS_IOCTL_MAGIC, 0x80, -+ struct kdbus_cmd_match), -+ KDBUS_CMD_MATCH_REMOVE = _IOW(KDBUS_IOCTL_MAGIC, 0x81, -+ struct kdbus_cmd_match), -+}; -+ -+#endif /* _KDBUS_UAPI_H_ */ diff --git a/0003-kdbus-add-driver-skeleton-ioctl-entry-points-and-uti.patch b/0003-kdbus-add-driver-skeleton-ioctl-entry-points-and-uti.patch deleted file mode 100644 index 0b368d36bbbc41..00000000000000 --- a/0003-kdbus-add-driver-skeleton-ioctl-entry-points-and-uti.patch +++ /dev/null @@ -1,1684 +0,0 @@ -From b6d2f6739b43746d6747516112af3834607e65ad Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 18:52:52 +0200 -Subject: [PATCH 03/12] kdbus: add driver skeleton, ioctl entry points and - utility functions - -From: Daniel Mack <daniel@zonque.org> - -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. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - Documentation/ioctl/ioctl-number.txt | 1 - drivers/misc/kdbus/handle.c | 1221 +++++++++++++++++++++++++++++++++++ - drivers/misc/kdbus/handle.h | 46 + - drivers/misc/kdbus/limits.h | 77 ++ - drivers/misc/kdbus/main.c | 70 ++ - drivers/misc/kdbus/util.c | 108 +++ - drivers/misc/kdbus/util.h | 94 ++ - 7 files changed, 1617 insertions(+) - create mode 100644 drivers/misc/kdbus/handle.c - create mode 100644 drivers/misc/kdbus/handle.h - create mode 100644 drivers/misc/kdbus/limits.h - create mode 100644 drivers/misc/kdbus/main.c - create mode 100644 drivers/misc/kdbus/util.c - create mode 100644 drivers/misc/kdbus/util.h - ---- a/Documentation/ioctl/ioctl-number.txt -+++ b/Documentation/ioctl/ioctl-number.txt -@@ -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:buk@buks.ipn.de> ---- /dev/null -+++ b/drivers/misc/kdbus/handle.c -@@ -0,0 +1,1221 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <linux/device.h> -+#include <linux/file.h> -+#include <linux/fs.h> -+#include <linux/idr.h> -+#include <linux/init.h> -+#include <linux/module.h> -+#include <linux/mutex.h> -+#include <linux/poll.h> -+#include <linux/sched.h> -+#include <linux/sizes.h> -+#include <linux/slab.h> -+#include <linux/uaccess.h> -+#include <linux/syscalls.h> -+ -+#include "bus.h" -+#include "connection.h" -+#include "endpoint.h" -+#include "handle.h" -+#include "item.h" -+#include "match.h" -+#include "message.h" -+#include "metadata.h" -+#include "names.h" -+#include "domain.h" -+#include "policy.h" -+ -+/** -+ * enum kdbus_handle_type - type a handle can be of -+ * @_KDBUS_HANDLE_NULL: Uninitialized/invalid -+ * @KDBUS_HANDLE_CONTROL: New file descriptor of a control node -+ * @KDBUS_HANDLE_CONTROL_DOMAIN_OWNER: File descriptor to hold a domain -+ * @KDBUS_HANDLE_CONTROL_BUS_OWNER: File descriptor to hold a bus -+ * @KDBUS_HANDLE_EP: New file descriptor of a bus node -+ * @KDBUS_HANDLE_ENDPOINT_CONNECTED: A bus connection after HELLO -+ * @KDBUS_HANDLE_ENDPOINT_OWNER: File descriptor to hold an endpoint -+ */ -+enum kdbus_handle_type { -+ _KDBUS_HANDLE_NULL, -+ KDBUS_HANDLE_CONTROL, -+ KDBUS_HANDLE_CONTROL_DOMAIN_OWNER, -+ KDBUS_HANDLE_CONTROL_BUS_OWNER, -+ KDBUS_HANDLE_EP, -+ KDBUS_HANDLE_ENDPOINT_CONNECTED, -+ KDBUS_HANDLE_ENDPOINT_OWNER, -+}; -+ -+/** -+ * struct kdbus_handle - a handle to the kdbus system -+ * @type: Type of this handle (KDBUS_HANDLE_*) -+ * @domain: Domain for this handle -+ * @meta: Cached connection creator's metadata/credentials -+ * @ep: The endpoint for this handle, in case @type is -+ * KDBUS_HANDLE_EP, KDBUS_HANDLE_ENDPOINT_OWNER or -+ * KDBUS_HANDLE_ENDPOINT_CONNECTED -+ * @ptr: Generic pointer used as alias for other members -+ * in the same union by kdbus_handle_transform() -+ * @domain_owner: The domain this handle owns, in case @type -+ * is KDBUS_HANDLE_CONTROL_DOMAIN_OWNER -+ * @bus_owner: The bus this handle owns, in case @type -+ * is KDBUS_HANDLE_CONTROL_BUS_OWNER -+ * @ep_owner: The endpoint this handle owns, in case @type -+ * is KDBUS_HANDLE_ENDPOINT_OWNER -+ * @conn: The connection this handle owns, in case @type -+ * is KDBUS_HANDLE_EP, after HELLO it is -+ * KDBUS_HANDLE_ENDPOINT_CONNECTED -+ */ -+struct kdbus_handle { -+ enum kdbus_handle_type type; -+ struct kdbus_domain *domain; -+ struct kdbus_meta *meta; -+ struct kdbus_ep *ep; -+ union { -+ void *ptr; -+ struct kdbus_domain *domain_owner; -+ struct kdbus_bus *bus_owner; -+ struct kdbus_ep *ep_owner; -+ struct kdbus_conn *conn; -+ }; -+}; -+ -+/* kdbus major */ -+static unsigned int kdbus_major; -+ -+/* map of minors to objects */ -+static DEFINE_IDR(kdbus_minor_idr); -+ -+/* kdbus minor lock */ -+static DEFINE_SPINLOCK(kdbus_minor_lock); -+ -+int kdbus_minor_init(void) -+{ -+ int ret; -+ -+ ret = __register_chrdev(0, 0, 0xfffff, KBUILD_MODNAME, -+ &kdbus_handle_ops); -+ if (ret < 0) -+ return ret; -+ -+ kdbus_major = ret; -+ return 0; -+} -+ -+void kdbus_minor_exit(void) -+{ -+ __unregister_chrdev(kdbus_major, 0, 0xfffff, KBUILD_MODNAME); -+ idr_destroy(&kdbus_minor_idr); -+} -+ -+static void *kdbus_minor_pack(enum kdbus_minor_type type, void *ptr) -+{ -+ unsigned long p = (unsigned long)ptr; -+ -+ BUILD_BUG_ON(KDBUS_MINOR_CNT > 4); -+ -+ if (WARN_ON(p & 0x3UL || type >= KDBUS_MINOR_CNT)) -+ return NULL; -+ -+ return (void *)(p | (unsigned long)type); -+} -+ -+static enum kdbus_minor_type kdbus_minor_unpack(void **ptr) -+{ -+ unsigned long p = (unsigned long)*ptr; -+ -+ *ptr = (void *)(p & ~0x3UL); -+ return p & 0x3UL; -+} -+ -+static void kdbus_minor_ref(enum kdbus_minor_type type, void *ptr) -+{ -+ if (ptr) { -+ switch (type) { -+ case KDBUS_MINOR_CONTROL: -+ kdbus_domain_ref(ptr); -+ break; -+ case KDBUS_MINOR_EP: -+ kdbus_ep_ref(ptr); -+ break; -+ default: -+ break; -+ } -+ } -+} -+ -+static void kdbus_minor_unref(enum kdbus_minor_type type, void *ptr) -+{ -+ if (ptr) { -+ switch (type) { -+ case KDBUS_MINOR_CONTROL: -+ kdbus_domain_unref(ptr); -+ break; -+ case KDBUS_MINOR_EP: -+ kdbus_ep_unref(ptr); -+ break; -+ default: -+ break; -+ } -+ } -+} -+ -+/** -+ * kdbus_minor_alloc() - allocate a minor for a new kdbus device node -+ * @type: The type of device to allocate -+ * @ptr: The opaque pointer of the new device to store -+ * @out: Pointer to a dev_t for storing the result. -+ * -+ * Returns: 0 on success, in which case @out is set to the newly allocated -+ * device node. -+ */ -+int kdbus_minor_alloc(enum kdbus_minor_type type, void *ptr, dev_t *out) -+{ -+ int ret; -+ -+ ptr = kdbus_minor_pack(type, ptr); -+ -+ idr_preload(GFP_KERNEL); -+ spin_lock(&kdbus_minor_lock); -+ ret = idr_alloc(&kdbus_minor_idr, ptr, 0, 0, GFP_NOWAIT); -+ spin_unlock(&kdbus_minor_lock); -+ idr_preload_end(); -+ -+ if (ret < 0) -+ return ret; -+ -+ *out = MKDEV(kdbus_major, ret); -+ return 0; -+} -+ -+/** -+ * kdbus_minor_free() - free a minor of a kdbus device node -+ * @devt: The device node to remove -+ */ -+void kdbus_minor_free(dev_t devt) -+{ -+ unsigned int minor = MINOR(devt); -+ -+ if (!devt) -+ return; -+ -+ spin_lock(&kdbus_minor_lock); -+ idr_remove(&kdbus_minor_idr, minor); -+ spin_unlock(&kdbus_minor_lock); -+} -+ -+/** -+ * kdbus_minor_set() - set an existing minor type of a kdbus device node -+ * @devt: The device node to remove -+ * @type: New type to set -+ * @ptr: Associated pointer when node was initially registered -+ */ -+void kdbus_minor_set(dev_t devt, enum kdbus_minor_type type, void *ptr) -+{ -+ unsigned int minor = MINOR(devt); -+ -+ ptr = kdbus_minor_pack(type, ptr); -+ -+ spin_lock(&kdbus_minor_lock); -+ ptr = idr_replace(&kdbus_minor_idr, ptr, minor); -+ spin_unlock(&kdbus_minor_lock); -+} -+ -+static int kdbus_minor_lookup(dev_t devt, void **out) -+{ -+ unsigned int minor = MINOR(devt); -+ enum kdbus_minor_type type; -+ void *ptr; -+ -+ spin_lock(&kdbus_minor_lock); -+ ptr = idr_find(&kdbus_minor_idr, minor); -+ type = kdbus_minor_unpack(&ptr); -+ kdbus_minor_ref(type, ptr); -+ spin_unlock(&kdbus_minor_lock); -+ -+ if (!ptr) -+ return -ESHUTDOWN; -+ -+ *out = ptr; -+ return type; -+} -+ -+static int kdbus_handle_open(struct inode *inode, struct file *file) -+{ -+ enum kdbus_minor_type minor_type; -+ struct kdbus_handle *handle; -+ void *minor_ptr; -+ int ret; -+ -+ ret = kdbus_minor_lookup(inode->i_rdev, &minor_ptr); -+ if (ret < 0) -+ return ret; -+ -+ minor_type = ret; -+ -+ handle = kzalloc(sizeof(*handle), GFP_KERNEL); -+ if (!handle) { -+ kdbus_minor_unref(minor_type, minor_ptr); -+ return -ENOMEM; -+ } -+ -+ file->private_data = handle; -+ -+ switch (minor_type) { -+ case KDBUS_MINOR_CONTROL: -+ handle->type = KDBUS_HANDLE_CONTROL; -+ handle->domain = minor_ptr; -+ -+ break; -+ -+ case KDBUS_MINOR_EP: -+ handle->type = KDBUS_HANDLE_EP; -+ handle->ep = minor_ptr; -+ handle->domain = kdbus_domain_ref(handle->ep->bus->domain); -+ -+ /* cache the metadata/credentials of the creator */ -+ ret = kdbus_meta_new(&handle->meta); -+ if (ret < 0) -+ goto exit_free; -+ -+ ret = kdbus_meta_append(handle->meta, NULL, 0, -+ KDBUS_ATTACH_CREDS | -+ 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_free; -+ -+ break; -+ -+ default: -+ kdbus_minor_unref(minor_type, minor_ptr); -+ ret = -EINVAL; -+ goto exit_free; -+ } -+ -+ return 0; -+ -+exit_free: -+ kdbus_meta_free(handle->meta); -+ kdbus_ep_unref(handle->ep); -+ kdbus_domain_unref(handle->domain); -+ kfree(handle); -+ return ret; -+} -+ -+static int kdbus_handle_release(struct inode *inode, struct file *file) -+{ -+ struct kdbus_handle *handle = file->private_data; -+ -+ switch (handle->type) { -+ case KDBUS_HANDLE_CONTROL_DOMAIN_OWNER: -+ kdbus_domain_disconnect(handle->domain_owner); -+ kdbus_domain_unref(handle->domain_owner); -+ break; -+ -+ case KDBUS_HANDLE_CONTROL_BUS_OWNER: -+ kdbus_bus_disconnect(handle->bus_owner); -+ kdbus_bus_unref(handle->bus_owner); -+ break; -+ -+ case KDBUS_HANDLE_ENDPOINT_OWNER: -+ kdbus_ep_disconnect(handle->ep_owner); -+ kdbus_ep_unref(handle->ep_owner); -+ break; -+ -+ case KDBUS_HANDLE_ENDPOINT_CONNECTED: -+ kdbus_conn_disconnect(handle->conn, false); -+ kdbus_conn_unref(handle->conn); -+ break; -+ -+ default: -+ break; -+ } -+ -+ kdbus_meta_free(handle->meta); -+ kdbus_domain_unref(handle->domain); -+ kdbus_ep_unref(handle->ep); -+ kfree(handle); -+ -+ return 0; -+} -+ -+static int kdbus_copy_from_user(void *dest, -+ void __user *user_ptr, -+ size_t size) -+{ -+ if (!KDBUS_IS_ALIGNED8((uintptr_t)user_ptr)) -+ return -EFAULT; -+ -+ if (copy_from_user(dest, user_ptr, size)) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+static int kdbus_memdup_user(void __user *user_ptr, -+ void **out, -+ size_t size_min, -+ size_t size_max) -+{ -+ void *ptr = NULL; -+ u64 size; -+ int ret; -+ -+ ret = kdbus_copy_from_user(&size, user_ptr, sizeof(size)); -+ if (ret < 0) -+ return ret; -+ -+ if (size < size_min) -+ return -EINVAL; -+ -+ if (size > size_max) -+ return -EMSGSIZE; -+ -+ ptr = memdup_user(user_ptr, size); -+ if (IS_ERR(ptr)) -+ return PTR_ERR(ptr); -+ -+ *out = ptr; -+ return 0; -+} -+ -+static int kdbus_handle_transform(struct kdbus_handle *handle, -+ enum kdbus_handle_type old_type, -+ enum kdbus_handle_type new_type, -+ void *ctx_ptr) -+{ -+ int ret = -EBADFD; -+ -+ /* -+ * This transforms a handle from one state into another. Only a single -+ * transformation is allowed per handle, and it must be one of: -+ * CONTROL -> CONTROL_DOMAIN_OWNER -+ * -> CONTROL_BUS_OWNER -+ * EP -> EP_CONNECTED -+ * -> EP_OWNER -+ * -+ * State transformations are protected by the domain-lock. If another -+ * transformation runs in parallel, we will fail and the caller has to -+ * revert any previous steps. -+ * -+ * We also update any context before we write the new type. Reads can -+ * now be sure that iff a specific non-entry type is set, the context -+ * is accessible, too (given appropriate read-barriers). -+ */ -+ -+ mutex_lock(&handle->domain->lock); -+ if (handle->type == old_type) { -+ handle->ptr = ctx_ptr; -+ /* make sure handle->XYZ is accessible before the type is set */ -+ smp_wmb(); -+ handle->type = new_type; -+ ret = 0; -+ } -+ mutex_unlock(&handle->domain->lock); -+ -+ return ret; -+} -+ -+/* kdbus control device commands */ -+static long kdbus_handle_ioctl_control(struct file *file, unsigned int cmd, -+ void __user *buf) -+{ -+ struct kdbus_handle *handle = file->private_data; -+ struct kdbus_bus *bus = NULL; -+ struct kdbus_cmd_make *make; -+ struct kdbus_domain *domain = NULL; -+ umode_t mode = 0600; -+ void *p = NULL; -+ int ret; -+ -+ switch (cmd) { -+ case KDBUS_CMD_BUS_MAKE: { -+ kgid_t gid = KGIDT_INIT(0); -+ struct kdbus_bloom_parameter bloom; -+ char *name; -+ -+ ret = kdbus_memdup_user(buf, &p, sizeof(*make), -+ KDBUS_MAKE_MAX_SIZE); -+ if (ret < 0) -+ break; -+ -+ make = p; -+ -+ ret = kdbus_negotiate_flags(make, buf, typeof(*make), -+ KDBUS_MAKE_ACCESS_GROUP | -+ KDBUS_MAKE_ACCESS_WORLD); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_validate(make->items, -+ KDBUS_ITEMS_SIZE(make, items)); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_bus_make_user(make, &name, &bloom); -+ if (ret < 0) -+ break; -+ -+ if (make->flags & KDBUS_MAKE_ACCESS_WORLD) { -+ mode = 0666; -+ } else if (make->flags & KDBUS_MAKE_ACCESS_GROUP) { -+ mode = 0660; -+ gid = current_fsgid(); -+ } -+ -+ ret = kdbus_bus_new(handle->domain, make, name, &bloom, -+ mode, current_fsuid(), gid, &bus); -+ if (ret < 0) -+ break; -+ -+ /* turn the control fd into a new bus owner device */ -+ ret = kdbus_handle_transform(handle, KDBUS_HANDLE_CONTROL, -+ KDBUS_HANDLE_CONTROL_BUS_OWNER, -+ bus); -+ if (ret < 0) { -+ kdbus_bus_disconnect(bus); -+ kdbus_bus_unref(bus); -+ break; -+ } -+ -+ break; -+ } -+ -+ case KDBUS_CMD_DOMAIN_MAKE: { -+ const char *name; -+ -+ if (!capable(CAP_IPC_OWNER)) { -+ ret = -EPERM; -+ break; -+ } -+ -+ ret = kdbus_memdup_user(buf, &p, sizeof(*make), -+ KDBUS_MAKE_MAX_SIZE); -+ if (ret < 0) -+ break; -+ -+ make = p; -+ -+ ret = kdbus_negotiate_flags(make, buf, typeof(*make), -+ KDBUS_MAKE_ACCESS_GROUP | -+ KDBUS_MAKE_ACCESS_WORLD); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_validate(make->items, -+ KDBUS_ITEMS_SIZE(make, items)); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_get_str(make->items, -+ KDBUS_ITEMS_SIZE(make, items), -+ KDBUS_ITEM_MAKE_NAME, &name); -+ if (ret < 0) -+ break; -+ -+ if (make->flags & KDBUS_MAKE_ACCESS_WORLD) -+ mode = 0666; -+ -+ ret = kdbus_domain_new(handle->domain, name, mode, &domain); -+ if (ret < 0) -+ break; -+ -+ /* turn the control fd into a new domain owner device */ -+ ret = kdbus_handle_transform(handle, KDBUS_HANDLE_CONTROL, -+ KDBUS_HANDLE_CONTROL_DOMAIN_OWNER, -+ domain); -+ if (ret < 0) { -+ kdbus_domain_disconnect(domain); -+ kdbus_domain_unref(domain); -+ break; -+ } -+ -+ break; -+ } -+ -+ default: -+ ret = -ENOTTY; -+ break; -+ } -+ -+ kfree(p); -+ -+ return ret; -+} -+ -+/* kdbus endpoint make commands */ -+static long kdbus_handle_ioctl_ep(struct file *file, unsigned int cmd, -+ void __user *buf) -+{ -+ struct kdbus_handle *handle = file->private_data; -+ void *p = NULL; -+ long ret = 0; -+ -+ switch (cmd) { -+ case KDBUS_CMD_ENDPOINT_MAKE: { -+ struct kdbus_cmd_make *make; -+ umode_t mode = 0; -+ kgid_t gid = KGIDT_INIT(0); -+ const char *name; -+ struct kdbus_ep *ep; -+ -+ /* creating custom endpoints is a privileged operation */ -+ if (!kdbus_bus_uid_is_privileged(handle->ep->bus)) { -+ ret = -EPERM; -+ break; -+ } -+ -+ ret = kdbus_memdup_user(buf, &p, sizeof(*make), -+ KDBUS_MAKE_MAX_SIZE); -+ if (ret < 0) -+ break; -+ -+ make = p; -+ -+ ret = kdbus_negotiate_flags(make, buf, typeof(*make), -+ KDBUS_MAKE_ACCESS_GROUP | -+ KDBUS_MAKE_ACCESS_WORLD); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_validate(make->items, -+ KDBUS_ITEMS_SIZE(make, items)); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_get_str(make->items, -+ KDBUS_ITEMS_SIZE(make, items), -+ KDBUS_ITEM_MAKE_NAME, &name); -+ if (ret < 0) -+ break; -+ -+ if (make->flags & KDBUS_MAKE_ACCESS_WORLD) { -+ mode = 0666; -+ } else if (make->flags & KDBUS_MAKE_ACCESS_GROUP) { -+ mode = 0660; -+ gid = current_fsgid(); -+ } -+ -+ /* custom endpoints always have a policy db */ -+ ret = kdbus_ep_new(handle->ep->bus, name, mode, -+ current_fsuid(), gid, true, &ep); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_ep_policy_set(ep, make->items, -+ KDBUS_ITEMS_SIZE(make, items)); -+ if (ret < 0) { -+ kdbus_ep_disconnect(ep); -+ kdbus_ep_unref(ep); -+ break; -+ } -+ -+ /* -+ * Get an anonymous user to account messages against; custom -+ * endpoint users do not share the budget with the ordinary -+ * users created for a UID. -+ */ -+ ret = kdbus_domain_get_user(handle->ep->bus->domain, -+ INVALID_UID, &ep->user); -+ if (ret < 0) { -+ kdbus_ep_disconnect(ep); -+ kdbus_ep_unref(ep); -+ break; -+ } -+ -+ /* turn the ep fd into a new endpoint owner device */ -+ ret = kdbus_handle_transform(handle, KDBUS_HANDLE_EP, -+ KDBUS_HANDLE_ENDPOINT_OWNER, ep); -+ if (ret < 0) { -+ kdbus_ep_disconnect(ep); -+ kdbus_ep_unref(ep); -+ break; -+ } -+ -+ break; -+ } -+ -+ case KDBUS_CMD_HELLO: { -+ struct kdbus_cmd_hello *hello; -+ struct kdbus_conn *conn = NULL; -+ -+ ret = kdbus_memdup_user(buf, &p, sizeof(*hello), -+ KDBUS_HELLO_MAX_SIZE); -+ if (ret < 0) -+ break; -+ -+ hello = p; -+ -+ ret = kdbus_negotiate_flags(hello, buf, typeof(*hello), -+ KDBUS_HELLO_ACCEPT_FD | -+ KDBUS_HELLO_ACTIVATOR | -+ KDBUS_HELLO_POLICY_HOLDER | -+ KDBUS_HELLO_MONITOR); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_validate(hello->items, -+ KDBUS_ITEMS_SIZE(hello, items)); -+ if (ret < 0) -+ break; -+ -+ if (hello->pool_size == 0 || -+ !IS_ALIGNED(hello->pool_size, PAGE_SIZE)) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ ret = kdbus_conn_new(handle->ep, hello, handle->meta, &conn); -+ if (ret < 0) -+ break; -+ -+ /* turn the ep fd into a new connection */ -+ ret = kdbus_handle_transform(handle, KDBUS_HANDLE_EP, -+ KDBUS_HANDLE_ENDPOINT_CONNECTED, -+ conn); -+ if (ret < 0) { -+ kdbus_conn_disconnect(conn, false); -+ kdbus_conn_unref(conn); -+ break; -+ } -+ -+ if (copy_to_user(buf, hello, sizeof(*hello))) -+ ret = -EFAULT; -+ -+ break; -+ } -+ -+ default: -+ ret = -ENOTTY; -+ break; -+ } -+ -+ kfree(p); -+ -+ return ret; -+} -+ -+/* kdbus endpoint commands for connected peers */ -+static long kdbus_handle_ioctl_ep_connected(struct file *file, unsigned int cmd, -+ void __user *buf) -+{ -+ struct kdbus_handle *handle = file->private_data; -+ struct kdbus_conn *conn = handle->conn; -+ void *p = NULL; -+ long ret = 0; -+ -+ /* -+ * BYEBYE is special; we must not acquire a connection when -+ * calling into kdbus_conn_disconnect() or we will deadlock, -+ * because kdbus_conn_disconnect() will wait for all acquired -+ * references to be dropped. -+ */ -+ if (cmd == KDBUS_CMD_BYEBYE) { -+ if (!kdbus_conn_is_connected(conn)) -+ return -EOPNOTSUPP; -+ -+ return kdbus_conn_disconnect(conn, true); -+ } -+ -+ ret = kdbus_conn_acquire(conn); -+ if (ret < 0) -+ return ret; -+ -+ switch (cmd) { -+ case KDBUS_CMD_NAME_ACQUIRE: { -+ /* acquire a well-known name */ -+ struct kdbus_cmd_name *cmd_name; -+ -+ if (!kdbus_conn_is_connected(conn)) { -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ ret = kdbus_memdup_user(buf, &p, sizeof(*cmd_name), -+ sizeof(*cmd_name) + -+ KDBUS_ITEM_HEADER_SIZE + -+ KDBUS_NAME_MAX_LEN + 1); -+ if (ret < 0) -+ break; -+ -+ cmd_name = p; -+ -+ ret = kdbus_negotiate_flags(cmd_name, buf, typeof(*cmd_name), -+ KDBUS_NAME_REPLACE_EXISTING | -+ KDBUS_NAME_ALLOW_REPLACEMENT | -+ KDBUS_NAME_QUEUE); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_validate(cmd_name->items, -+ KDBUS_ITEMS_SIZE(cmd_name, items)); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_cmd_name_acquire(conn->bus->name_registry, conn, p); -+ if (ret < 0) -+ break; -+ -+ /* return flags to the caller */ -+ if (copy_to_user(buf, p, cmd_name->size)) -+ ret = -EFAULT; -+ -+ break; -+ } -+ -+ case KDBUS_CMD_NAME_RELEASE: { -+ /* release a well-known name */ -+ struct kdbus_cmd_name *cmd_name; -+ -+ if (!kdbus_conn_is_connected(conn)) { -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ ret = kdbus_memdup_user(buf, &p, sizeof(*cmd_name), -+ sizeof(*cmd_name) + -+ KDBUS_ITEM_HEADER_SIZE + -+ KDBUS_NAME_MAX_LEN + 1); -+ if (ret < 0) -+ break; -+ -+ cmd_name = p; -+ -+ ret = kdbus_negotiate_flags(cmd_name, buf, typeof(*cmd_name), -+ 0); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_validate(cmd_name->items, -+ KDBUS_ITEMS_SIZE(cmd_name, items)); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_cmd_name_release(conn->bus->name_registry, conn, p); -+ break; -+ } -+ -+ case KDBUS_CMD_NAME_LIST: { -+ struct kdbus_cmd_name_list cmd_list; -+ -+ /* query current IDs and names */ -+ if (kdbus_copy_from_user(&cmd_list, buf, sizeof(cmd_list))) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ ret = kdbus_negotiate_flags(&cmd_list, buf, typeof(cmd_list), -+ KDBUS_NAME_LIST_UNIQUE | -+ KDBUS_NAME_LIST_NAMES | -+ KDBUS_NAME_LIST_ACTIVATORS | -+ KDBUS_NAME_LIST_QUEUED); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_cmd_name_list(conn->bus->name_registry, -+ conn, &cmd_list); -+ if (ret < 0) -+ break; -+ -+ /* return allocated data */ -+ if (kdbus_offset_set_user(&cmd_list.offset, buf, -+ struct kdbus_cmd_name_list)) -+ ret = -EFAULT; -+ -+ break; -+ } -+ -+ case KDBUS_CMD_CONN_INFO: -+ case KDBUS_CMD_BUS_CREATOR_INFO: { -+ struct kdbus_cmd_info *cmd_info; -+ -+ /* return the properties of a connection */ -+ ret = kdbus_memdup_user(buf, &p, sizeof(*cmd_info), -+ sizeof(*cmd_info) + -+ KDBUS_NAME_MAX_LEN + 1); -+ if (ret < 0) -+ break; -+ -+ cmd_info = p; -+ -+ ret = kdbus_negotiate_flags(cmd_info, buf, typeof(*cmd_info), -+ _KDBUS_ATTACH_ALL); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_validate(cmd_info->items, -+ KDBUS_ITEMS_SIZE(cmd_info, items)); -+ if (ret < 0) -+ break; -+ -+ if (cmd == KDBUS_CMD_CONN_INFO) -+ ret = kdbus_cmd_info(conn, cmd_info); -+ else -+ ret = kdbus_cmd_bus_creator_info(conn, cmd_info); -+ -+ if (ret < 0) -+ break; -+ -+ if (kdbus_offset_set_user(&cmd_info->offset, buf, -+ struct kdbus_cmd_info)) -+ ret = -EFAULT; -+ -+ break; -+ } -+ -+ case KDBUS_CMD_CONN_UPDATE: { -+ /* update the properties of a connection */ -+ struct kdbus_cmd_update *cmd_update; -+ -+ if (!kdbus_conn_is_connected(conn) && -+ !kdbus_conn_is_policy_holder(conn) && -+ !kdbus_conn_is_monitor(conn)) { -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ ret = kdbus_memdup_user(buf, &p, sizeof(*cmd_update), -+ KDBUS_UPDATE_MAX_SIZE); -+ if (ret < 0) -+ break; -+ -+ cmd_update = p; -+ -+ ret = kdbus_negotiate_flags(cmd_update, buf, -+ typeof(*cmd_update), 0); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_validate(cmd_update->items, -+ KDBUS_ITEMS_SIZE(cmd_update, items)); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_cmd_conn_update(conn, p); -+ break; -+ } -+ -+ case KDBUS_CMD_MATCH_ADD: { -+ /* subscribe to/filter for broadcast messages */ -+ struct kdbus_cmd_match *cmd_match; -+ -+ if (!kdbus_conn_is_connected(conn) && -+ !kdbus_conn_is_monitor(conn)) { -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ ret = kdbus_memdup_user(buf, &p, sizeof(*cmd_match), -+ KDBUS_MATCH_MAX_SIZE); -+ if (ret < 0) -+ break; -+ -+ cmd_match = p; -+ -+ ret = kdbus_negotiate_flags(cmd_match, buf, typeof(*cmd_match), -+ KDBUS_MATCH_REPLACE); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_validate(cmd_match->items, -+ KDBUS_ITEMS_SIZE(cmd_match, items)); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_match_db_add(conn, cmd_match); -+ break; -+ } -+ -+ case KDBUS_CMD_MATCH_REMOVE: { -+ /* unsubscribe from broadcast messages */ -+ struct kdbus_cmd_match *cmd_match; -+ -+ if (!kdbus_conn_is_connected(conn) && -+ !kdbus_conn_is_monitor(conn)) { -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ ret = kdbus_memdup_user(buf, &p, -+ sizeof(*cmd_match), -+ sizeof(*cmd_match)); -+ if (ret < 0) -+ break; -+ -+ cmd_match = p; -+ -+ ret = kdbus_negotiate_flags(cmd_match, buf, typeof(*cmd_match), -+ 0); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_validate(cmd_match->items, -+ KDBUS_ITEMS_SIZE(cmd_match, items)); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_match_db_remove(conn, p); -+ break; -+ } -+ -+ case KDBUS_CMD_MSG_SEND: { -+ /* submit a message which will be queued in the receiver */ -+ struct kdbus_kmsg *kmsg = NULL; -+ -+ if (!kdbus_conn_is_connected(conn)) { -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ ret = kdbus_kmsg_new_from_user(conn, buf, &kmsg); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_conn_kmsg_send(conn->ep, conn, kmsg); -+ if (ret < 0) { -+ kdbus_kmsg_free(kmsg); -+ break; -+ } -+ -+ /* store the offset of the reply back to userspace */ -+ if (kmsg->msg.flags & KDBUS_MSG_FLAGS_SYNC_REPLY) { -+ struct kdbus_msg __user *msg = buf; -+ -+ if (copy_to_user(&msg->offset_reply, -+ &kmsg->msg.offset_reply, -+ sizeof(msg->offset_reply))) -+ ret = -EFAULT; -+ } -+ -+ kdbus_kmsg_free(kmsg); -+ break; -+ } -+ -+ case KDBUS_CMD_MSG_RECV: { -+ struct kdbus_cmd_recv cmd_recv; -+ -+ if (!kdbus_conn_is_connected(conn) && -+ !kdbus_conn_is_monitor(conn)) { -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ ret = kdbus_copy_from_user(&cmd_recv, buf, sizeof(cmd_recv)); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_negotiate_flags(&cmd_recv, buf, typeof(cmd_recv), -+ KDBUS_RECV_PEEK | KDBUS_RECV_DROP | -+ KDBUS_RECV_USE_PRIORITY); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_cmd_msg_recv(conn, &cmd_recv); -+ if (ret < 0) -+ break; -+ -+ /* return the address of the next message in the pool */ -+ if (kdbus_offset_set_user(&cmd_recv.offset, buf, -+ struct kdbus_cmd_recv)) -+ ret = -EFAULT; -+ -+ break; -+ } -+ -+ case KDBUS_CMD_MSG_CANCEL: { -+ struct kdbus_cmd_cancel cmd_cancel; -+ -+ if (!kdbus_conn_is_connected(conn)) { -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ /* cancel sync message send requests by cookie */ -+ ret = kdbus_copy_from_user(&cmd_cancel, buf, -+ sizeof(cmd_cancel)); -+ if (ret < 0) -+ break; -+ -+ if (cmd_cancel.flags != 0) -+ return -EOPNOTSUPP; -+ -+ ret = kdbus_cmd_msg_cancel(conn, cmd_cancel.cookie); -+ break; -+ } -+ -+ case KDBUS_CMD_FREE: { -+ struct kdbus_cmd_free cmd_free; -+ -+ if (!kdbus_conn_is_connected(conn) && -+ !kdbus_conn_is_monitor(conn)) { -+ ret = -EOPNOTSUPP; -+ break; -+ } -+ -+ /* free the memory used in the receiver's pool */ -+ ret = copy_from_user(&cmd_free, buf, sizeof(cmd_free)); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_negotiate_flags(&cmd_free, buf, typeof(cmd_free), -+ 0); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_pool_release_offset(conn->pool, cmd_free.offset); -+ break; -+ } -+ -+ default: -+ ret = -ENOTTY; -+ break; -+ } -+ -+ kdbus_conn_release(conn); -+ kfree(p); -+ return ret; -+} -+ -+/* kdbus endpoint commands for endpoint owners */ -+static long kdbus_handle_ioctl_ep_owner(struct file *file, unsigned int cmd, -+ void __user *buf) -+{ -+ struct kdbus_handle *handle = file->private_data; -+ struct kdbus_ep *ep = handle->ep_owner; -+ void *p = NULL; -+ long ret = 0; -+ -+ switch (cmd) { -+ case KDBUS_CMD_ENDPOINT_UPDATE: { -+ struct kdbus_cmd_update *cmd_update; -+ -+ /* update the properties of a custom endpoint */ -+ ret = kdbus_memdup_user(buf, &p, sizeof(*cmd_update), -+ KDBUS_UPDATE_MAX_SIZE); -+ if (ret < 0) -+ break; -+ -+ cmd_update = p; -+ -+ ret = kdbus_negotiate_flags(cmd_update, buf, -+ typeof(*cmd_update), 0); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_items_validate(cmd_update->items, -+ KDBUS_ITEMS_SIZE(cmd_update, items)); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_ep_policy_set(ep, cmd_update->items, -+ KDBUS_ITEMS_SIZE(cmd_update, items)); -+ break; -+ } -+ -+ default: -+ ret = -ENOTTY; -+ break; -+ } -+ -+ kfree(p); -+ return ret; -+} -+ -+static long kdbus_handle_ioctl(struct file *file, unsigned int cmd, -+ unsigned long arg) -+{ -+ struct kdbus_handle *handle = file->private_data; -+ void __user *argp = (void __user *)arg; -+ enum kdbus_handle_type type = handle->type; -+ -+ /* make sure all handle fields are set if handle->type is */ -+ smp_rmb(); -+ -+ switch (type) { -+ case KDBUS_HANDLE_CONTROL: -+ return kdbus_handle_ioctl_control(file, cmd, argp); -+ -+ case KDBUS_HANDLE_EP: -+ return kdbus_handle_ioctl_ep(file, cmd, argp); -+ -+ case KDBUS_HANDLE_ENDPOINT_CONNECTED: -+ return kdbus_handle_ioctl_ep_connected(file, cmd, argp); -+ -+ case KDBUS_HANDLE_ENDPOINT_OWNER: -+ return kdbus_handle_ioctl_ep_owner(file, cmd, argp); -+ -+ default: -+ return -EBADFD; -+ } -+} -+ -+static unsigned int kdbus_handle_poll(struct file *file, -+ struct poll_table_struct *wait) -+{ -+ struct kdbus_handle *handle = file->private_data; -+ struct kdbus_conn *conn; -+ unsigned int mask = POLLOUT | POLLWRNORM; -+ -+ /* Only a connected endpoint can read/write data */ -+ if (handle->type != KDBUS_HANDLE_ENDPOINT_CONNECTED) -+ return POLLERR | POLLHUP; -+ -+ /* make sure handle->conn is set if handle->type is */ -+ smp_rmb(); -+ conn = handle->conn; -+ -+ poll_wait(file, &conn->wait, wait); -+ -+ mutex_lock(&conn->lock); -+ if (!kdbus_conn_active(conn)) -+ mask = POLLERR | POLLHUP; -+ else if (!list_empty(&conn->queue.msg_list)) -+ mask |= POLLIN | POLLRDNORM; -+ mutex_unlock(&conn->lock); -+ -+ return mask; -+} -+ -+static int kdbus_handle_mmap(struct file *file, struct vm_area_struct *vma) -+{ -+ struct kdbus_handle *handle = file->private_data; -+ -+ if (handle->type != KDBUS_HANDLE_ENDPOINT_CONNECTED) -+ return -EPERM; -+ -+ /* make sure handle->conn is set if handle->type is */ -+ smp_rmb(); -+ -+ return kdbus_pool_mmap(handle->conn->pool, vma); -+} -+ -+const struct file_operations kdbus_handle_ops = { -+ .owner = THIS_MODULE, -+ .open = kdbus_handle_open, -+ .release = kdbus_handle_release, -+ .poll = kdbus_handle_poll, -+ .llseek = noop_llseek, -+ .unlocked_ioctl = kdbus_handle_ioctl, -+ .mmap = kdbus_handle_mmap, -+#ifdef CONFIG_COMPAT -+ .compat_ioctl = kdbus_handle_ioctl, -+#endif -+}; ---- /dev/null -+++ b/drivers/misc/kdbus/handle.h -@@ -0,0 +1,46 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_HANDLE_H -+#define __KDBUS_HANDLE_H -+ -+struct kdbus_domain; -+struct kdbus_ep; -+ -+extern const struct file_operations kdbus_handle_ops; -+ -+enum kdbus_minor_type { -+ KDBUS_MINOR_CONTROL, -+ KDBUS_MINOR_EP, -+ KDBUS_MINOR_CNT -+}; -+ -+int kdbus_minor_init(void); -+void kdbus_minor_exit(void); -+int kdbus_minor_alloc(enum kdbus_minor_type type, void *ptr, dev_t *out); -+void kdbus_minor_free(dev_t devt); -+void kdbus_minor_set(dev_t devt, enum kdbus_minor_type type, void *ptr); -+ -+/* type-safe kdbus_minor_set() */ -+static inline void kdbus_minor_set_control(dev_t devt, struct kdbus_domain *d) -+{ -+ kdbus_minor_set(devt, KDBUS_MINOR_CONTROL, d); -+} -+ -+/* type-safe kdbus_minor_set() */ -+static inline void kdbus_minor_set_ep(dev_t devt, struct kdbus_ep *e) -+{ -+ kdbus_minor_set(devt, KDBUS_MINOR_EP, e); -+} -+ -+#endif ---- /dev/null -+++ b/drivers/misc/kdbus/limits.h -@@ -0,0 +1,77 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_DEFAULTS_H -+#define __KDBUS_DEFAULTS_H -+ -+/* maximum size of message header and items */ -+#define KDBUS_MSG_MAX_SIZE SZ_8K -+ -+/* maximum number of message items */ -+#define KDBUS_MSG_MAX_ITEMS 128 -+ -+/* -+ * Maximum number of passed file descriptors -+ * Number taken from AF_UNIX upper limits -+ */ -+#define KDBUS_MSG_MAX_FDS 253 -+ -+/* maximum message payload size */ -+#define KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE SZ_2M -+ -+/* maximum size of bloom bit field in bytes */ -+#define KDBUS_BUS_BLOOM_MAX_SIZE SZ_4K -+ -+/* maximum length of well-known bus name */ -+#define KDBUS_NAME_MAX_LEN 255 -+ -+/* maximum length of bus, domain, ep name */ -+#define KDBUS_SYSNAME_MAX_LEN 63 -+ -+/* maximum size of make data */ -+#define KDBUS_MAKE_MAX_SIZE SZ_32K -+ -+/* maximum size of hello data */ -+#define KDBUS_HELLO_MAX_SIZE SZ_32K -+ -+/* maximum size for update commands */ -+#define KDBUS_UPDATE_MAX_SIZE SZ_32K -+ -+/* maximum number of matches per connection */ -+#define KDBUS_MATCH_MAX 256 -+ -+/* maximum size of match data */ -+#define KDBUS_MATCH_MAX_SIZE SZ_32K -+ -+/* maximum size of policy data */ -+#define KDBUS_POLICY_MAX_SIZE SZ_32K -+ -+/* maximum number of queued messages in a connection */ -+#define KDBUS_CONN_MAX_MSGS 256 -+ -+/* maximum number of queued messages from the same indvidual user */ -+#define KDBUS_CONN_MAX_MSGS_PER_USER 16 -+ -+/* maximum number of well-known names per connection */ -+#define KDBUS_CONN_MAX_NAMES 64 -+ -+/* maximum number of queued requests waiting for a reply */ -+#define KDBUS_CONN_MAX_REQUESTS_PENDING 128 -+ -+/* maximum number of connections per user in one domain */ -+#define KDBUS_USER_MAX_CONN 256 -+ -+/* maximum number of buses per user in one domain */ -+#define KDBUS_USER_MAX_BUSES 16 -+ -+#endif ---- /dev/null -+++ b/drivers/misc/kdbus/main.c -@@ -0,0 +1,70 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt -+#include <linux/device.h> -+#include <linux/fs.h> -+#include <linux/init.h> -+#include <linux/module.h> -+ -+#include "util.h" -+#include "domain.h" -+#include "handle.h" -+ -+/* kdbus initial domain */ -+static struct kdbus_domain *kdbus_domain_init; -+ -+static int __init kdbus_init(void) -+{ -+ int ret; -+ -+ ret = subsys_virtual_register(&kdbus_subsys, NULL); -+ if (ret < 0) -+ return ret; -+ -+ ret = kdbus_minor_init(); -+ if (ret < 0) -+ goto exit_subsys; -+ -+ /* -+ * Create the initial domain; it is world-accessible and -+ * provides the /dev/kdbus/control device node. -+ */ -+ ret = kdbus_domain_new(NULL, NULL, 0666, &kdbus_domain_init); -+ if (ret < 0) { -+ pr_err("failed to initialize, error=%i\n", ret); -+ goto exit_minor; -+ } -+ -+ pr_info("initialized\n"); -+ return 0; -+ -+exit_minor: -+ kdbus_minor_exit(); -+exit_subsys: -+ bus_unregister(&kdbus_subsys); -+ return ret; -+} -+ -+static void __exit kdbus_exit(void) -+{ -+ kdbus_domain_disconnect(kdbus_domain_init); -+ kdbus_domain_unref(kdbus_domain_init); -+ kdbus_minor_exit(); -+ bus_unregister(&kdbus_subsys); -+} -+ -+module_init(kdbus_init); -+module_exit(kdbus_exit); -+MODULE_LICENSE("GPL"); -+MODULE_DESCRIPTION("D-Bus, powerful, easy to use interprocess communication"); ---- /dev/null -+++ b/drivers/misc/kdbus/util.c -@@ -0,0 +1,108 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <linux/ctype.h> -+#include <linux/file.h> -+#include <linux/string.h> -+#include <linux/uaccess.h> -+ -+#include "limits.h" -+#include "util.h" -+ -+/** -+ * kdbus_sysname_valid() - validate names showing up in /proc, /sys and /dev -+ * @name: Name of domain, bus, endpoint -+ * -+ * Return: 0 if the given name is valid, otherwise negative errno -+ */ -+int kdbus_sysname_is_valid(const char *name) -+{ -+ unsigned int i; -+ size_t len; -+ -+ len = strlen(name); -+ if (len == 0) -+ return -EINVAL; -+ -+ for (i = 0; i < len; i++) { -+ if (isalpha(name[i])) -+ continue; -+ if (isdigit(name[i])) -+ continue; -+ if (name[i] == '_') -+ continue; -+ if (i > 0 && i + 1 < len && strchr("-.", name[i])) -+ continue; -+ -+ return -EINVAL; -+ } -+ -+ return 0; -+} -+ -+/** -+ * kdbus_check_and_write_flags() - check flags provided by user, and write the -+ * valid mask back -+ * @flags: The flags mask provided by userspace -+ * @buf: The buffer provided by userspace -+ * @offset_out: Offset of the kernel_flags field inside the user-provided struct -+ * @valid: Mask of valid bits -+ * -+ * This function will check whether the flags provided by userspace are within -+ * the combination of allowed bits to the kernel, with the KDBUS_FLAGS_KERNEL -+ * bit set in the return buffer. -+ * -+ * Return: 0 on success, -EFAULT if copy_to_user() failed, or -EINVAL if -+ * userspace submitted invalid bits in its mask. -+ */ -+int kdbus_check_and_write_flags(u64 flags, void __user *buf, -+ off_t offset_out, u64 valid) -+{ -+ u64 val = valid | KDBUS_FLAG_KERNEL; -+ -+ /* -+ * KDBUS_FLAG_KERNEL is reserved and will never be considered -+ * valid by any user of this function. -+ */ -+ WARN_ON_ONCE(valid & KDBUS_FLAG_KERNEL); -+ -+ if (copy_to_user(((u8 __user *) buf) + offset_out, &val, sizeof(val))) -+ return -EFAULT; -+ -+ if (flags & ~valid) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+/** -+ * kdbus_fput_files() - fput() an array of struct files -+ * @files: The array of files to put, may be NULL -+ * @count: The number of elements in @files -+ * -+ * Call fput() on all non-NULL elements in @files, and set the entries to -+ * NULL afterwards. -+ */ -+void kdbus_fput_files(struct file **files, unsigned int count) -+{ -+ int i; -+ -+ if (!files) -+ return; -+ -+ for (i = count - 1; i >= 0; i--) -+ if (files[i]) { -+ fput(files[i]); -+ files[i] = NULL; -+ } -+} ---- /dev/null -+++ b/drivers/misc/kdbus/util.h -@@ -0,0 +1,94 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_UTIL_H -+#define __KDBUS_UTIL_H -+ -+#include <linux/dcache.h> -+#include <linux/ioctl.h> -+ -+#include "kdbus.h" -+ -+/* all exported addresses are 64 bit */ -+#define KDBUS_PTR(addr) ((void __user *)(uintptr_t)(addr)) -+ -+/* all exported sizes are 64 bit and data aligned to 64 bit */ -+#define KDBUS_ALIGN8(s) ALIGN((s), 8) -+#define KDBUS_IS_ALIGNED8(s) (IS_ALIGNED(s, 8)) -+ -+/** -+ * kdbus_size_get_user - read the size variable from user memory -+ * @_s: Size variable -+ * @_b: Buffer to read from -+ * @_t: Structure, "size" is a member of -+ * -+ * Return: the result of copy_from_user() -+ */ -+#define kdbus_size_get_user(_s, _b, _t) \ -+({ \ -+ u64 __user *_sz = \ -+ (void __user *)((u8 __user *)(_b) + offsetof(_t, size));\ -+ copy_from_user(_s, _sz, sizeof(__u64)); \ -+}) -+ -+/** -+ * kdbus_offset_set_user - write the offset variable to user memory -+ * @_s: Offset variable -+ * @_b: Buffer to write to -+ * @_t: Structure, "offset" is a member of -+ * -+ * Return: the result of copy_to_user() -+ */ -+#define kdbus_offset_set_user(_s, _b, _t) \ -+({ \ -+ u64 __user *_sz = \ -+ (void __user *)((u8 __user *)(_b) + offsetof(_t, offset)); \ -+ copy_to_user(_sz, _s, sizeof(__u64)); \ -+}) -+ -+/** -+ * kdbus_str_hash - calculate a hash -+ * @str: String -+ * -+ * Return: hash value -+ */ -+static inline unsigned int kdbus_str_hash(const char *str) -+{ -+ return full_name_hash(str, strlen(str)); -+} -+ -+/** -+ * kdbus_str_valid - verify a string -+ * @str: String to verify -+ * @size: Size of buffer of string (including 0-byte) -+ * -+ * This verifies the string at position @str with size @size is properly -+ * zero-terminated and does not contain a 0-byte but at the end. -+ * -+ * Return: true if string is valid, false if not. -+ */ -+static inline bool kdbus_str_valid(const char *str, size_t size) -+{ -+ return size > 0 && memchr(str, '\0', size) == str + size - 1; -+} -+ -+int kdbus_sysname_is_valid(const char *name); -+void kdbus_fput_files(struct file **files, unsigned int count); -+int kdbus_check_and_write_flags(u64 flags, void __user *buf, -+ off_t offset_out, u64 valid); -+ -+#define kdbus_negotiate_flags(_s, _b, _t, _v) \ -+ kdbus_check_and_write_flags((_s)->flags, _b, \ -+ offsetof(_t, kernel_flags), _v) \ -+ -+#endif diff --git a/0004-kdbus-add-connection-pool-implementation.patch b/0004-kdbus-add-connection-pool-implementation.patch deleted file mode 100644 index d534b49e0187df..00000000000000 --- a/0004-kdbus-add-connection-pool-implementation.patch +++ /dev/null @@ -1,812 +0,0 @@ -From 277346aed34c8c33e554dfca87a0b12b5ae92182 Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 18:56:41 +0200 -Subject: [PATCH 04/12] kdbus: add connection pool implementation - -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. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - drivers/misc/kdbus/pool.c | 728 ++++++++++++++++++++++++++++++++++++++++++++++ - drivers/misc/kdbus/pool.h | 43 ++ - 2 files changed, 771 insertions(+) - create mode 100644 drivers/misc/kdbus/pool.c - create mode 100644 drivers/misc/kdbus/pool.h - ---- /dev/null -+++ b/drivers/misc/kdbus/pool.c -@@ -0,0 +1,728 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#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 "pool.h" -+#include "util.h" -+ -+/** -+ * struct kdbus_pool - the receiver's buffer -+ * @f: The backing shmem file -+ * @size: The size of the file -+ * @busy: The currently used size -+ * @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_MSG_SEND are copied direcly by the -+ * sending process into the receiver's pool. -+ * -+ * Messages received with KDBUS_CMD_MSG_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_MSG_FREE. -+ */ -+struct kdbus_pool { -+ struct file *f; -+ size_t size; -+ size_t busy; -+ 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 -+ * @public: Slice was exposed to userspace and may be freed -+ * with KDBUS_CMD_FREE. -+ * -+ * 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; -+ bool public; -+}; -+ -+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; -+ slice->public = false; -+ 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; -+ } -+ -+ 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 -+ * @slice: Slice allocated from the the pool -+ * @size: The number of bytes to allocate -+ * -+ * The returned slice is used for kdbus_pool_slice_free() to -+ * free the allocated memory. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_pool_slice_alloc(struct kdbus_pool *pool, -+ struct kdbus_pool_slice **slice, size_t size) -+{ -+ size_t slice_size = KDBUS_ALIGN8(size); -+ struct rb_node *n, *found = NULL; -+ struct kdbus_pool_slice *s; -+ int ret = 0; -+ -+ /* 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 = -ENOBUFS; -+ goto exit_unlock; -+ } -+ -+ /* no exact match, use the closest one */ -+ if (!n) -+ s = rb_entry(found, struct kdbus_pool_slice, rb_node); -+ -+ /* move slice from free to the busy tree */ -+ rb_erase(found, &pool->slices_free); -+ kdbus_pool_add_busy_slice(pool, s); -+ -+ /* we got a slice larger than what we asked for? */ -+ if (s->size > slice_size) { -+ struct kdbus_pool_slice *s_new; -+ -+ /* 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; -+ } -+ -+ s->free = false; -+ s->public = false; -+ pool->busy += s->size; -+ mutex_unlock(&pool->lock); -+ -+ *slice = s; -+ return 0; -+ -+exit_unlock: -+ mutex_unlock(&pool->lock); -+ return ret; -+} -+ -+static void __kdbus_pool_slice_free(struct kdbus_pool_slice *slice) -+{ -+ struct kdbus_pool *pool = slice->pool; -+ -+ BUG_ON(slice->free); -+ -+ rb_erase(&slice->rb_node, &pool->slices_busy); -+ pool->busy -= slice->size; -+ -+ /* 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_free() - give allocated memory back to the pool -+ * @slice: Slice allocated from the the pool -+ * -+ * The slice was returned by the call to kdbus_pool_alloc_slice(), the -+ * memory is returned to the pool. -+ */ -+void kdbus_pool_slice_free(struct kdbus_pool_slice *slice) -+{ -+ struct kdbus_pool *pool = slice->pool; -+ -+ mutex_lock(&pool->lock); -+ __kdbus_pool_slice_free(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, EINVAL if the offset is -+ * valid but not public. -+ */ -+int kdbus_pool_release_offset(struct kdbus_pool *pool, size_t off) -+{ -+ struct kdbus_pool_slice *slice; -+ int ret = 0; -+ -+ mutex_lock(&pool->lock); -+ slice = kdbus_pool_find_slice(pool, off); -+ if (slice) { -+ if (slice->public) -+ __kdbus_pool_slice_free(slice); -+ else -+ ret = -EINVAL; -+ } else { -+ ret = -ENXIO; -+ } -+ mutex_unlock(&pool->lock); -+ -+ return ret; -+} -+ -+/** -+ * kdbus_pool_slice_offset() - return the slice's offset inside the pool -+ * @slice: The slice -+ * -+ * Return: the offset in bytes. -+ */ -+size_t kdbus_pool_slice_offset(const struct kdbus_pool_slice *slice) -+{ -+ return slice->off; -+} -+ -+/** -+ * kdbus_pool_slice_make_public() - set a slice's public flag to true -+ * @slice: The slice -+ */ -+void kdbus_pool_slice_make_public(struct kdbus_pool_slice *slice) -+{ -+ slice->public = true; -+} -+ -+/** -+ * kdbus_pool_new() - create a new pool -+ * @name: Name of the (deleted) file which shows up in -+ * /proc, used for debugging -+ * @pool: Newly allocated pool -+ * @size: Maximum size of the pool -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_pool_new(const char *name, struct kdbus_pool **pool, size_t size) -+{ -+ struct kdbus_pool_slice *s; -+ struct kdbus_pool *p; -+ struct file *f; -+ char *n = NULL; -+ int ret; -+ -+ BUG_ON(*pool); -+ -+ p = kzalloc(sizeof(*p), GFP_KERNEL); -+ if (!p) -+ return -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, VM_NORESERVE); -+ 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->busy = 0; -+ 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); -+ *pool = p; -+ return 0; -+ -+exit_put_write: -+ put_write_access(file_inode(f)); -+exit_put_shmem: -+ fput(f); -+exit_free: -+ kfree(p); -+ return 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_remain() - the number of free bytes in the pool -+ * @pool: The receiver's pool -+ * -+ * Return: the number of unallocated bytes in the pool -+ */ -+size_t kdbus_pool_remain(struct kdbus_pool *pool) -+{ -+ size_t size; -+ -+ mutex_lock(&pool->lock); -+ size = pool->size - pool->busy; -+ mutex_unlock(&pool->lock); -+ -+ return size; -+} -+ -+/* copy data from a file to a page in the receiver's pool */ -+static int kdbus_pool_copy_file(struct page *p, size_t start, -+ struct file *f, size_t off, size_t count) -+{ -+ loff_t o = off; -+ char *kaddr; -+ ssize_t n; -+ -+ kaddr = kmap(p); -+ n = f->f_op->read(f, (char __force __user *)kaddr + start, count, &o); -+ kunmap(p); -+ if (n < 0) -+ return n; -+ if (n != count) -+ return -EFAULT; -+ -+ return 0; -+} -+ -+/* copy data to a page in the receiver's pool */ -+static int kdbus_pool_copy_data(struct page *p, size_t start, -+ const void __user *from, size_t count) -+{ -+ unsigned long remain; -+ char *kaddr; -+ -+ if (fault_in_pages_readable(from, count) < 0) -+ return -EFAULT; -+ -+ kaddr = kmap_atomic(p); -+ pagefault_disable(); -+ remain = __copy_from_user_inatomic(kaddr + start, from, count); -+ pagefault_enable(); -+ kunmap_atomic(kaddr); -+ if (remain > 0) -+ return -EFAULT; -+ -+ cond_resched(); -+ return 0; -+} -+ -+/* copy data to the receiver's pool */ -+static size_t kdbus_pool_copy(const struct kdbus_pool_slice *slice, size_t off, -+ const void __user *data, struct file *f_src, -+ size_t off_src, size_t len) -+{ -+ struct file *f_dst = slice->pool->f; -+ struct address_space *mapping = f_dst->f_mapping; -+ const struct address_space_operations *aops = mapping->a_ops; -+ unsigned long fpos = slice->off + off; -+ unsigned long rem = len; -+ size_t pos = 0; -+ int ret = 0; -+ -+ BUG_ON(off + len > slice->size); -+ BUG_ON(slice->free); -+ -+ while (rem > 0) { -+ struct page *p; -+ unsigned long o; -+ unsigned long n; -+ void *fsdata; -+ int status; -+ -+ o = fpos & (PAGE_CACHE_SIZE - 1); -+ n = min_t(unsigned long, PAGE_CACHE_SIZE - o, rem); -+ -+ status = aops->write_begin(f_dst, mapping, fpos, n, 0, &p, -+ &fsdata); -+ if (status) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ if (data) -+ ret = kdbus_pool_copy_data(p, o, data + pos, n); -+ else -+ ret = kdbus_pool_copy_file(p, o, f_src, -+ off_src + pos, n); -+ mark_page_accessed(p); -+ -+ status = aops->write_end(f_dst, mapping, fpos, n, n, p, fsdata); -+ -+ if (ret < 0) -+ break; -+ if (status != n) { -+ ret = -EFAULT; -+ break; -+ } -+ -+ pos += n; -+ fpos += n; -+ rem -= n; -+ } -+ -+ return ret; -+} -+ -+/** -+ * kdbus_pool_slice_copy_user() - copy user memory to a slice -+ * @slice: The slice to write to -+ * @off: Offset in the slice to write to -+ * @data: User memory to copy from -+ * @len: Number of bytes to copy -+ * -+ * The offset was returned by the call to kdbus_pool_alloc_slice(). -+ * The user memory at @data will be copied to the @off in the allocated -+ * slice in the pool. -+ * -+ * Return: the numbers of bytes copied, negative errno on failure. -+ */ -+ssize_t -+kdbus_pool_slice_copy_user(const struct kdbus_pool_slice *slice, size_t off, -+ const void __user *data, size_t len) -+{ -+ return kdbus_pool_copy(slice, off, data, NULL, 0, len); -+} -+ -+/** -+ * kdbus_pool_slice_copy() - copy kernel memory to a slice -+ * @slice: The slice to write to -+ * @off: Offset in the slice to write to -+ * @data: Kernel memory to copy from -+ * @len: Number of bytes to copy -+ * -+ * The slice was returned by the call to kdbus_pool_alloc_slice(). -+ * The user memory at @data will be copied to the @off in the allocated -+ * slice in the pool. -+ * -+ * Return: the numbers of bytes copied, negative errno on failure. -+ */ -+ssize_t kdbus_pool_slice_copy(const struct kdbus_pool_slice *slice, size_t off, -+ const void *data, size_t len) -+{ -+ mm_segment_t old_fs; -+ ssize_t ret; -+ -+ old_fs = get_fs(); -+ set_fs(get_ds()); -+ ret = kdbus_pool_copy(slice, off, -+ (const void __user *)data, NULL, 0, len); -+ set_fs(old_fs); -+ -+ return ret; -+} -+ -+/** -+ * kdbus_pool_move_slice() - move memory from one pool into another one -+ * @dst_pool: The receiver's pool to copy to -+ * @src_pool: The receiver's pool to copy from -+ * @slice: Reference to the slice to copy from the source; -+ * updated with the newly allocated slice in the -+ * destination -+ * -+ * Move memory from one pool to another. Memory will be allocated in the -+ * destination pool, the memory copied over, and the free()d in source -+ * pool. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_pool_move_slice(struct kdbus_pool *dst_pool, -+ struct kdbus_pool *src_pool, -+ struct kdbus_pool_slice **slice) -+{ -+ mm_segment_t old_fs; -+ struct kdbus_pool_slice *slice_new; -+ int ret; -+ -+ ret = kdbus_pool_slice_alloc(dst_pool, &slice_new, (*slice)->size); -+ if (ret < 0) -+ return ret; -+ -+ old_fs = get_fs(); -+ set_fs(get_ds()); -+ ret = kdbus_pool_copy(slice_new, 0, NULL, -+ src_pool->f, (*slice)->off, (*slice)->size); -+ set_fs(old_fs); -+ if (ret < 0) -+ goto exit_free; -+ -+ kdbus_pool_slice_free(*slice); -+ -+ *slice = slice_new; -+ return 0; -+ -+exit_free: -+ kdbus_pool_slice_free(slice_new); -+ return ret; -+} -+ -+/** -+ * kdbus_pool_slice_flush() - flush dcache memory area of a slice -+ * @slice: The allocated slice to flush -+ * -+ * Dcache flushes are delayed to happen only right before the receiver -+ * gets the new buffer area announced. The mapped buffer is always -+ * read-only for the receiver, and only the area of the announced message -+ * needs to be flushed. -+ */ -+void kdbus_pool_slice_flush(const struct kdbus_pool_slice *slice) -+{ -+#if ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE == 1 -+ struct address_space *mapping = slice->pool->f->f_mapping; -+ pgoff_t first = slice->off >> PAGE_CACHE_SHIFT; -+ pgoff_t last = (slice->off + slice->size + -+ PAGE_CACHE_SIZE-1) >> PAGE_CACHE_SHIFT; -+ pgoff_t i; -+ -+ for (i = first; i < last; i++) { -+ struct page *page; -+ -+ page = find_get_page(mapping, i); -+ if (!page) -+ continue; -+ -+ flush_dcache_page(page); -+ put_page(page); -+ } -+#endif -+} -+ -+/** -+ * 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); -+} ---- /dev/null -+++ b/drivers/misc/kdbus/pool.h -@@ -0,0 +1,43 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_POOL_H -+#define __KDBUS_POOL_H -+ -+struct kdbus_pool; -+struct kdbus_pool_slice; -+ -+int kdbus_pool_new(const char *name, struct kdbus_pool **pool, size_t size); -+void kdbus_pool_free(struct kdbus_pool *pool); -+size_t kdbus_pool_remain(struct kdbus_pool *pool); -+int kdbus_pool_mmap(const struct kdbus_pool *pool, struct vm_area_struct *vma); -+int kdbus_pool_move_slice(struct kdbus_pool *dst_pool, -+ struct kdbus_pool *src_pool, -+ struct kdbus_pool_slice **slice); -+int kdbus_pool_release_offset(struct kdbus_pool *pool, size_t off); -+ -+int kdbus_pool_slice_alloc(struct kdbus_pool *pool, -+ struct kdbus_pool_slice **slice, size_t size); -+void kdbus_pool_slice_free(struct kdbus_pool_slice *slice); -+struct kdbus_pool_slice *kdbus_pool_slice_find(struct kdbus_pool *pool, -+ size_t off); -+size_t kdbus_pool_slice_offset(const struct kdbus_pool_slice *slice); -+ssize_t kdbus_pool_slice_copy(const struct kdbus_pool_slice *slice, size_t off, -+ const void *data, size_t len); -+ssize_t -+kdbus_pool_slice_copy_user(const struct kdbus_pool_slice *slice, size_t off, -+ const void __user *data, size_t len); -+void kdbus_pool_slice_flush(const struct kdbus_pool_slice *slice); -+ -+void kdbus_pool_slice_make_public(struct kdbus_pool_slice *slice); -+#endif diff --git a/0005-kdbus-add-connection-queue-handling-and-message-vali.patch b/0005-kdbus-add-connection-queue-handling-and-message-vali.patch deleted file mode 100644 index 5c1018245a8cf2..00000000000000 --- a/0005-kdbus-add-connection-queue-handling-and-message-vali.patch +++ /dev/null @@ -1,3470 +0,0 @@ -From 9b569a01686f188cc65a7424669953aa0b10bdf3 Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 18:57:24 +0200 -Subject: [PATCH 05/12] kdbus: add connection, queue handling and message - validation code - -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. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - drivers/misc/kdbus/connection.c | 1751 ++++++++++++++++++++++++++++++++++++++++ - drivers/misc/kdbus/connection.h | 177 ++++ - drivers/misc/kdbus/item.c | 256 +++++ - drivers/misc/kdbus/item.h | 40 - drivers/misc/kdbus/message.c | 420 +++++++++ - drivers/misc/kdbus/message.h | 72 + - drivers/misc/kdbus/queue.c | 602 +++++++++++++ - drivers/misc/kdbus/queue.h | 82 + - drivers/misc/kdbus/util.h | 2 - 9 files changed, 3401 insertions(+), 1 deletion(-) - create mode 100644 drivers/misc/kdbus/connection.c - create mode 100644 drivers/misc/kdbus/connection.h - create mode 100644 drivers/misc/kdbus/item.c - create mode 100644 drivers/misc/kdbus/item.h - create mode 100644 drivers/misc/kdbus/message.c - create mode 100644 drivers/misc/kdbus/message.h - create mode 100644 drivers/misc/kdbus/queue.c - create mode 100644 drivers/misc/kdbus/queue.h - ---- /dev/null -+++ b/drivers/misc/kdbus/connection.c -@@ -0,0 +1,1751 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 Linux Foundation -+ * Copyright (C) 2014 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/audit.h> -+#include <linux/device.h> -+#include <linux/file.h> -+#include <linux/fs.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/poll.h> -+#include <linux/sched.h> -+#include <linux/shmem_fs.h> -+#include <linux/sizes.h> -+#include <linux/slab.h> -+#include <linux/syscalls.h> -+ -+#include "bus.h" -+#include "connection.h" -+#include "endpoint.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 "util.h" -+#include "queue.h" -+ -+struct kdbus_conn_reply; -+ -+#define KDBUS_CONN_ACTIVE_BIAS (INT_MIN + 1) -+ -+/** -+ * struct kdbus_conn_reply - an entry of kdbus_conn's list of replies -+ * @kref: Ref-count of this object -+ * @entry: The entry of the connection's reply_list -+ * @reply_dst: The connection the reply will be sent to (method origin) -+ * @queue_entry: The queue enty item that is prepared by the replying -+ * connection -+ * @deadline_ns: The deadline of the reply, in nanoseconds -+ * @cookie: The cookie of the requesting message -+ * @name_id: ID of the well-known name the original msg was sent to -+ * @sync: The reply block is waiting for synchronous I/O -+ * @waiting: The condition to synchronously wait for -+ * @interrupted: The sync reply was left in an interrupted state -+ * @err: The error code for the synchronous reply -+ */ -+struct kdbus_conn_reply { -+ struct kref kref; -+ struct list_head entry; -+ struct kdbus_conn *reply_dst; -+ struct kdbus_queue_entry *queue_entry; -+ u64 deadline_ns; -+ u64 cookie; -+ u64 name_id; -+ bool sync:1; -+ bool waiting:1; -+ bool interrupted:1; -+ int err; -+}; -+ -+static int kdbus_conn_reply_new(struct kdbus_conn_reply **reply_wait, -+ struct kdbus_conn *reply_dst, -+ const struct kdbus_msg *msg, -+ struct kdbus_name_entry *name_entry) -+{ -+ bool sync = msg->flags & KDBUS_MSG_FLAGS_SYNC_REPLY; -+ struct kdbus_conn_reply *r; -+ int ret = 0; -+ -+ if (atomic_inc_return(&reply_dst->reply_count) > -+ KDBUS_CONN_MAX_REQUESTS_PENDING) { -+ ret = -EMLINK; -+ goto exit_dec_reply_count; -+ } -+ -+ r = kzalloc(sizeof(*r), GFP_KERNEL); -+ if (!r) { -+ ret = -ENOMEM; -+ goto exit_dec_reply_count; -+ } -+ -+ kref_init(&r->kref); -+ r->reply_dst = kdbus_conn_ref(reply_dst); -+ r->cookie = msg->cookie; -+ r->name_id = name_entry ? name_entry->name_id : 0; -+ r->deadline_ns = msg->timeout_ns; -+ -+ if (sync) { -+ r->sync = true; -+ r->waiting = true; -+ } -+ -+ *reply_wait = r; -+ -+exit_dec_reply_count: -+ if (ret < 0) -+ atomic_dec(&reply_dst->reply_count); -+ -+ return ret; -+} -+ -+static void __kdbus_conn_reply_free(struct kref *kref) -+{ -+ struct kdbus_conn_reply *reply = -+ container_of(kref, struct kdbus_conn_reply, kref); -+ -+ atomic_dec(&reply->reply_dst->reply_count); -+ kdbus_conn_unref(reply->reply_dst); -+ kfree(reply); -+} -+ -+static struct kdbus_conn_reply* -+kdbus_conn_reply_ref(struct kdbus_conn_reply *r) -+{ -+ if (r) -+ kref_get(&r->kref); -+ return r; -+} -+ -+static struct kdbus_conn_reply* -+kdbus_conn_reply_unref(struct kdbus_conn_reply *r) -+{ -+ if (r) -+ kref_put(&r->kref, __kdbus_conn_reply_free); -+ return NULL; -+} -+ -+static void kdbus_conn_reply_sync(struct kdbus_conn_reply *reply, int err) -+{ -+ BUG_ON(!reply->sync); -+ -+ list_del_init(&reply->entry); -+ reply->waiting = false; -+ reply->err = err; -+ wake_up_interruptible(&reply->reply_dst->wait); -+} -+ -+/* -+ * Check for maximum number of messages per individual user. This -+ * should prevent a single user from being able to fill the receiver's -+ * queue. -+ */ -+static int kdbus_conn_queue_user_quota(struct kdbus_conn *conn, -+ const struct kdbus_conn *conn_src, -+ struct kdbus_queue_entry *entry) -+{ -+ unsigned int user; -+ -+ if (!conn_src) -+ return 0; -+ -+ if (ns_capable(&init_user_ns, CAP_IPC_OWNER)) -+ return 0; -+ -+ /* -+ * Only after the queue grows above the maximum number of messages -+ * per individual user, we start to count all further messages -+ * from the sending users. -+ */ -+ if (conn->queue.msg_count < KDBUS_CONN_MAX_MSGS_PER_USER) -+ return 0; -+ -+ user = conn_src->user->idr; -+ -+ /* extend array to store the user message counters */ -+ if (user >= conn->msg_users_max) { -+ unsigned int *users; -+ unsigned int i; -+ -+ i = 8 + KDBUS_ALIGN8(user); -+ users = kcalloc(i, sizeof(unsigned int), GFP_KERNEL); -+ if (!users) -+ return -ENOMEM; -+ -+ memcpy(users, conn->msg_users, -+ sizeof(unsigned int) * conn->msg_users_max); -+ kfree(conn->msg_users); -+ conn->msg_users = users; -+ conn->msg_users_max = i; -+ } -+ -+ if (conn->msg_users[user] > KDBUS_CONN_MAX_MSGS_PER_USER) -+ return -ENOBUFS; -+ -+ conn->msg_users[user]++; -+ entry->user = user; -+ return 0; -+} -+ -+static void kdbus_conn_work(struct work_struct *work) -+{ -+ struct kdbus_conn *conn; -+ struct kdbus_conn_reply *reply, *reply_tmp; -+ u64 deadline = ~0ULL; -+ struct timespec64 ts; -+ u64 now; -+ -+ conn = container_of(work, struct kdbus_conn, work.work); -+ ktime_get_ts64(&ts); -+ now = timespec64_to_ns(&ts); -+ -+ mutex_lock(&conn->lock); -+ if (!kdbus_conn_active(conn)) { -+ mutex_unlock(&conn->lock); -+ return; -+ } -+ -+ list_for_each_entry_safe(reply, reply_tmp, &conn->reply_list, entry) { -+ /* -+ * If the reply block is waiting for synchronous I/O, -+ * the timeout is handled by wait_event_*_timeout(), -+ * so we don't have to care for it here. -+ */ -+ if (reply->sync && !reply->interrupted) -+ continue; -+ -+ if (reply->deadline_ns > now) { -+ /* remember next timeout */ -+ if (deadline > reply->deadline_ns) -+ deadline = reply->deadline_ns; -+ -+ continue; -+ } -+ -+ /* -+ * A zero deadline means the connection died, was -+ * cleaned up already and the notification was sent. -+ * Don't send notifications for reply trackers that were -+ * left in an interrupted syscall state. -+ */ -+ if (reply->deadline_ns != 0 && !reply->interrupted) -+ kdbus_notify_reply_timeout(conn->bus, -+ reply->reply_dst->id, -+ reply->cookie); -+ -+ list_del_init(&reply->entry); -+ kdbus_conn_reply_unref(reply); -+ } -+ -+ /* rearm delayed work with next timeout */ -+ if (deadline != ~0ULL) -+ schedule_delayed_work(&conn->work, -+ nsecs_to_jiffies(deadline - now)); -+ -+ mutex_unlock(&conn->lock); -+ -+ kdbus_notify_flush(conn->bus); -+} -+ -+/** -+ * kdbus_cmd_msg_recv() - receive a message from the queue -+ * @conn: Connection to work on -+ * @recv: The command as passed in by the ioctl -+ * -+ * Return: 0 on success, negative errno on failure -+ */ -+int kdbus_cmd_msg_recv(struct kdbus_conn *conn, -+ struct kdbus_cmd_recv *recv) -+{ -+ struct kdbus_queue_entry *entry = NULL; -+ int ret; -+ -+ if (recv->offset > 0) -+ return -EINVAL; -+ -+ mutex_lock(&conn->lock); -+ ret = kdbus_queue_entry_peek(&conn->queue, recv->priority, -+ recv->flags & KDBUS_RECV_USE_PRIORITY, -+ &entry); -+ if (ret < 0) -+ goto exit_unlock; -+ -+ BUG_ON(!entry); -+ -+ /* just drop the message */ -+ if (recv->flags & KDBUS_RECV_DROP) { -+ bool reply_found = false; -+ -+ if (entry->reply) { -+ struct kdbus_conn_reply *r; -+ -+ /* -+ * Walk the list of pending replies and see if the -+ * one attached to this entry item is stil there. -+ * It might have been removed by an incoming reply, -+ * and we currently don't track reply entries in that -+ * direction in order to prevent potentially dangling -+ * pointers. -+ */ -+ list_for_each_entry(r, &conn->reply_list, entry) { -+ if (r == entry->reply) { -+ reply_found = true; -+ break; -+ } -+ } -+ } -+ -+ if (reply_found) { -+ if (entry->reply->sync) { -+ kdbus_conn_reply_sync(entry->reply, -EPIPE); -+ } else { -+ list_del_init(&entry->reply->entry); -+ kdbus_conn_reply_unref(entry->reply); -+ kdbus_notify_reply_dead(conn->bus, -+ entry->src_id, -+ entry->cookie); -+ } -+ } -+ -+ kdbus_queue_entry_remove(conn, entry); -+ kdbus_pool_slice_free(entry->slice); -+ mutex_unlock(&conn->lock); -+ -+ kdbus_queue_entry_free(entry); -+ -+ goto exit; -+ } -+ -+ /* Give the offset back to the caller. */ -+ recv->offset = kdbus_pool_slice_offset(entry->slice); -+ -+ /* -+ * Just return the location of the next message. Do not install -+ * file descriptors or anything else. This is usually used to -+ * determine the sender of the next queued message. -+ * -+ * File descriptor numbers referenced in the message items -+ * are undefined, they are only valid with the full receive -+ * not with peek. -+ */ -+ if (recv->flags & KDBUS_RECV_PEEK) { -+ kdbus_pool_slice_flush(entry->slice); -+ goto exit_unlock; -+ } -+ -+ ret = kdbus_queue_entry_install(entry); -+ kdbus_pool_slice_make_public(entry->slice); -+ kdbus_queue_entry_remove(conn, entry); -+ kdbus_queue_entry_free(entry); -+ -+exit_unlock: -+ mutex_unlock(&conn->lock); -+exit: -+ kdbus_notify_flush(conn->bus); -+ return ret; -+} -+ -+static int kdbus_conn_find_reply(struct kdbus_conn *conn_replying, -+ struct kdbus_conn *conn_reply_dst, -+ uint64_t cookie, -+ struct kdbus_conn_reply **reply) -+{ -+ struct kdbus_conn_reply *r; -+ int ret = -ENOENT; -+ -+ if (atomic_read(&conn_reply_dst->reply_count) == 0) -+ return -ENOENT; -+ -+ list_for_each_entry(r, &conn_replying->reply_list, entry) { -+ if (r->reply_dst == conn_reply_dst && -+ r->cookie == cookie) { -+ *reply = r; -+ ret = 0; -+ break; -+ } -+ } -+ -+ return ret; -+} -+ -+/** -+ * kdbus_cmd_msg_cancel() - cancel all pending sync requests -+ * with the given cookie -+ * @conn: The connection -+ * @cookie: The cookie -+ * -+ * Return: 0 on success, or -ENOENT if no pending request with that -+ * cookie was found. -+ */ -+int kdbus_cmd_msg_cancel(struct kdbus_conn *conn, -+ u64 cookie) -+{ -+ struct kdbus_conn_reply *reply; -+ struct kdbus_conn *c; -+ bool found = false; -+ int ret, i; -+ -+ if (atomic_read(&conn->reply_count) == 0) -+ return -ENOENT; -+ -+ /* lock order: domain -> bus -> ep -> names -> conn */ -+ down_read(&conn->bus->conn_rwlock); -+ hash_for_each(conn->bus->conn_hash, i, c, hentry) { -+ if (c == conn) -+ continue; -+ -+ mutex_lock(&c->lock); -+ ret = kdbus_conn_find_reply(c, conn, cookie, &reply); -+ if (ret == 0) { -+ kdbus_conn_reply_sync(reply, -ECANCELED); -+ found = true; -+ } -+ mutex_unlock(&c->lock); -+ } -+ up_read(&conn->bus->conn_rwlock); -+ -+ return found ? 0 : -ENOENT; -+} -+ -+static int kdbus_conn_check_access(struct kdbus_ep *ep, -+ const struct kdbus_msg *msg, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst, -+ struct kdbus_conn_reply **reply_wake) -+{ -+ bool allowed = false; -+ int ret; -+ -+ /* -+ * Walk the conn_src's list of expected replies. If there's any -+ * matching entry, allow the message to be sent, and remove it. -+ */ -+ if (reply_wake && msg->cookie_reply > 0) { -+ struct kdbus_conn_reply *r; -+ -+ mutex_lock(&conn_src->lock); -+ ret = kdbus_conn_find_reply(conn_src, conn_dst, -+ msg->cookie_reply, &r); -+ if (ret == 0) { -+ list_del_init(&r->entry); -+ if (r->sync) -+ *reply_wake = kdbus_conn_reply_ref(r); -+ else -+ kdbus_conn_reply_unref(r); -+ -+ allowed = true; -+ } -+ mutex_unlock(&conn_src->lock); -+ } -+ -+ if (allowed) -+ return 0; -+ -+ /* ... otherwise, ask the policy DBs for permission */ -+ ret = kdbus_ep_policy_check_talk_access(ep, conn_src, conn_dst); -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} -+ -+/* enqueue a message into the receiver's pool */ -+static int kdbus_conn_entry_insert(struct kdbus_conn *conn, -+ struct kdbus_conn *conn_src, -+ const struct kdbus_kmsg *kmsg, -+ struct kdbus_conn_reply *reply) -+{ -+ struct kdbus_queue_entry *entry; -+ int ret; -+ -+ mutex_lock(&conn->lock); -+ -+ /* limit the maximum number of queued messages */ -+ if (!ns_capable(&init_user_ns, CAP_IPC_OWNER) && -+ conn->queue.msg_count > KDBUS_CONN_MAX_MSGS) { -+ ret = -ENOBUFS; -+ goto exit_unlock; -+ } -+ -+ if (!kdbus_conn_active(conn)) { -+ ret = -ECONNRESET; -+ goto exit_unlock; -+ } -+ -+ /* The connection does not accept file descriptors */ -+ if (!(conn->flags & KDBUS_HELLO_ACCEPT_FD) && kmsg->fds_count > 0) { -+ ret = -ECOMM; -+ goto exit_unlock; -+ } -+ -+ ret = kdbus_queue_entry_alloc(conn, kmsg, &entry); -+ if (ret < 0) -+ goto exit_unlock; -+ -+ /* limit the number of queued messages from the same individual user */ -+ ret = kdbus_conn_queue_user_quota(conn, conn_src, entry); -+ if (ret < 0) -+ goto exit_queue_free; -+ -+ /* -+ * Remember the the reply associated with this queue entry, so we can -+ * move the reply entry's connection when a connection moves from an -+ * activator to an implementor. -+ */ -+ entry->reply = reply; -+ -+ if (reply) { -+ list_add(&reply->entry, &conn->reply_list); -+ if (!reply->sync) -+ schedule_delayed_work(&conn->work, 0); -+ } -+ -+ /* link the message into the receiver's entry */ -+ kdbus_queue_entry_add(&conn->queue, entry); -+ mutex_unlock(&conn->lock); -+ -+ /* wake up poll() */ -+ wake_up_interruptible(&conn->wait); -+ return 0; -+ -+exit_queue_free: -+ kdbus_queue_entry_free(entry); -+exit_unlock: -+ mutex_unlock(&conn->lock); -+ return ret; -+} -+ -+static int kdbus_kmsg_attach_metadata(struct kdbus_kmsg *kmsg, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst) -+{ -+ u64 attach_flags; -+ -+ /* -+ * Append metadata items according to the destination connection's -+ * attach flags. If the source connection has faked credentials, the -+ * metadata object associated with the kmsg has been pre-filled with -+ * conn_src->owner_meta, and we only attach the connection's name and -+ * currently owned names on top of that. -+ */ -+ attach_flags = atomic64_read(&conn_dst->attach_flags); -+ -+ if (conn_src->owner_meta) -+ attach_flags &= KDBUS_ATTACH_NAMES | KDBUS_ATTACH_CONN_NAME; -+ -+ return kdbus_meta_append(kmsg->meta, conn_src, kmsg->seq, attach_flags); -+} -+ -+static void kdbus_conn_broadcast(struct kdbus_ep *ep, -+ struct kdbus_conn *conn_src, -+ struct kdbus_kmsg *kmsg) -+{ -+ const struct kdbus_msg *msg = &kmsg->msg; -+ struct kdbus_bus *bus = ep->bus; -+ struct kdbus_conn *conn_dst; -+ unsigned int i; -+ int ret = 0; -+ -+ down_read(&bus->conn_rwlock); -+ -+ hash_for_each(bus->conn_hash, i, conn_dst, hentry) { -+ if (conn_dst->id == msg->src_id) -+ continue; -+ -+ /* -+ * Activator or policy holder connections will -+ * not receive any broadcast messages, only -+ * ordinary and monitor ones. -+ */ -+ if (!kdbus_conn_is_connected(conn_dst) && -+ !kdbus_conn_is_monitor(conn_dst)) -+ continue; -+ -+ if (!kdbus_match_db_match_kmsg(conn_dst->match_db, conn_src, -+ kmsg)) -+ continue; -+ -+ ret = kdbus_ep_policy_check_notification(conn_dst->ep, -+ conn_dst, kmsg); -+ if (ret < 0) -+ continue; -+ -+ /* -+ * The first receiver which requests additional -+ * metadata causes the message to carry it; all -+ * receivers after that will see all of the added -+ * data, even when they did not ask for it. -+ */ -+ if (conn_src) { -+ /* Check if conn_src is allowed to signal */ -+ ret = kdbus_ep_policy_check_broadcast(conn_dst->ep, -+ conn_src, -+ conn_dst); -+ if (ret < 0) -+ continue; -+ -+ ret = kdbus_ep_policy_check_src_names(conn_dst->ep, -+ conn_src, -+ conn_dst); -+ if (ret < 0) -+ continue; -+ -+ ret = kdbus_kmsg_attach_metadata(kmsg, conn_src, -+ conn_dst); -+ if (ret < 0) -+ goto exit_unlock; -+ } -+ -+ kdbus_conn_entry_insert(conn_dst, conn_src, kmsg, NULL); -+ } -+ -+exit_unlock: -+ up_read(&bus->conn_rwlock); -+} -+ -+static void kdbus_conn_eavesdrop(struct kdbus_ep *ep, struct kdbus_conn *conn, -+ struct kdbus_kmsg *kmsg) -+{ -+ struct kdbus_conn *c; -+ int ret; -+ -+ /* -+ * Monitor connections get all messages; ignore possible errors -+ * when sending messages to monitor connections. -+ */ -+ -+ down_read(&ep->bus->conn_rwlock); -+ list_for_each_entry(c, &ep->bus->monitors_list, monitor_entry) { -+ /* -+ * The first monitor which requests additional -+ * metadata causes the message to carry it; all -+ * monitors after that will see all of the added -+ * data, even when they did not ask for it. -+ */ -+ if (conn) { -+ ret = kdbus_kmsg_attach_metadata(kmsg, conn, c); -+ if (ret < 0) -+ break; -+ } -+ -+ kdbus_conn_entry_insert(c, NULL, kmsg, NULL); -+ } -+ up_read(&ep->bus->conn_rwlock); -+} -+ -+static int kdbus_conn_wait_reply(struct kdbus_ep *ep, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst, -+ struct kdbus_msg *msg, -+ struct kdbus_conn_reply *reply_wait, -+ u64 timeout_ns) -+{ -+ struct kdbus_queue_entry *entry; -+ int r, ret; -+ -+ /* -+ * 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. -+ */ -+ r = wait_event_interruptible_timeout(reply_wait->reply_dst->wait, -+ !reply_wait->waiting || !kdbus_conn_active(conn_src), -+ nsecs_to_jiffies(timeout_ns)); -+ if (r < 0) { -+ /* -+ * 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_dst->lock); -+ reply_wait->interrupted = true; -+ schedule_delayed_work(&conn_dst->work, 0); -+ mutex_unlock(&conn_dst->lock); -+ -+ return r; -+ } -+ -+ if (r == 0) -+ ret = -ETIMEDOUT; -+ else if (!kdbus_conn_active(conn_src)) -+ ret = -ECONNRESET; -+ else -+ ret = reply_wait->err; -+ -+ mutex_lock(&conn_dst->lock); -+ list_del_init(&reply_wait->entry); -+ mutex_unlock(&conn_dst->lock); -+ -+ mutex_lock(&conn_src->lock); -+ reply_wait->waiting = false; -+ entry = reply_wait->queue_entry; -+ if (entry) { -+ if (ret == 0) -+ ret = kdbus_queue_entry_install(entry); -+ -+ msg->offset_reply = kdbus_pool_slice_offset(entry->slice); -+ kdbus_pool_slice_make_public(entry->slice); -+ kdbus_queue_entry_free(entry); -+ } -+ mutex_unlock(&conn_src->lock); -+ -+ kdbus_conn_reply_unref(reply_wait); -+ -+ return ret; -+} -+ -+/** -+ * kdbus_conn_kmsg_send() - send a message -+ * @ep: Endpoint to send from -+ * @conn_src: Connection, kernel-generated messages do not have one -+ * @kmsg: Message to send -+ * -+ * Return: 0 on success, negative errno on failure -+ */ -+int kdbus_conn_kmsg_send(struct kdbus_ep *ep, -+ struct kdbus_conn *conn_src, -+ struct kdbus_kmsg *kmsg) -+{ -+ struct kdbus_conn_reply *reply_wait = NULL; -+ struct kdbus_conn_reply *reply_wake = NULL; -+ struct kdbus_name_entry *name_entry = NULL; -+ struct kdbus_msg *msg = &kmsg->msg; -+ struct kdbus_conn *conn_dst = NULL; -+ struct kdbus_bus *bus = ep->bus; -+ bool sync = msg->flags & KDBUS_MSG_FLAGS_SYNC_REPLY; -+ int ret = 0; -+ -+ /* assign domain-global message sequence number */ -+ BUG_ON(kmsg->seq > 0); -+ kmsg->seq = atomic64_inc_return(&bus->domain->msg_seq_last); -+ -+ /* non-kernel senders append credentials/metadata */ -+ if (conn_src) { -+ /* -+ * If a connection has installed faked credentials when it was -+ * created, make sure only those are sent out as attachments -+ * of messages, and nothing that is gathered at retrieved from -+ * 'current' at the time of sending. -+ * -+ * Hence, in such cases, duplicate the connection's owner_meta, -+ * and take care not to augment it by attaching any new items. -+ */ -+ if (conn_src->owner_meta) -+ ret = kdbus_meta_dup(conn_src->owner_meta, &kmsg->meta); -+ else -+ ret = kdbus_meta_new(&kmsg->meta); -+ -+ if (ret < 0) -+ return ret; -+ } -+ -+ if (msg->dst_id == KDBUS_DST_ID_BROADCAST) { -+ kdbus_conn_broadcast(ep, conn_src, kmsg); -+ return 0; -+ } -+ -+ if (kmsg->dst_name) { -+ name_entry = kdbus_name_lock(bus->name_registry, -+ kmsg->dst_name); -+ if (!name_entry) -+ 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_entry->conn->id) { -+ ret = -EREMCHG; -+ goto exit_name_unlock; -+ } -+ -+ if (!name_entry->conn && name_entry->activator) -+ conn_dst = kdbus_conn_ref(name_entry->activator); -+ else -+ conn_dst = kdbus_conn_ref(name_entry->conn); -+ -+ if ((msg->flags & KDBUS_MSG_FLAGS_NO_AUTO_START) && -+ kdbus_conn_is_activator(conn_dst)) { -+ ret = -EADDRNOTAVAIL; -+ goto exit_unref; -+ } -+ } else { -+ /* unicast message to unique name */ -+ conn_dst = kdbus_bus_find_conn_by_id(bus, msg->dst_id); -+ if (!conn_dst) -+ return -ENXIO; -+ -+ /* -+ * Special-purpose connections are not allowed to be addressed -+ * via their unique IDs. -+ */ -+ if (!kdbus_conn_is_connected(conn_dst)) { -+ ret = -ENXIO; -+ goto exit_unref; -+ } -+ } -+ -+ /* -+ * 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 -+ * activator connections of the same name. -+ */ -+ if (name_entry) -+ kmsg->dst_name_id = name_entry->name_id; -+ -+ if (conn_src) { -+ /* -+ * If we got here due to an interrupted system call, our reply -+ * wait object is still queued on conn_dst, with the former -+ * cookie. Look it up, and in case it exists, go dormant right -+ * away again, and don't queue the message again. -+ */ -+ if (sync) { -+ mutex_lock(&conn_dst->lock); -+ ret = kdbus_conn_find_reply(conn_dst, conn_src, -+ kmsg->msg.cookie, -+ &reply_wait); -+ if (ret == 0) { -+ if (reply_wait->interrupted) -+ reply_wait->interrupted = false; -+ else -+ reply_wait = NULL; -+ } -+ mutex_unlock(&conn_dst->lock); -+ -+ if (reply_wait) -+ goto wait_sync; -+ } -+ -+ ret = kdbus_kmsg_attach_metadata(kmsg, conn_src, conn_dst); -+ if (ret < 0) -+ goto exit_unref; -+ -+ if (msg->flags & KDBUS_MSG_FLAGS_EXPECT_REPLY) { -+ ret = kdbus_conn_check_access(ep, msg, conn_src, -+ conn_dst, NULL); -+ if (ret < 0) -+ goto exit_unref; -+ -+ ret = kdbus_conn_reply_new(&reply_wait, conn_src, msg, -+ name_entry); -+ if (ret < 0) -+ goto exit_unref; -+ } else { -+ ret = kdbus_conn_check_access(ep, msg, conn_src, -+ conn_dst, &reply_wake); -+ if (ret < 0) -+ goto exit_unref; -+ } -+ } -+ -+ if (reply_wake) { -+ /* -+ * If we're synchronously responding to a message, allocate a -+ * queue item and attach it to the reply tracking object. -+ * The connection's queue will never get to see it. -+ */ -+ mutex_lock(&conn_dst->lock); -+ if (reply_wake->waiting && kdbus_conn_active(conn_dst)) -+ ret = kdbus_queue_entry_alloc(conn_dst, kmsg, -+ &reply_wake->queue_entry); -+ else -+ ret = -ECONNRESET; -+ -+ kdbus_conn_reply_sync(reply_wake, ret); -+ kdbus_conn_reply_unref(reply_wake); -+ mutex_unlock(&conn_dst->lock); -+ -+ if (ret < 0) -+ goto exit_unref; -+ } else { -+ /* -+ * Otherwise, put it in the queue and wait for the connection -+ * to dequeue and receive the message. -+ */ -+ ret = kdbus_conn_entry_insert(conn_dst, conn_src, -+ kmsg, reply_wait); -+ if (ret < 0) { -+ if (reply_wait) -+ kdbus_conn_reply_unref(reply_wait); -+ goto exit_unref; -+ } -+ } -+ -+ /* forward to monitors */ -+ kdbus_conn_eavesdrop(ep, conn_src, kmsg); -+ -+wait_sync: -+ /* no reason to keep names locked for replies */ -+ name_entry = kdbus_name_unlock(bus->name_registry, name_entry); -+ -+ if (sync) { -+ struct timespec64 ts; -+ u64 now, timeout; -+ -+ BUG_ON(!reply_wait); -+ -+ ktime_get_ts64(&ts); -+ now = timespec64_to_ns(&ts); -+ -+ if (unlikely(msg->timeout_ns <= now)) -+ timeout = 0; -+ else -+ timeout = msg->timeout_ns - now; -+ -+ ret = kdbus_conn_wait_reply(ep, conn_src, conn_dst, msg, -+ reply_wait, timeout); -+ } -+ -+exit_unref: -+ kdbus_conn_unref(conn_dst); -+exit_name_unlock: -+ kdbus_name_unlock(bus->name_registry, name_entry); -+ -+ 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_conn_reply *reply, *reply_tmp; -+ struct kdbus_queue_entry *entry, *tmp; -+ LIST_HEAD(reply_list); -+ -+ mutex_lock(&conn->lock); -+ if (!kdbus_conn_active(conn)) { -+ mutex_unlock(&conn->lock); -+ return -EALREADY; -+ } -+ -+ if (ensure_queue_empty && !list_empty(&conn->queue.msg_list)) { -+ 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); -+ -+ /* lock order: domain -> bus -> ep -> names -> conn */ -+ mutex_lock(&conn->ep->lock); -+ down_write(&conn->bus->conn_rwlock); -+ -+ /* remove from bus and endpoint */ -+ hash_del(&conn->hentry); -+ list_del(&conn->monitor_entry); -+ list_del(&conn->ep_entry); -+ -+ up_write(&conn->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_remove_by_conn(conn->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(conn->bus, entry->src_id, -+ entry->cookie); -+ -+ kdbus_queue_entry_remove(conn, entry); -+ kdbus_pool_slice_free(entry->slice); -+ kdbus_queue_entry_free(entry); -+ } -+ list_splice_init(&conn->reply_list, &reply_list); -+ mutex_unlock(&conn->lock); -+ -+ list_for_each_entry_safe(reply, reply_tmp, &reply_list, entry) { -+ if (reply->sync) { -+ kdbus_conn_reply_sync(reply, -EPIPE); -+ continue; -+ } -+ -+ /* send a 'connection dead' notification */ -+ kdbus_notify_reply_dead(conn->bus, reply->reply_dst->id, -+ reply->cookie); -+ -+ list_del(&reply->entry); -+ kdbus_conn_reply_unref(reply); -+ } -+ -+ kdbus_notify_id_change(conn->bus, KDBUS_ITEM_ID_REMOVE, -+ conn->id, conn->flags); -+ -+ kdbus_notify_flush(conn->bus); -+ -+ return 0; -+} -+ -+/** -+ * 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_flush_policy() - flush all cached policy entries that -+ * refer to a connecion -+ * @conn: Connection to check -+ */ -+void kdbus_conn_purge_policy_cache(struct kdbus_conn *conn) -+{ -+ kdbus_policy_purge_cache(&conn->ep->policy_db, conn); -+ kdbus_policy_purge_cache(&conn->bus->policy_db, conn); -+} -+ -+static void __kdbus_conn_free(struct kref *kref) -+{ -+ struct kdbus_conn *conn = container_of(kref, struct kdbus_conn, kref); -+ -+ BUG_ON(kdbus_conn_active(conn)); -+ BUG_ON(delayed_work_pending(&conn->work)); -+ BUG_ON(!list_empty(&conn->queue.msg_list)); -+ BUG_ON(!list_empty(&conn->names_list)); -+ BUG_ON(!list_empty(&conn->names_queue_list)); -+ BUG_ON(!list_empty(&conn->reply_list)); -+ -+ atomic_dec(&conn->user->connections); -+ kdbus_domain_user_unref(conn->user); -+ -+ kdbus_conn_purge_policy_cache(conn); -+ kdbus_policy_remove_owner(&conn->bus->policy_db, conn); -+ -+ kdbus_meta_free(conn->owner_meta); -+ kdbus_match_db_free(conn->match_db); -+ kdbus_pool_free(conn->pool); -+ kdbus_ep_unref(conn->ep); -+ kdbus_bus_unref(conn->bus); -+ put_cred(conn->cred); -+ kfree(conn->name); -+ kfree(conn); -+} -+ -+/** -+ * kdbus_conn_ref() - take a connection reference -+ * @conn: Connection -+ * -+ * Return: the connection itself -+ */ -+struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *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) -+ return NULL; -+ -+ kref_put(&conn->kref, __kdbus_conn_free); -+ return NULL; -+} -+ -+/** -+ * 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; -+ -+#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); -+} -+ -+/** -+ * 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 implementor connection is taking over/giving back a well-known name -+ * from/to an activator connection. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_conn_move_messages(struct kdbus_conn *conn_dst, -+ struct kdbus_conn *conn_src, -+ u64 name_id) -+{ -+ struct kdbus_queue_entry *q, *q_tmp; -+ struct kdbus_conn_reply *r, *r_tmp; -+ LIST_HEAD(reply_list); -+ LIST_HEAD(msg_list); -+ int ret = 0; -+ -+ BUG_ON(!mutex_is_locked(&conn_dst->bus->lock)); -+ BUG_ON(conn_src == conn_dst); -+ -+ /* remove all messages from the source */ -+ mutex_lock(&conn_src->lock); -+ list_for_each_entry_safe(r, r_tmp, &conn_src->reply_list, entry) { -+ /* filter messages for a specific name */ -+ if (name_id > 0 && r->name_id != name_id) -+ continue; -+ -+ list_move_tail(&r->entry, &reply_list); -+ } -+ list_for_each_entry_safe(q, q_tmp, &conn_src->queue.msg_list, entry) { -+ /* filter messages for a specific name */ -+ if (name_id > 0 && q->dst_name_id != name_id) -+ continue; -+ -+ kdbus_queue_entry_remove(conn_src, q); -+ list_add_tail(&q->entry, &msg_list); -+ } -+ mutex_unlock(&conn_src->lock); -+ -+ /* insert messages into destination */ -+ mutex_lock(&conn_dst->lock); -+ if (!kdbus_conn_active(conn_dst)) { -+ struct kdbus_conn_reply *r, *r_tmp; -+ -+ /* our destination connection died, just drop all messages */ -+ mutex_unlock(&conn_dst->lock); -+ list_for_each_entry_safe(q, q_tmp, &msg_list, entry) -+ kdbus_queue_entry_free(q); -+ list_for_each_entry_safe(r, r_tmp, &reply_list, entry) -+ kdbus_conn_reply_unref(r); -+ return -ECONNRESET; -+ } -+ -+ list_for_each_entry_safe(q, q_tmp, &msg_list, entry) { -+ ret = kdbus_pool_move_slice(conn_dst->pool, conn_src->pool, -+ &q->slice); -+ if (ret < 0) -+ kdbus_queue_entry_free(q); -+ else -+ kdbus_queue_entry_add(&conn_dst->queue, q); -+ } -+ list_splice(&reply_list, &conn_dst->reply_list); -+ mutex_unlock(&conn_dst->lock); -+ -+ /* wake up poll() */ -+ wake_up_interruptible(&conn_dst->wait); -+ -+ return ret; -+} -+ -+/** -+ * kdbus_cmd_info() - retrieve info about a connection -+ * @conn: Connection -+ * @cmd_info: The command as passed in by the ioctl -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_cmd_info(struct kdbus_conn *conn, -+ struct kdbus_cmd_info *cmd_info) -+{ -+ struct kdbus_name_entry *entry = NULL; -+ struct kdbus_conn *owner_conn = NULL; -+ struct kdbus_info info = {}; -+ struct kdbus_meta *meta = NULL; -+ struct kdbus_pool_slice *slice; -+ size_t pos; -+ int ret = 0; -+ u64 flags; -+ -+ if (cmd_info->id == 0) { -+ const char *name; -+ -+ ret = kdbus_items_get_str(cmd_info->items, -+ KDBUS_ITEMS_SIZE(cmd_info, items), -+ KDBUS_ITEM_NAME, &name); -+ if (ret < 0) -+ return -EINVAL; -+ -+ if (!kdbus_name_is_valid(name, false)) -+ return -EINVAL; -+ -+ /* check if 'conn' is allowed to see 'name' */ -+ ret = kdbus_ep_policy_check_see_access(conn->ep, conn, name); -+ if (ret < 0) -+ return ret; -+ -+ entry = kdbus_name_lock(conn->bus->name_registry, name); -+ if (!entry) -+ return -ESRCH; -+ else if (entry->conn) -+ owner_conn = kdbus_conn_ref(entry->conn); -+ } else { -+ owner_conn = kdbus_bus_find_conn_by_id(conn->bus, cmd_info->id); -+ if (!owner_conn) { -+ ret = -ENXIO; -+ goto exit; -+ } -+ -+ /* check if 'conn' is allowed to see any of owner_conn's names*/ -+ ret = kdbus_ep_policy_check_src_names(conn->ep, owner_conn, -+ conn); -+ if (ret < 0) -+ return ret; -+ } -+ -+ info.size = sizeof(info); -+ info.id = owner_conn->id; -+ info.flags = owner_conn->flags; -+ -+ /* do not leak domain-specific credentials */ -+ if (kdbus_meta_ns_eq(conn->meta, owner_conn->meta)) -+ info.size += owner_conn->meta->size; -+ -+ /* -+ * Unlike the rest of the values which are cached at connection -+ * creation time, some values need to be appended here because -+ * at creation time a connection does not have names and other -+ * properties. -+ */ -+ flags = cmd_info->flags & (KDBUS_ATTACH_NAMES | KDBUS_ATTACH_CONN_NAME); -+ if (flags) { -+ ret = kdbus_meta_new(&meta); -+ if (ret < 0) -+ goto exit; -+ -+ ret = kdbus_meta_append(meta, owner_conn, 0, flags); -+ if (ret < 0) -+ goto exit; -+ -+ info.size += meta->size; -+ } -+ -+ ret = kdbus_pool_slice_alloc(conn->pool, &slice, info.size); -+ if (ret < 0) -+ goto exit; -+ -+ ret = kdbus_pool_slice_copy(slice, 0, &info, sizeof(info)); -+ if (ret < 0) -+ goto exit_free; -+ pos = sizeof(info); -+ -+ if (kdbus_meta_ns_eq(conn->meta, owner_conn->meta)) { -+ ret = kdbus_pool_slice_copy(slice, pos, owner_conn->meta->data, -+ owner_conn->meta->size); -+ if (ret < 0) -+ goto exit_free; -+ -+ pos += owner_conn->meta->size; -+ } -+ -+ if (meta) { -+ ret = kdbus_pool_slice_copy(slice, pos, meta->data, meta->size); -+ if (ret < 0) -+ goto exit_free; -+ } -+ -+ /* write back the offset */ -+ cmd_info->offset = kdbus_pool_slice_offset(slice); -+ kdbus_pool_slice_flush(slice); -+ kdbus_pool_slice_make_public(slice); -+ -+exit_free: -+ if (ret < 0) -+ kdbus_pool_slice_free(slice); -+ -+exit: -+ kdbus_meta_free(meta); -+ kdbus_conn_unref(owner_conn); -+ kdbus_name_unlock(conn->bus->name_registry, entry); -+ -+ return ret; -+} -+ -+/** -+ * kdbus_cmd_conn_update() - update the attach-flags of a connection or -+ * the policy entries of a policy holding one -+ * @conn: Connection -+ * @cmd: The command as passed in by the ioctl -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_cmd_conn_update(struct kdbus_conn *conn, -+ const struct kdbus_cmd_update *cmd) -+{ -+ const struct kdbus_item *item; -+ bool policy_provided = false; -+ bool flags_provided = false; -+ u64 attach_flags; -+ int ret; -+ -+ KDBUS_ITEMS_FOREACH(item, cmd->items, KDBUS_ITEMS_SIZE(cmd, items)) { -+ switch (item->type) { -+ case KDBUS_ITEM_ATTACH_FLAGS: -+ /* -+ * Only ordinary or monitor connections -+ * may update their attach-flags. -+ */ -+ if (!kdbus_conn_is_connected(conn) && -+ !kdbus_conn_is_monitor(conn)) -+ return -EOPNOTSUPP; -+ -+ flags_provided = true; -+ attach_flags = item->data64[0]; -+ break; -+ -+ case KDBUS_ITEM_NAME: -+ case KDBUS_ITEM_POLICY_ACCESS: -+ /* -+ * Only policy holders may update their policy entries. -+ */ -+ if (!kdbus_conn_is_policy_holder(conn)) -+ return -EOPNOTSUPP; -+ -+ policy_provided = true; -+ break; -+ } -+ } -+ -+ if (policy_provided) { -+ ret = kdbus_policy_set(&conn->bus->policy_db, cmd->items, -+ KDBUS_ITEMS_SIZE(cmd, items), -+ 1, true, conn); -+ if (ret < 0) -+ return ret; -+ } -+ -+ if (flags_provided) -+ atomic64_set(&conn->attach_flags, attach_flags); -+ -+ return 0; -+} -+ -+/** -+ * kdbus_conn_new() - create a new connection -+ * @ep: The endpoint the connection is connected to -+ * @hello: The kdbus_cmd_hello as passed in by the user -+ * @meta: The metadata gathered at open() time of the handle -+ * @c: Returned connection -+ * -+ * Return: 0 on success, negative errno on failure -+ */ -+int kdbus_conn_new(struct kdbus_ep *ep, -+ struct kdbus_cmd_hello *hello, -+ struct kdbus_meta *meta, -+ struct kdbus_conn **c) -+{ -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ static struct lock_class_key __key; -+#endif -+ const struct kdbus_creds *creds = NULL; -+ const struct kdbus_item *item; -+ const char *conn_name = NULL; -+ const char *seclabel = NULL; -+ const char *name = NULL; -+ struct kdbus_conn *conn; -+ struct kdbus_bus *bus = ep->bus; -+ size_t seclabel_len = 0; -+ bool is_policy_holder; -+ bool is_activator; -+ bool is_monitor; -+ int ret; -+ -+ BUG_ON(*c); -+ -+ is_monitor = hello->flags & KDBUS_HELLO_MONITOR; -+ is_activator = hello->flags & KDBUS_HELLO_ACTIVATOR; -+ is_policy_holder = hello->flags & KDBUS_HELLO_POLICY_HOLDER; -+ -+ /* can't be activator or policy holder and monitor at the same time */ -+ if (is_monitor && (is_activator || is_policy_holder)) -+ return -EINVAL; -+ -+ /* can't be policy holder and activator at the same time */ -+ if (is_activator && is_policy_holder) -+ return -EINVAL; -+ -+ /* only privileged connections can activate and monitor */ -+ if (!kdbus_bus_uid_is_privileged(bus) && -+ (is_activator || is_policy_holder || is_monitor)) -+ return -EPERM; -+ -+ KDBUS_ITEMS_FOREACH(item, hello->items, -+ KDBUS_ITEMS_SIZE(hello, items)) { -+ switch (item->type) { -+ case KDBUS_ITEM_NAME: -+ if (!is_activator && !is_policy_holder) -+ return -EINVAL; -+ -+ if (name) -+ return -EINVAL; -+ -+ if (!kdbus_name_is_valid(item->str, true)) -+ return -EINVAL; -+ -+ name = item->str; -+ break; -+ -+ case KDBUS_ITEM_CREDS: -+ /* privileged processes can impersonate somebody else */ -+ if (!kdbus_bus_uid_is_privileged(bus)) -+ return -EPERM; -+ -+ if (item->size != KDBUS_ITEM_SIZE(sizeof(*creds))) -+ return -EINVAL; -+ -+ creds = &item->creds; -+ break; -+ -+ case KDBUS_ITEM_SECLABEL: -+ /* privileged processes can impersonate somebody else */ -+ if (!kdbus_bus_uid_is_privileged(bus)) -+ return -EPERM; -+ -+ seclabel = item->str; -+ seclabel_len = item->size - KDBUS_ITEM_HEADER_SIZE; -+ break; -+ -+ case KDBUS_ITEM_CONN_NAME: -+ /* human-readable connection name (debugging) */ -+ if (conn_name) -+ return -EINVAL; -+ -+ conn_name = item->str; -+ break; -+ } -+ } -+ -+ if ((is_activator || is_policy_holder) && !name) -+ return -EINVAL; -+ -+ conn = kzalloc(sizeof(*conn), GFP_KERNEL); -+ if (!conn) -+ return -ENOMEM; -+ -+ if (is_activator || is_policy_holder) { -+ /* -+ * Policy holders may install one name, and are -+ * allowed to use wildcards. -+ */ -+ ret = kdbus_policy_set(&bus->policy_db, hello->items, -+ KDBUS_ITEMS_SIZE(hello, items), -+ 1, is_policy_holder, conn); -+ if (ret < 0) -+ goto exit_free_conn; -+ } -+ -+ if (conn_name) { -+ conn->name = kstrdup(conn_name, GFP_KERNEL); -+ if (!conn->name) { -+ ret = -ENOMEM; -+ goto exit_free_conn; -+ } -+ } -+ -+ kref_init(&conn->kref); -+ atomic_set(&conn->active, 0); -+#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->reply_count, 0); -+ INIT_DELAYED_WORK(&conn->work, kdbus_conn_work); -+ conn->cred = get_current_cred(); -+ init_waitqueue_head(&conn->wait); -+ kdbus_queue_init(&conn->queue); -+ -+ /* init entry, so we can unconditionally remove it */ -+ INIT_LIST_HEAD(&conn->monitor_entry); -+ -+ ret = kdbus_pool_new(conn->name, &conn->pool, hello->pool_size); -+ if (ret < 0) -+ goto exit_unref_cred; -+ -+ ret = kdbus_match_db_new(&conn->match_db); -+ if (ret < 0) -+ goto exit_free_pool; -+ -+ conn->bus = kdbus_bus_ref(ep->bus); -+ conn->ep = kdbus_ep_ref(ep); -+ -+ /* get new id for this connection */ -+ conn->id = atomic64_inc_return(&bus->conn_seq_last); -+ -+ /* return properties of this connection to the caller */ -+ hello->bus_flags = bus->bus_flags; -+ hello->bloom = bus->bloom; -+ hello->id = conn->id; -+ -+ BUILD_BUG_ON(sizeof(bus->id128) != sizeof(hello->id128)); -+ memcpy(hello->id128, bus->id128, sizeof(hello->id128)); -+ -+ conn->flags = hello->flags; -+ atomic64_set(&conn->attach_flags, hello->attach_flags); -+ -+ if (is_activator) { -+ u64 flags = KDBUS_NAME_ACTIVATOR; -+ -+ ret = kdbus_name_acquire(bus->name_registry, conn, -+ name, &flags, NULL); -+ if (ret < 0) -+ goto exit_unref_ep; -+ } -+ -+ if (is_monitor) { -+ down_write(&bus->conn_rwlock); -+ list_add_tail(&conn->monitor_entry, &bus->monitors_list); -+ up_write(&bus->conn_rwlock); -+ } -+ -+ /* privileged processes can impersonate somebody else */ -+ if (creds || seclabel) { -+ ret = kdbus_meta_new(&conn->owner_meta); -+ if (ret < 0) -+ goto exit_release_names; -+ -+ if (creds) { -+ ret = kdbus_meta_append_data(conn->owner_meta, -+ KDBUS_ITEM_CREDS, -+ creds, sizeof(*creds)); -+ if (ret < 0) -+ goto exit_free_meta; -+ } -+ -+ if (seclabel) { -+ ret = kdbus_meta_append_data(conn->owner_meta, -+ KDBUS_ITEM_SECLABEL, -+ seclabel, seclabel_len); -+ if (ret < 0) -+ goto exit_free_meta; -+ } -+ -+ /* use the information provided with the HELLO call */ -+ conn->meta = conn->owner_meta; -+ } else { -+ /* use the connection's metadata gathered at open() */ -+ conn->meta = meta; -+ } -+ -+ /* -+ * Account the connection against the current user (UID), or for -+ * custom endpoints use the anonymous user assigned to the endpoint. -+ */ -+ if (ep->user) { -+ conn->user = kdbus_domain_user_ref(ep->user); -+ } else { -+ ret = kdbus_domain_get_user(ep->bus->domain, -+ current_fsuid(), -+ &conn->user); -+ if (ret < 0) -+ goto exit_free_meta; -+ } -+ -+ /* lock order: domain -> bus -> ep -> names -> conn */ -+ mutex_lock(&bus->lock); -+ mutex_lock(&ep->lock); -+ down_write(&bus->conn_rwlock); -+ -+ if (bus->disconnected || ep->disconnected) { -+ ret = -ESHUTDOWN; -+ goto exit_unref_user_unlock; -+ } -+ -+ if (!kdbus_bus_uid_is_privileged(bus) && -+ atomic_inc_return(&conn->user->connections) > KDBUS_USER_MAX_CONN) { -+ atomic_dec(&conn->user->connections); -+ ret = -EMFILE; -+ goto exit_unref_user_unlock; -+ } -+ -+ /* link into bus and endpoint */ -+ list_add_tail(&conn->ep_entry, &ep->conn_list); -+ hash_add(bus->conn_hash, &conn->hentry, conn->id); -+ -+ up_write(&bus->conn_rwlock); -+ mutex_unlock(&ep->lock); -+ mutex_unlock(&bus->lock); -+ -+ /* notify subscribers about the new active connection */ -+ ret = kdbus_notify_id_change(conn->bus, KDBUS_ITEM_ID_ADD, -+ conn->id, conn->flags); -+ if (ret < 0) { -+ atomic_dec(&conn->user->connections); -+ goto exit_domain_user_unref; -+ } -+ -+ kdbus_notify_flush(conn->bus); -+ -+ *c = conn; -+ return 0; -+ -+exit_unref_user_unlock: -+ up_write(&bus->conn_rwlock); -+ mutex_unlock(&ep->lock); -+ mutex_unlock(&bus->lock); -+exit_domain_user_unref: -+ kdbus_domain_user_unref(conn->user); -+exit_free_meta: -+ kdbus_meta_free(conn->owner_meta); -+exit_release_names: -+ kdbus_name_remove_by_conn(bus->name_registry, conn); -+exit_unref_ep: -+ kdbus_ep_unref(conn->ep); -+ kdbus_bus_unref(conn->bus); -+ kdbus_match_db_free(conn->match_db); -+exit_free_pool: -+ kdbus_pool_free(conn->pool); -+exit_unref_cred: -+ put_cred(conn->cred); -+exit_free_conn: -+ kfree(conn->name); -+ kfree(conn); -+ -+ return ret; -+} -+ -+/** -+ * kdbus_conn_has_name() - check if a connection owns a name -+ * @conn: Connection -+ * @name: Well-know name to check for -+ * -+ * 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; -+ bool match = false; -+ -+ mutex_lock(&conn->lock); -+ list_for_each_entry(e, &conn->names_list, conn_entry) { -+ if (strcmp(e->name, name) == 0) { -+ match = true; -+ break; -+ } -+ } -+ mutex_unlock(&conn->lock); -+ -+ return match; -+} ---- /dev/null -+++ b/drivers/misc/kdbus/connection.h -@@ -0,0 +1,177 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 Linux Foundation -+ * Copyright (C) 2014 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. -+ */ -+ -+#ifndef __KDBUS_CONNECTION_H -+#define __KDBUS_CONNECTION_H -+ -+#include <linux/atomic.h> -+#include <linux/lockdep.h> -+#include "limits.h" -+#include "metadata.h" -+#include "pool.h" -+#include "queue.h" -+#include "util.h" -+ -+#define KDBUS_HELLO_SPECIAL_CONN (KDBUS_HELLO_ACTIVATOR | \ -+ KDBUS_HELLO_POLICY_HOLDER | \ -+ KDBUS_HELLO_MONITOR) -+ -+/** -+ * struct kdbus_conn - connection to a bus -+ * @kref: Reference count -+ * @active: Active references to the connection -+ * @id: Connection ID -+ * @flags: KDBUS_HELLO_* flags -+ * @attach_flags: KDBUS_ATTACH_* flags -+ * @name: Human-readable connection name, used for debugging -+ * @bus: The bus this connection belongs to -+ * @ep: The endpoint this connection belongs to -+ * @lock: Connection data lock -+ * @msg_users: Array to account the number of queued messages per -+ * individual user -+ * @msg_users_max: Size of the users array -+ * @hentry: Entry in ID <-> connection map -+ * @ep_entry: Entry in endpoint -+ * @monitor_entry: Entry in monitor, if the connection is a monitor -+ * @names_list: List of well-known names -+ * @names_queue_list: Well-known names this connection waits for -+ * @reply_list: List of connections this connection expects -+ * a reply from. -+ * @work: Delayed work to handle timeouts -+ * @activator_of: Well-known name entry this connection acts as an -+ * activator for -+ * @match_db: Subscription filter to broadcast messages -+ * @meta: Active connection creator's metadata/credentials, -+ * either from the handle or from HELLO -+ * @owner_meta: The connection's metadata/credentials supplied by -+ * HELLO -+ * @pool: The user's buffer to receive messages -+ * @user: Owner of the connection -+ * @cred: The credentials of the connection at creation time -+ * @name_count: Number of owned well-known names -+ * @reply_count: Number of requests this connection has issued, and -+ * waits for replies from the peer -+ * @wait: Wake up this endpoint -+ * @queue: The message queue associcated with this connection -+ */ -+struct kdbus_conn { -+ struct kref kref; -+ atomic_t active; -+#ifdef CONFIG_DEBUG_LOCK_ALLOC -+ struct lockdep_map dep_map; -+#endif -+ u64 id; -+ u64 flags; -+ atomic64_t attach_flags; -+ const char *name; -+ struct kdbus_bus *bus; -+ struct kdbus_ep *ep; -+ struct mutex lock; -+ unsigned int *msg_users; -+ unsigned int msg_users_max; -+ struct hlist_node hentry; -+ struct list_head ep_entry; -+ struct list_head monitor_entry; -+ struct list_head names_list; -+ struct list_head names_queue_list; -+ struct list_head reply_list; -+ struct delayed_work work; -+ struct kdbus_name_entry *activator_of; -+ struct kdbus_match_db *match_db; -+ struct kdbus_meta *meta; -+ struct kdbus_meta *owner_meta; -+ struct kdbus_pool *pool; -+ struct kdbus_domain_user *user; -+ const struct cred *cred; -+ atomic_t name_count; -+ atomic_t reply_count; -+ wait_queue_head_t wait; -+ struct kdbus_queue queue; -+}; -+ -+struct kdbus_kmsg; -+struct kdbus_name_registry; -+ -+int kdbus_conn_new(struct kdbus_ep *ep, -+ struct kdbus_cmd_hello *hello, -+ struct kdbus_meta *meta, -+ struct kdbus_conn **conn); -+struct kdbus_conn *kdbus_conn_ref(struct kdbus_conn *conn); -+struct kdbus_conn *kdbus_conn_unref(struct kdbus_conn *conn); -+int kdbus_conn_acquire(struct kdbus_conn *conn); -+void kdbus_conn_release(struct kdbus_conn *conn); -+int kdbus_conn_disconnect(struct kdbus_conn *conn, bool ensure_queue_empty); -+bool kdbus_conn_active(const struct kdbus_conn *conn); -+void kdbus_conn_purge_policy_cache(struct kdbus_conn *conn); -+ -+int kdbus_cmd_msg_recv(struct kdbus_conn *conn, -+ struct kdbus_cmd_recv *recv); -+int kdbus_cmd_msg_cancel(struct kdbus_conn *conn, -+ u64 cookie); -+int kdbus_cmd_info(struct kdbus_conn *conn, -+ struct kdbus_cmd_info *cmd_info); -+int kdbus_cmd_conn_update(struct kdbus_conn *conn, -+ const struct kdbus_cmd_update *cmd_update); -+int kdbus_conn_kmsg_send(struct kdbus_ep *ep, -+ struct kdbus_conn *conn_src, -+ struct kdbus_kmsg *kmsg); -+int kdbus_conn_move_messages(struct kdbus_conn *conn_dst, -+ struct kdbus_conn *conn_src, -+ u64 name_id); -+bool kdbus_conn_has_name(struct kdbus_conn *conn, const char *name); -+ -+/** -+ * kdbus_conn_is_connected() - Check if connection is ordinary -+ * @conn: The connection to check -+ * -+ * Return: Non-zero if the connection is an ordinary connection -+ */ -+static inline int kdbus_conn_is_connected(const struct kdbus_conn *conn) -+{ -+ return !(conn->flags & KDBUS_HELLO_SPECIAL_CONN); -+} -+ -+/** -+ * kdbus_conn_is_activator() - Check if connection is an activator -+ * @conn: The connection to check -+ * -+ * Return: Non-zero if the connection is an activator -+ */ -+static inline int kdbus_conn_is_activator(const struct kdbus_conn *conn) -+{ -+ return conn->flags & KDBUS_HELLO_ACTIVATOR; -+} -+ -+/** -+ * kdbus_conn_is_policy_holder() - Check if connection is a policy holder -+ * @conn: The connection to check -+ * -+ * Return: Non-zero if the connection is a policy holder -+ */ -+static inline int kdbus_conn_is_policy_holder(const struct kdbus_conn *conn) -+{ -+ return conn->flags & KDBUS_HELLO_POLICY_HOLDER; -+} -+ -+/** -+ * kdbus_conn_is_monitor() - Check if connection is a monitor -+ * @conn: The connection to check -+ * -+ * Return: Non-zero if the connection is a monitor -+ */ -+static inline int kdbus_conn_is_monitor(const struct kdbus_conn *conn) -+{ -+ return conn->flags & KDBUS_HELLO_MONITOR; -+} -+#endif ---- /dev/null -+++ b/drivers/misc/kdbus/item.c -@@ -0,0 +1,256 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <linux/ctype.h> -+#include <linux/string.h> -+ -+#include "item.h" -+#include "limits.h" -+#include "util.h" -+ -+#define KDBUS_ITEM_VALID(_i, _is, _s) \ -+ ((_i)->size > KDBUS_ITEM_HEADER_SIZE && \ -+ (u8 *)(_i) + (_i)->size <= (u8 *)(_is) + (_s) && \ -+ (u8 *)(_i) >= (u8 *)(_is)) -+ -+#define KDBUS_ITEMS_END(_i, _is, _s) \ -+ ((u8 *)_i == ((u8 *)(_is) + KDBUS_ALIGN8(_s))) -+ -+/** -+ * kdbus_item_validate_name() - validate an item containing a name -+ * @item: Item to validate -+ * -+ * Return: zero on success or an negative error code on failure -+ */ -+int kdbus_item_validate_name(const struct kdbus_item *item) -+{ -+ if (item->size < KDBUS_ITEM_HEADER_SIZE + 2) -+ return -EINVAL; -+ -+ if (item->size > KDBUS_ITEM_HEADER_SIZE + -+ KDBUS_SYSNAME_MAX_LEN + 1) -+ return -ENAMETOOLONG; -+ -+ if (!kdbus_str_valid(item->str, KDBUS_ITEM_PAYLOAD_SIZE(item))) -+ return -EINVAL; -+ -+ return kdbus_sysname_is_valid(item->str); -+} -+ -+static int kdbus_item_validate(const struct kdbus_item *item) -+{ -+ size_t payload_size = KDBUS_ITEM_PAYLOAD_SIZE(item); -+ size_t l; -+ int ret; -+ -+ if (item->size < KDBUS_ITEM_HEADER_SIZE) -+ return -EINVAL; -+ -+ switch (item->type) { -+ case KDBUS_ITEM_PAYLOAD_VEC: -+ if (payload_size != sizeof(struct kdbus_vec)) -+ return -EINVAL; -+ if (item->vec.size == 0 || item->vec.size > SIZE_MAX) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_PAYLOAD_OFF: -+ if (payload_size != sizeof(struct kdbus_vec)) -+ return -EINVAL; -+ if (item->vec.size == 0 || item->vec.size > SIZE_MAX) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_PAYLOAD_MEMFD: -+ if (payload_size != sizeof(struct kdbus_memfd)) -+ return -EINVAL; -+ if (item->memfd.size == 0 || item->memfd.size > SIZE_MAX) -+ return -EINVAL; -+ if (item->memfd.fd < 0) -+ return -EBADF; -+ break; -+ -+ case KDBUS_ITEM_FDS: -+ if (payload_size % sizeof(int) != 0) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_BLOOM_PARAMETER: -+ if (payload_size != sizeof(struct kdbus_bloom_parameter)) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_BLOOM_FILTER: -+ /* followed by the bloom-mask, depends on the bloom-size */ -+ if (payload_size < sizeof(struct kdbus_bloom_filter)) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_BLOOM_MASK: -+ /* size depends on bloom-size of bus */ -+ break; -+ -+ case KDBUS_ITEM_CONN_NAME: -+ case KDBUS_ITEM_MAKE_NAME: -+ ret = kdbus_item_validate_name(item); -+ if (ret < 0) -+ return ret; -+ break; -+ -+ case KDBUS_ITEM_ATTACH_FLAGS: -+ case KDBUS_ITEM_ID: -+ if (payload_size != sizeof(u64)) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_TIMESTAMP: -+ if (payload_size != sizeof(struct kdbus_timestamp)) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_CREDS: -+ if (payload_size != sizeof(struct kdbus_creds)) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_AUXGROUPS: -+ if (payload_size % sizeof(u64) != 0) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_NAME: -+ case KDBUS_ITEM_DST_NAME: -+ case KDBUS_ITEM_PID_COMM: -+ case KDBUS_ITEM_TID_COMM: -+ case KDBUS_ITEM_EXE: -+ case KDBUS_ITEM_CMDLINE: -+ case KDBUS_ITEM_CGROUP: -+ case KDBUS_ITEM_SECLABEL: -+ if (!kdbus_str_valid(item->str, payload_size)) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_CAPS: -+ /* TODO */ -+ break; -+ -+ case KDBUS_ITEM_AUDIT: -+ if (payload_size != sizeof(struct kdbus_audit)) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_POLICY_ACCESS: -+ if (payload_size != sizeof(struct kdbus_policy_access)) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_NAME_ADD: -+ case KDBUS_ITEM_NAME_REMOVE: -+ case KDBUS_ITEM_NAME_CHANGE: -+ if (payload_size < sizeof(struct kdbus_notify_name_change)) -+ return -EINVAL; -+ l = payload_size - offsetof(struct kdbus_notify_name_change, -+ name); -+ if (l > 0 && !kdbus_str_valid(item->name_change.name, l)) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_ID_ADD: -+ case KDBUS_ITEM_ID_REMOVE: -+ if (payload_size != sizeof(struct kdbus_notify_id_change)) -+ return -EINVAL; -+ break; -+ -+ case KDBUS_ITEM_REPLY_TIMEOUT: -+ case KDBUS_ITEM_REPLY_DEAD: -+ if (payload_size != 0) -+ return -EINVAL; -+ break; -+ -+ default: -+ break; -+ } -+ -+ return 0; -+} -+ -+/** -+ * kdbus_items_validate() - validate items passed by user-space -+ * @items: items to validate -+ * @items_size: number of items -+ * -+ * This verifies that the passed items pointer is consistent and valid. -+ * Furthermore, each item is checked for: -+ * - valid "size" value -+ * - payload is of expected type -+ * - payload is fully included in the item -+ * - string payloads are zero-terminated -+ * -+ * Return: 0 on success, negative error code on failure. -+ */ -+int kdbus_items_validate(const struct kdbus_item *items, size_t items_size) -+{ -+ const struct kdbus_item *item; -+ int ret; -+ -+ KDBUS_ITEMS_FOREACH(item, items, items_size) { -+ if (!KDBUS_ITEM_VALID(item, items, items_size)) -+ return -EINVAL; -+ -+ ret = kdbus_item_validate(item); -+ if (ret < 0) -+ return ret; -+ } -+ -+ if (!KDBUS_ITEMS_END(item, items, items_size)) -+ return -EINVAL; -+ -+ return 0; -+} -+ -+/** -+ * kdbus_items_get_str() - get string from a list of items -+ * @items: The items to walk -+ * @items_size: The size of all items -+ * @item_type: The item type to look for -+ * @str_ret: A pointer to store the found name -+ * -+ * This function walks a list of items and searches for items of type -+ * @item_type. If it finds exactly one such item, @str_ret will be set to -+ * the .str member of the item. -+ * -+ * Return: 0 if the item was found exactly once, -EEXIST if the item was -+ * found more than once, and -EBADMSG if there was no item of the given type. -+ */ -+int kdbus_items_get_str(const struct kdbus_item *items, size_t items_size, -+ unsigned int item_type, const char **str_ret) -+{ -+ const struct kdbus_item *item; -+ const char *n = NULL; -+ -+ KDBUS_ITEMS_FOREACH(item, items, items_size) { -+ if (item->type == item_type) { -+ if (n) -+ return -EEXIST; -+ -+ n = item->str; -+ continue; -+ } -+ } -+ -+ if (!n) -+ return -EBADMSG; -+ -+ *str_ret = n; -+ return 0; -+} ---- /dev/null -+++ b/drivers/misc/kdbus/item.h -@@ -0,0 +1,40 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_ITEM_H -+#define __KDBUS_ITEM_H -+ -+#include <linux/kernel.h> -+#include <uapi/linux/kdbus.h> -+ -+#include "util.h" -+ -+/* generic access and iterators over a stream of items */ -+#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data) -+#define KDBUS_ITEM_PAYLOAD_SIZE(_i) ((_i)->size - KDBUS_ITEM_HEADER_SIZE) -+#define KDBUS_ITEM_SIZE(_s) KDBUS_ALIGN8(KDBUS_ITEM_HEADER_SIZE + (_s)) -+#define KDBUS_ITEM_NEXT(_i) (typeof(_i))(((u8 *)_i) + KDBUS_ALIGN8((_i)->size)) -+#define KDBUS_ITEMS_SIZE(_h, _is) ((_h)->size - offsetof(typeof(*_h), _is)) -+ -+#define KDBUS_ITEMS_FOREACH(_i, _is, _s) \ -+ for (_i = _is; \ -+ ((u8 *)(_i) < (u8 *)(_is) + (_s)) && \ -+ ((u8 *)(_i) >= (u8 *)(_is)); \ -+ _i = KDBUS_ITEM_NEXT(_i)) -+ -+int kdbus_item_validate_name(const struct kdbus_item *item); -+int kdbus_items_validate(const struct kdbus_item *items, size_t items_size); -+int kdbus_items_get_str(const struct kdbus_item *items, size_t items_size, -+ unsigned int item_type, const char **str_ret); -+ -+#endif ---- /dev/null -+++ b/drivers/misc/kdbus/message.c -@@ -0,0 +1,420 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <linux/capability.h> -+#include <linux/cgroup.h> -+#include <linux/cred.h> -+#include <linux/device.h> -+#include <linux/file.h> -+#include <linux/init.h> -+#include <linux/module.h> -+#include <linux/mutex.h> -+#include <linux/sched.h> -+#include <linux/shmem_fs.h> -+#include <linux/sizes.h> -+#include <linux/slab.h> -+#include <linux/uaccess.h> -+#include <net/sock.h> -+ -+#include "bus.h" -+#include "connection.h" -+#include "domain.h" -+#include "endpoint.h" -+#include "handle.h" -+#include "item.h" -+#include "match.h" -+#include "message.h" -+#include "names.h" -+#include "policy.h" -+ -+#define KDBUS_KMSG_HEADER_SIZE offsetof(struct kdbus_kmsg, msg) -+ -+/** -+ * kdbus_kmsg_free() - free allocated message -+ * @kmsg: Message -+ */ -+void kdbus_kmsg_free(struct kdbus_kmsg *kmsg) -+{ -+ kdbus_fput_files(kmsg->memfds, kmsg->memfds_count); -+ kdbus_fput_files(kmsg->fds, kmsg->fds_count); -+ kdbus_meta_free(kmsg->meta); -+ kfree(kmsg->memfds); -+ kfree(kmsg->fds); -+ kfree(kmsg); -+} -+ -+/** -+ * kdbus_kmsg_new() - allocate message -+ * @extra_size: additional size to reserve for data -+ * @kmsg: Returned Message -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_kmsg_new(size_t extra_size, struct kdbus_kmsg **kmsg) -+{ -+ struct kdbus_kmsg *m; -+ size_t size; -+ -+ BUG_ON(*kmsg); -+ -+ size = sizeof(struct kdbus_kmsg) + KDBUS_ITEM_SIZE(extra_size); -+ m = kzalloc(size, GFP_KERNEL); -+ if (!m) -+ return -ENOMEM; -+ -+ m->msg.size = size - KDBUS_KMSG_HEADER_SIZE; -+ m->msg.items[0].size = KDBUS_ITEM_SIZE(extra_size); -+ -+ *kmsg = m; -+ return 0; -+} -+ -+static int kdbus_handle_check_file(struct file *file) -+{ -+ struct inode *inode = file_inode(file); -+ struct socket *sock; -+ -+ /* -+ * Don't allow file descriptors in the transport that themselves allow -+ * file descriptor queueing. This will eventually be allowed once both -+ * unix domain sockets and kdbus share a generic garbage collector. -+ */ -+ -+ if (file->f_op == &kdbus_handle_ops) -+ return -EOPNOTSUPP; -+ -+ if (!S_ISSOCK(inode->i_mode)) -+ return 0; -+ -+ if (file->f_mode & FMODE_PATH) -+ return 0; -+ -+ sock = SOCKET_I(inode); -+ if (sock->sk && sock->ops && sock->ops->family == PF_UNIX) -+ return -EOPNOTSUPP; -+ -+ return 0; -+} -+ -+/* -+ * kdbus_msg_scan_items() - validate incoming data and prepare parsing -+ * @conn: Connection -+ * @kmsg: Message -+ * -+ * Return: 0 on success, negative errno on failure. -+ * -+ * On errors, the caller should drop any taken reference with -+ * kdbus_kmsg_free() -+ */ -+static int kdbus_msg_scan_items(struct kdbus_conn *conn, -+ struct kdbus_kmsg *kmsg) -+{ -+ const struct kdbus_msg *msg = &kmsg->msg; -+ const struct kdbus_item *item; -+ unsigned int items_count = 0; -+ size_t vecs_size = 0; -+ bool has_bloom = false; -+ bool has_name = false; -+ bool has_fds = false; -+ struct file *f; -+ -+ KDBUS_ITEMS_FOREACH(item, msg->items, KDBUS_ITEMS_SIZE(msg, items)) -+ if (item->type == KDBUS_ITEM_PAYLOAD_MEMFD) -+ kmsg->memfds_count++; -+ -+ if (kmsg->memfds_count > 0) { -+ kmsg->memfds = kcalloc(kmsg->memfds_count, -+ sizeof(struct file *), GFP_KERNEL); -+ if (!kmsg->memfds) -+ return -ENOMEM; -+ -+ /* reset counter so we can reuse it */ -+ kmsg->memfds_count = 0; -+ } -+ -+ KDBUS_ITEMS_FOREACH(item, msg->items, KDBUS_ITEMS_SIZE(msg, items)) { -+ size_t payload_size; -+ -+ if (++items_count > KDBUS_MSG_MAX_ITEMS) -+ return -E2BIG; -+ -+ payload_size = KDBUS_ITEM_PAYLOAD_SIZE(item); -+ -+ switch (item->type) { -+ case KDBUS_ITEM_PAYLOAD_VEC: -+ if (vecs_size + item->vec.size <= vecs_size) -+ return -EMSGSIZE; -+ -+ vecs_size += item->vec.size; -+ if (vecs_size > KDBUS_MSG_MAX_PAYLOAD_VEC_SIZE) -+ return -EMSGSIZE; -+ -+ /* \0-bytes records store only the alignment bytes */ -+ if (KDBUS_PTR(item->vec.address)) -+ kmsg->vecs_size += item->vec.size; -+ else -+ kmsg->vecs_size += item->vec.size % 8; -+ kmsg->vecs_count++; -+ break; -+ -+ case KDBUS_ITEM_PAYLOAD_MEMFD: { -+ int seals, mask; -+ int fd = item->memfd.fd; -+ -+ /* Verify the fd and increment the usage count */ -+ if (fd < 0) -+ return -EBADF; -+ -+ f = fget(fd); -+ if (!f) -+ return -EBADF; -+ -+ kmsg->memfds[kmsg->memfds_count] = f; -+ kmsg->memfds_count++; -+ -+ /* -+ * We only accept a sealed memfd file whose content -+ * cannot be altered by the sender or anybody else -+ * while it is shared or in-flight. Other files need -+ * to be passed with KDBUS_MSG_FDS. -+ */ -+ seals = shmem_get_seals(f); -+ if (seals < 0) -+ return -EMEDIUMTYPE; -+ -+ mask = F_SEAL_SHRINK | F_SEAL_GROW | F_SEAL_WRITE; -+ if ((seals & mask) != mask) -+ return -ETXTBSY; -+ -+ /* -+ * The specified size in the item cannot be larger -+ * than the backing file. -+ */ -+ if (item->memfd.size > i_size_read(file_inode(f))) -+ return -EBADF; -+ -+ break; -+ } -+ -+ case KDBUS_ITEM_FDS: { -+ unsigned int n, i; -+ -+ /* do not allow multiple fd arrays */ -+ if (has_fds) -+ return -EEXIST; -+ has_fds = true; -+ -+ /* do not allow to broadcast file descriptors */ -+ if (msg->dst_id == KDBUS_DST_ID_BROADCAST) -+ return -ENOTUNIQ; -+ -+ n = KDBUS_ITEM_PAYLOAD_SIZE(item) / sizeof(int); -+ if (n > KDBUS_MSG_MAX_FDS) -+ return -EMFILE; -+ -+ kmsg->fds = kcalloc(n, sizeof(*kmsg->fds), GFP_KERNEL); -+ if (!kmsg->fds) -+ return -ENOMEM; -+ -+ for (i = 0; i < n; i++) { -+ int ret; -+ int fd = item->fds[i]; -+ -+ /* -+ * Verify the fd and increment the usage count. -+ * Use fget_raw() to allow passing O_PATH fds. -+ */ -+ if (fd < 0) -+ return -EBADF; -+ -+ f = fget_raw(fd); -+ if (!f) -+ return -EBADF; -+ -+ kmsg->fds[i] = f; -+ kmsg->fds_count++; -+ -+ ret = kdbus_handle_check_file(f); -+ if (ret < 0) -+ return ret; -+ } -+ -+ break; -+ } -+ -+ case KDBUS_ITEM_BLOOM_FILTER: { -+ u64 bloom_size; -+ -+ /* do not allow multiple bloom filters */ -+ if (has_bloom) -+ return -EEXIST; -+ has_bloom = true; -+ -+ /* bloom filters are only for broadcast messages */ -+ if (msg->dst_id != KDBUS_DST_ID_BROADCAST) -+ return -EBADMSG; -+ -+ bloom_size = payload_size - -+ offsetof(struct kdbus_bloom_filter, data); -+ -+ /* -+ * Allow only bloom filter sizes of a multiple of 64bit. -+ */ -+ if (!KDBUS_IS_ALIGNED8(bloom_size)) -+ return -EFAULT; -+ -+ /* do not allow mismatching bloom filter sizes */ -+ if (bloom_size != conn->bus->bloom.size) -+ return -EDOM; -+ -+ kmsg->bloom_filter = &item->bloom_filter; -+ break; -+ } -+ -+ case KDBUS_ITEM_DST_NAME: -+ /* do not allow multiple names */ -+ if (has_name) -+ return -EEXIST; -+ has_name = true; -+ -+ if (!kdbus_name_is_valid(item->str, false)) -+ return -EINVAL; -+ -+ kmsg->dst_name = item->str; -+ break; -+ } -+ } -+ -+ /* name is needed if no ID is given */ -+ if (msg->dst_id == KDBUS_DST_ID_NAME && !has_name) -+ return -EDESTADDRREQ; -+ -+ if (msg->dst_id == KDBUS_DST_ID_BROADCAST) { -+ /* broadcasts can't take names */ -+ if (has_name) -+ return -EBADMSG; -+ -+ /* broadcast messages require a bloom filter */ -+ if (!has_bloom) -+ return -EBADMSG; -+ -+ /* timeouts are not allowed for broadcasts */ -+ if (msg->timeout_ns > 0) -+ return -ENOTUNIQ; -+ } -+ -+ /* bloom filters are for undirected messages only */ -+ if (has_name && has_bloom) -+ return -EBADMSG; -+ -+ return 0; -+} -+ -+/** -+ * kdbus_kmsg_new_from_user() - copy message from user memory -+ * @conn: Connection -+ * @msg: User-provided message -+ * @kmsg: Copy of message -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_kmsg_new_from_user(struct kdbus_conn *conn, -+ struct kdbus_msg __user *msg, -+ struct kdbus_kmsg **kmsg) -+{ -+ struct kdbus_kmsg *m; -+ u64 size, alloc_size; -+ int ret; -+ -+ BUG_ON(*kmsg); -+ -+ if (!KDBUS_IS_ALIGNED8((unsigned long)msg)) -+ return -EFAULT; -+ -+ if (kdbus_size_get_user(&size, msg, struct kdbus_msg)) -+ return -EFAULT; -+ -+ if (size < sizeof(struct kdbus_msg) || size > KDBUS_MSG_MAX_SIZE) -+ return -EMSGSIZE; -+ -+ alloc_size = size + KDBUS_KMSG_HEADER_SIZE; -+ -+ m = kmalloc(alloc_size, GFP_KERNEL); -+ if (!m) -+ return -ENOMEM; -+ memset(m, 0, KDBUS_KMSG_HEADER_SIZE); -+ -+ if (copy_from_user(&m->msg, msg, size)) { -+ ret = -EFAULT; -+ goto exit_free; -+ } -+ -+ ret = kdbus_items_validate(m->msg.items, -+ KDBUS_ITEMS_SIZE(&m->msg, items)); -+ if (ret < 0) -+ goto exit_free; -+ -+ /* do not accept kernel-generated messages */ -+ if (m->msg.payload_type == KDBUS_PAYLOAD_KERNEL) { -+ ret = -EINVAL; -+ goto exit_free; -+ } -+ -+ ret = kdbus_negotiate_flags(&m->msg, msg, struct kdbus_msg, -+ KDBUS_MSG_FLAGS_EXPECT_REPLY | -+ KDBUS_MSG_FLAGS_SYNC_REPLY | -+ KDBUS_MSG_FLAGS_NO_AUTO_START); -+ if (ret < 0) -+ goto exit_free; -+ -+ if (m->msg.flags & KDBUS_MSG_FLAGS_EXPECT_REPLY) { -+ /* requests for replies need a timeout */ -+ if (m->msg.timeout_ns == 0) { -+ ret = -EINVAL; -+ goto exit_free; -+ } -+ -+ /* replies may not be expected for broadcasts */ -+ if (m->msg.dst_id == KDBUS_DST_ID_BROADCAST) { -+ ret = -ENOTUNIQ; -+ goto exit_free; -+ } -+ } else { -+ /* -+ * KDBUS_MSG_FLAGS_SYNC_REPLY is only valid together with -+ * KDBUS_MSG_FLAGS_EXPECT_REPLY -+ */ -+ if (m->msg.flags & KDBUS_MSG_FLAGS_SYNC_REPLY) { -+ ret = -EINVAL; -+ goto exit_free; -+ } -+ } -+ -+ ret = kdbus_msg_scan_items(conn, m); -+ if (ret < 0) -+ goto exit_free; -+ -+ /* patch-in the source of this message */ -+ if (m->msg.src_id > 0 && m->msg.src_id != conn->id) { -+ ret = -EINVAL; -+ goto exit_free; -+ } -+ m->msg.src_id = conn->id; -+ -+ *kmsg = m; -+ return 0; -+ -+exit_free: -+ kdbus_kmsg_free(m); -+ return ret; -+} ---- /dev/null -+++ b/drivers/misc/kdbus/message.h -@@ -0,0 +1,72 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_MESSAGE_H -+#define __KDBUS_MESSAGE_H -+ -+#include "util.h" -+#include "metadata.h" -+ -+/** -+ * struct kdbus_kmsg - internal message handling data -+ * @seq: Domain-global message sequence number -+ * @notify_type: Short-cut for faster lookup -+ * @notify_old_id: Short-cut for faster lookup -+ * @notify_new_id: Short-cut for faster lookup -+ * @notify_name: Short-cut for faster lookup -+ * @dst_name: Short-cut to msg for faster lookup -+ * @dst_name_id: Short-cut to msg for faster lookup -+ * @bloom_filter: Bloom filter to match message properties -+ * @bloom_generation: Generation of bloom element set -+ * @fds: Array of file descriptors to pass -+ * @fds_count: Number of file descriptors to pass -+ * @meta: Appended SCM-like metadata of the sending process -+ * @vecs_size: Size of PAYLOAD data -+ * @vecs_count: Number of PAYLOAD vectors -+ * @memfds_count: Number of memfds to pass -+ * @queue_entry: List of kernel-generated notifications -+ * @msg: Message from or to userspace -+ */ -+struct kdbus_kmsg { -+ u64 seq; -+ u64 notify_type; -+ u64 notify_old_id; -+ u64 notify_new_id; -+ const char *notify_name; -+ -+ const char *dst_name; -+ u64 dst_name_id; -+ const struct kdbus_bloom_filter *bloom_filter; -+ u64 bloom_generation; -+ struct file **fds; -+ unsigned int fds_count; -+ struct kdbus_meta *meta; -+ size_t vecs_size; -+ unsigned int vecs_count; -+ struct file **memfds; -+ unsigned int memfds_count; -+ struct list_head queue_entry; -+ -+ /* variable size, must be the last member */ -+ struct kdbus_msg msg; -+}; -+ -+struct kdbus_ep; -+struct kdbus_conn; -+ -+int kdbus_kmsg_new(size_t extra_size, struct kdbus_kmsg **kmsg); -+int kdbus_kmsg_new_from_user(struct kdbus_conn *conn, -+ struct kdbus_msg __user *msg, -+ struct kdbus_kmsg **kmsg); -+void kdbus_kmsg_free(struct kdbus_kmsg *kmsg); -+#endif ---- /dev/null -+++ b/drivers/misc/kdbus/queue.c -@@ -0,0 +1,602 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <linux/audit.h> -+#include <linux/device.h> -+#include <linux/file.h> -+#include <linux/fs.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/poll.h> -+#include <linux/sched.h> -+#include <linux/sizes.h> -+#include <linux/slab.h> -+#include <linux/syscalls.h> -+ -+#include "connection.h" -+#include "item.h" -+#include "message.h" -+#include "metadata.h" -+#include "util.h" -+#include "queue.h" -+ -+static int kdbus_queue_entry_fds_install(struct kdbus_queue_entry *entry) -+{ -+ unsigned int i; -+ int ret, *fds; -+ size_t count; -+ -+ /* get array of file descriptors */ -+ count = entry->fds_count + entry->memfds_count; -+ if (!count) -+ return 0; -+ -+ fds = kcalloc(count, sizeof(int), GFP_KERNEL); -+ if (!fds) -+ return -ENOMEM; -+ -+ /* allocate new file descriptors in the receiver's process */ -+ for (i = 0; i < count; i++) { -+ fds[i] = get_unused_fd_flags(O_CLOEXEC); -+ if (fds[i] < 0) { -+ ret = fds[i]; -+ goto exit_remove_unused; -+ } -+ } -+ -+ if (entry->fds_count) { -+ /* copy the array into the message item */ -+ ret = kdbus_pool_slice_copy(entry->slice, entry->fds, fds, -+ entry->fds_count * sizeof(int)); -+ if (ret < 0) -+ goto exit_remove_unused; -+ -+ /* install files in the receiver's process */ -+ for (i = 0; i < entry->fds_count; i++) -+ fd_install(fds[i], get_file(entry->fds_fp[i])); -+ } -+ -+ if (entry->memfds_count) { -+ off_t o = entry->fds_count; -+ -+ /* -+ * Update the file descriptor number in the items. -+ * We remembered the locations of the values in the buffer. -+ */ -+ for (i = 0; i < entry->memfds_count; i++) { -+ ret = kdbus_pool_slice_copy(entry->slice, -+ entry->memfds[i], -+ &fds[o + i], sizeof(int)); -+ if (ret < 0) -+ goto exit_rewind_fds; -+ } -+ -+ /* install files in the receiver's process */ -+ for (i = 0; i < entry->memfds_count; i++) -+ fd_install(fds[o + i], get_file(entry->memfds_fp[i])); -+ } -+ -+ kfree(fds); -+ return 0; -+ -+exit_rewind_fds: -+ for (i = 0; i < entry->fds_count; i++) -+ sys_close(fds[i]); -+ -+exit_remove_unused: -+ for (i = 0; i < count; i++) { -+ if (fds[i] < 0) -+ break; -+ -+ put_unused_fd(fds[i]); -+ } -+ -+ kfree(fds); -+ return ret; -+} -+ -+/** -+ * kdbus_queue_entry_install() - install message components into the -+ * receiver's process -+ * @entry: The queue entry to install -+ * -+ * This function will install file descriptors into 'current'. -+ * Also, it the associated message has metadata attached which's final values -+ * couldn't be determined before (such as details that are related to name -+ * spaces etc), the correct information is patched in at this point. -+ * -+ * Return: 0 on success. -+ */ -+int kdbus_queue_entry_install(struct kdbus_queue_entry *entry) -+{ -+ int *memfds = NULL; -+ int *fds = NULL; -+ int ret = 0; -+ -+ ret = kdbus_queue_entry_fds_install(entry); -+ if (ret < 0) -+ return ret; -+ -+ kfree(fds); -+ kfree(memfds); -+ kdbus_pool_slice_flush(entry->slice); -+ return 0; -+} -+ -+static int kdbus_queue_entry_payload_add(struct kdbus_queue_entry *entry, -+ const struct kdbus_kmsg *kmsg, -+ size_t items, size_t vec_data) -+{ -+ const struct kdbus_item *item; -+ int ret; -+ -+ if (kmsg->memfds_count > 0) { -+ entry->memfds = kcalloc(kmsg->memfds_count, -+ sizeof(off_t), GFP_KERNEL); -+ if (!entry->memfds) -+ return -ENOMEM; -+ -+ entry->memfds_fp = kcalloc(kmsg->memfds_count, -+ sizeof(struct file *), GFP_KERNEL); -+ if (!entry->memfds_fp) -+ return -ENOMEM; -+ } -+ -+ KDBUS_ITEMS_FOREACH(item, kmsg->msg.items, -+ KDBUS_ITEMS_SIZE(&kmsg->msg, items)) { -+ switch (item->type) { -+ case KDBUS_ITEM_PAYLOAD_VEC: { -+ char tmp[KDBUS_ITEM_HEADER_SIZE + -+ sizeof(struct kdbus_vec)]; -+ struct kdbus_item *it = (struct kdbus_item *)tmp; -+ -+ /* add item */ -+ it->type = KDBUS_ITEM_PAYLOAD_OFF; -+ it->size = sizeof(tmp); -+ -+ /* a NULL address specifies a \0-bytes record */ -+ if (KDBUS_PTR(item->vec.address)) -+ it->vec.offset = vec_data; -+ else -+ it->vec.offset = ~0ULL; -+ it->vec.size = item->vec.size; -+ ret = kdbus_pool_slice_copy(entry->slice, items, -+ it, it->size); -+ if (ret < 0) -+ return ret; -+ items += KDBUS_ALIGN8(it->size); -+ -+ /* \0-bytes record */ -+ if (!KDBUS_PTR(item->vec.address)) { -+ size_t l = item->vec.size % 8; -+ const char *n = "\0\0\0\0\0\0\0"; -+ -+ if (l == 0) -+ break; -+ -+ /* -+ * Preserve the alignment for the next payload -+ * record in the output buffer; write as many -+ * null-bytes to the buffer which the \0-bytes -+ * record would have shifted the alignment. -+ */ -+ ret = kdbus_pool_slice_copy(entry->slice, -+ vec_data, n, l); -+ if (ret < 0) -+ return ret; -+ -+ vec_data += l; -+ break; -+ } -+ -+ /* copy kdbus_vec data from sender to receiver */ -+ ret = kdbus_pool_slice_copy_user(entry->slice, vec_data, -+ KDBUS_PTR(item->vec.address), item->vec.size); -+ if (ret < 0) -+ return ret; -+ -+ vec_data += item->vec.size; -+ break; -+ } -+ -+ case KDBUS_ITEM_PAYLOAD_MEMFD: { -+ char tmp[KDBUS_ITEM_HEADER_SIZE + -+ sizeof(struct kdbus_memfd)]; -+ struct kdbus_item *it = (struct kdbus_item *)tmp; -+ -+ /* add item */ -+ it->type = KDBUS_ITEM_PAYLOAD_MEMFD; -+ it->size = sizeof(tmp); -+ it->memfd.size = item->memfd.size; -+ it->memfd.fd = -1; -+ ret = kdbus_pool_slice_copy(entry->slice, items, -+ it, it->size); -+ if (ret < 0) -+ return ret; -+ -+ /* -+ * Remember the file and the location of the fd number -+ * which will be updated at RECV time. -+ */ -+ entry->memfds[entry->memfds_count] = -+ items + offsetof(struct kdbus_item, memfd.fd); -+ entry->memfds_fp[entry->memfds_count] = -+ get_file(kmsg->memfds[entry->memfds_count]); -+ entry->memfds_count++; -+ -+ items += KDBUS_ALIGN8(it->size); -+ break; -+ } -+ -+ default: -+ break; -+ } -+ } -+ -+ return 0; -+} -+ -+/** -+ * kdbus_queue_entry_add() - Add an queue entry to a queue -+ * @queue: The queue to attach the item to -+ * @entry: The entry to attach -+ * -+ * Adds a previously allocated queue item to a queue, and maintains the -+ * priority r/b tree. -+ */ -+/* add queue entry to connection, maintain priority queue */ -+void kdbus_queue_entry_add(struct kdbus_queue *queue, -+ struct kdbus_queue_entry *entry) -+{ -+ struct rb_node **n, *pn = NULL; -+ bool highest = true; -+ -+ /* sort into priority entry tree */ -+ n = &queue->msg_prio_queue.rb_node; -+ while (*n) { -+ struct kdbus_queue_entry *e; -+ -+ pn = *n; -+ e = rb_entry(pn, struct kdbus_queue_entry, prio_node); -+ -+ /* existing node for this priority, add to its list */ -+ if (likely(entry->priority == e->priority)) { -+ list_add_tail(&entry->prio_entry, &e->prio_entry); -+ goto prio_done; -+ } -+ -+ if (entry->priority < e->priority) { -+ n = &pn->rb_left; -+ } else { -+ n = &pn->rb_right; -+ highest = false; -+ } -+ } -+ -+ /* cache highest-priority entry */ -+ if (highest) -+ queue->msg_prio_highest = &entry->prio_node; -+ -+ /* new node for this priority */ -+ rb_link_node(&entry->prio_node, pn, n); -+ rb_insert_color(&entry->prio_node, &queue->msg_prio_queue); -+ INIT_LIST_HEAD(&entry->prio_entry); -+ -+prio_done: -+ /* add to unsorted fifo list */ -+ list_add_tail(&entry->entry, &queue->msg_list); -+ queue->msg_count++; -+} -+ -+/** -+ * kdbus_queue_entry_peek() - Retrieves an entry from a queue -+ * -+ * @queue: The queue -+ * @priority: The minimum priority of the entry to peek -+ * @use_priority: Boolean flag whether or not to peek by priority -+ * @entry: Pointer to return the peeked entry -+ * -+ * Look for a entry in a queue, either by priority, or the oldest one (FIFO). -+ * The entry is not freed, put off the queue's lists or anything else. -+ * -+ * Return: 0 on success, -ENOMSG if there is no entry with the requested -+ * priority, or -EAGAIN if there are no entries at all. -+ */ -+int kdbus_queue_entry_peek(struct kdbus_queue *queue, -+ s64 priority, bool use_priority, -+ struct kdbus_queue_entry **entry) -+{ -+ struct kdbus_queue_entry *e; -+ -+ if (queue->msg_count == 0) -+ return -EAGAIN; -+ -+ if (use_priority) { -+ /* get next entry with highest priority */ -+ e = rb_entry(queue->msg_prio_highest, -+ struct kdbus_queue_entry, prio_node); -+ -+ /* no entry with the requested priority */ -+ if (e->priority > priority) -+ return -ENOMSG; -+ } else { -+ /* ignore the priority, return the next entry in the entry */ -+ e = list_first_entry(&queue->msg_list, -+ struct kdbus_queue_entry, entry); -+ } -+ -+ *entry = e; -+ -+ return 0; -+} -+ -+/** -+ * kdbus_queue_entry_remove() - Remove an entry from a queue -+ * @conn: The connection containing the queue -+ * @entry: The entry to remove -+ * -+ * Remove an entry from both the queue's list and the priority r/b tree. -+ */ -+void kdbus_queue_entry_remove(struct kdbus_conn *conn, -+ struct kdbus_queue_entry *entry) -+{ -+ struct kdbus_queue *queue = &conn->queue; -+ -+ list_del(&entry->entry); -+ queue->msg_count--; -+ -+ /* user quota */ -+ if (entry->user >= 0) { -+ BUG_ON(conn->msg_users[entry->user] == 0); -+ conn->msg_users[entry->user]--; -+ entry->user = -1; -+ } -+ -+ /* the queue is empty, remove the user quota accounting */ -+ if (queue->msg_count == 0 && conn->msg_users_max > 0) { -+ kfree(conn->msg_users); -+ conn->msg_users = NULL; -+ conn->msg_users_max = 0; -+ } -+ -+ if (list_empty(&entry->prio_entry)) { -+ /* -+ * Single entry for this priority, update cached -+ * highest-priority entry, remove the tree node. -+ */ -+ if (queue->msg_prio_highest == &entry->prio_node) -+ queue->msg_prio_highest = rb_next(&entry->prio_node); -+ -+ rb_erase(&entry->prio_node, &queue->msg_prio_queue); -+ } else { -+ struct kdbus_queue_entry *q; -+ -+ /* -+ * Multiple entries for this priority entry, get next one in -+ * the list. Update cached highest-priority entry, store the -+ * new one as the tree node. -+ */ -+ q = list_first_entry(&entry->prio_entry, -+ struct kdbus_queue_entry, prio_entry); -+ list_del(&entry->prio_entry); -+ -+ if (queue->msg_prio_highest == &entry->prio_node) -+ queue->msg_prio_highest = &q->prio_node; -+ -+ rb_replace_node(&entry->prio_node, &q->prio_node, -+ &queue->msg_prio_queue); -+ } -+} -+ -+/** -+ * kdbus_queue_entry_alloc() - allocate a queue entry -+ * @conn: The connection that holds the queue -+ * @kmsg: The kmsg object the queue entry should track -+ * @e: Pointer to return the allocated entry -+ * -+ * Allocates a queue entry based on a given kmsg and allocate space for -+ * the message payload and the requested metadata in the connection's pool. -+ * The entry is not actually added to the queue's lists at this point. -+ */ -+int kdbus_queue_entry_alloc(struct kdbus_conn *conn, -+ const struct kdbus_kmsg *kmsg, -+ struct kdbus_queue_entry **e) -+{ -+ struct kdbus_queue_entry *entry; -+ struct kdbus_item *it; -+ u64 msg_size; -+ size_t size; -+ size_t dst_name_len = 0; -+ size_t payloads = 0; -+ size_t fds = 0; -+ size_t meta_off = 0; -+ size_t vec_data; -+ size_t want, have; -+ int ret = 0; -+ -+ entry = kzalloc(sizeof(*entry), GFP_KERNEL); -+ if (!entry) -+ return -ENOMEM; -+ -+ entry->user = -1; -+ -+ /* copy message properties we need for the entry management */ -+ entry->src_id = kmsg->msg.src_id; -+ entry->cookie = kmsg->msg.cookie; -+ -+ /* space for the header */ -+ if (kmsg->msg.src_id == KDBUS_SRC_ID_KERNEL) -+ size = kmsg->msg.size; -+ else -+ size = offsetof(struct kdbus_msg, items); -+ msg_size = size; -+ -+ /* let the receiver know where the message was addressed to */ -+ if (kmsg->dst_name) { -+ dst_name_len = strlen(kmsg->dst_name) + 1; -+ msg_size += KDBUS_ITEM_SIZE(dst_name_len); -+ entry->dst_name_id = kmsg->dst_name_id; -+ } -+ -+ /* space for PAYLOAD items */ -+ if ((kmsg->vecs_count + kmsg->memfds_count) > 0) { -+ payloads = msg_size; -+ msg_size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)) * -+ kmsg->vecs_count; -+ msg_size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd)) * -+ kmsg->memfds_count; -+ } -+ -+ /* space for FDS item */ -+ if (kmsg->fds_count > 0) { -+ entry->fds_fp = kcalloc(kmsg->fds_count, sizeof(struct file *), -+ GFP_KERNEL); -+ if (!entry->fds_fp) -+ return -ENOMEM; -+ -+ fds = msg_size; -+ msg_size += KDBUS_ITEM_SIZE(kmsg->fds_count * sizeof(int)); -+ } -+ -+ /* space for metadata/credential items */ -+ if (kmsg->meta && kmsg->meta->size > 0 && -+ kdbus_meta_ns_eq(kmsg->meta, conn->meta)) { -+ meta_off = msg_size; -+ msg_size += kmsg->meta->size; -+ } -+ -+ /* data starts after the message */ -+ vec_data = KDBUS_ALIGN8(msg_size); -+ -+ /* do not give out more than half of the remaining space */ -+ want = vec_data + kmsg->vecs_size; -+ have = kdbus_pool_remain(conn->pool); -+ if (want < have && want > have / 2) { -+ ret = -EXFULL; -+ goto exit; -+ } -+ -+ /* allocate the needed space in the pool of the receiver */ -+ ret = kdbus_pool_slice_alloc(conn->pool, &entry->slice, want); -+ if (ret < 0) -+ goto exit; -+ -+ /* copy the message header */ -+ ret = kdbus_pool_slice_copy(entry->slice, 0, &kmsg->msg, size); -+ if (ret < 0) -+ goto exit_pool_free; -+ -+ /* update the size */ -+ ret = kdbus_pool_slice_copy(entry->slice, 0, &msg_size, -+ sizeof(kmsg->msg.size)); -+ if (ret < 0) -+ goto exit_pool_free; -+ -+ if (dst_name_len > 0) { -+ char tmp[KDBUS_ITEM_HEADER_SIZE + dst_name_len]; -+ -+ it = (struct kdbus_item *)tmp; -+ it->size = KDBUS_ITEM_HEADER_SIZE + dst_name_len; -+ it->type = KDBUS_ITEM_DST_NAME; -+ memcpy(it->str, kmsg->dst_name, dst_name_len); -+ -+ ret = kdbus_pool_slice_copy(entry->slice, size, it, it->size); -+ if (ret < 0) -+ goto exit_pool_free; -+ } -+ -+ /* add PAYLOAD items */ -+ if (payloads > 0) { -+ ret = kdbus_queue_entry_payload_add(entry, kmsg, -+ payloads, vec_data); -+ if (ret < 0) -+ goto exit_pool_free; -+ } -+ -+ /* add a FDS item; the array content will be updated at RECV time */ -+ if (kmsg->fds_count > 0) { -+ char tmp[KDBUS_ITEM_HEADER_SIZE]; -+ unsigned int i; -+ -+ it = (struct kdbus_item *)tmp; -+ it->type = KDBUS_ITEM_FDS; -+ it->size = KDBUS_ITEM_HEADER_SIZE + -+ (kmsg->fds_count * sizeof(int)); -+ ret = kdbus_pool_slice_copy(entry->slice, fds, -+ it, KDBUS_ITEM_HEADER_SIZE); -+ if (ret < 0) -+ goto exit_pool_free; -+ -+ for (i = 0; i < kmsg->fds_count; i++) { -+ entry->fds_fp[i] = get_file(kmsg->fds[i]); -+ if (!entry->fds_fp[i]) { -+ ret = -EBADF; -+ goto exit_pool_free; -+ } -+ } -+ -+ /* remember the array to update at RECV */ -+ entry->fds = fds + offsetof(struct kdbus_item, fds); -+ entry->fds_count = kmsg->fds_count; -+ } -+ -+ /* append message metadata/credential items */ -+ if (meta_off > 0) { -+ ret = kdbus_pool_slice_copy(entry->slice, meta_off, -+ kmsg->meta->data, -+ kmsg->meta->size); -+ if (ret < 0) -+ goto exit_pool_free; -+ } -+ -+ entry->priority = kmsg->msg.priority; -+ *e = entry; -+ return 0; -+ -+exit_pool_free: -+ kdbus_pool_slice_free(entry->slice); -+exit: -+ kdbus_queue_entry_free(entry); -+ return ret; -+} -+ -+/** -+ * kdbus_queue_entry_free() - free resources of an entry -+ * @entry: The entry to free -+ * -+ * Removes resources allocated by a queue entry, along with the entry itself. -+ * Note that the entry's slice is not freed at this point. -+ */ -+void kdbus_queue_entry_free(struct kdbus_queue_entry *entry) -+{ -+ kdbus_fput_files(entry->memfds_fp, entry->memfds_count); -+ kdbus_fput_files(entry->fds_fp, entry->fds_count); -+ kfree(entry->memfds_fp); -+ kfree(entry->fds_fp); -+ kfree(entry); -+} -+ -+/** -+ * kdbus_queue_init() - initialize data structure related to a queue -+ * @queue: The queue to initialize -+ */ -+void kdbus_queue_init(struct kdbus_queue *queue) -+{ -+ INIT_LIST_HEAD(&queue->msg_list); -+ queue->msg_prio_queue = RB_ROOT; -+} ---- /dev/null -+++ b/drivers/misc/kdbus/queue.h -@@ -0,0 +1,82 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_QUEUE_H -+#define __KDBUS_QUEUE_H -+ -+struct kdbus_queue { -+ size_t msg_count; -+ struct list_head msg_list; -+ struct rb_root msg_prio_queue; -+ struct rb_node *msg_prio_highest; -+}; -+ -+/** -+ * struct kdbus_queue_entry - messages waiting to be read -+ * @entry: Entry in the connection's list -+ * @prio_node: Entry in the priority queue tree -+ * @prio_entry: Queue tree node entry in the list of one priority -+ * @priority: Queueing priority of the message -+ * @slice: Allocated slice in the receiver's pool -+ * @memfds: Arrays of offsets where to update the installed -+ * fd number -+ * @memfds_fp: Array memfd files queued up for this message -+ * @memfds_count: Number of memfds -+ * @fds: Offset to array where to update the installed fd number -+ * @fds_fp: Array of passed files queued up for this message -+ * @fds_count: Number of files -+ * @src_id: The ID of the sender -+ * @cookie: Message cookie, used for replies -+ * @dst_name_id: The sequence number of the name this message is -+ * addressed to, 0 for messages sent to an ID -+ * @reply: The reply block if a reply to this message is expected. -+ * @user: Index in per-user message counter, -1 for unused -+ */ -+struct kdbus_queue_entry { -+ struct list_head entry; -+ struct rb_node prio_node; -+ struct list_head prio_entry; -+ s64 priority; -+ struct kdbus_pool_slice *slice; -+ size_t *memfds; -+ struct file **memfds_fp; -+ unsigned int memfds_count; -+ size_t fds; -+ struct file **fds_fp; -+ unsigned int fds_count; -+ u64 src_id; -+ u64 cookie; -+ u64 dst_name_id; -+ struct kdbus_conn_reply *reply; -+ int user; -+}; -+ -+struct kdbus_kmsg; -+ -+void kdbus_queue_init(struct kdbus_queue *queue); -+ -+int kdbus_queue_entry_alloc(struct kdbus_conn *conn, -+ const struct kdbus_kmsg *kmsg, -+ struct kdbus_queue_entry **e); -+void kdbus_queue_entry_free(struct kdbus_queue_entry *entry); -+ -+void kdbus_queue_entry_add(struct kdbus_queue *queue, -+ struct kdbus_queue_entry *entry); -+void kdbus_queue_entry_remove(struct kdbus_conn *conn, -+ struct kdbus_queue_entry *entry); -+int kdbus_queue_entry_peek(struct kdbus_queue *queue, -+ s64 priority, bool use_priority, -+ struct kdbus_queue_entry **entry); -+int kdbus_queue_entry_install(struct kdbus_queue_entry *entry); -+ -+#endif /* __KDBUS_QUEUE_H */ ---- a/drivers/misc/kdbus/util.h -+++ b/drivers/misc/kdbus/util.h -@@ -17,7 +17,7 @@ - #include <linux/dcache.h> - #include <linux/ioctl.h> - --#include "kdbus.h" -+#include <uapi/linux/kdbus.h> - - /* all exported addresses are 64 bit */ - #define KDBUS_PTR(addr) ((void __user *)(uintptr_t)(addr)) diff --git a/0006-kdbus-add-code-to-gather-metadata.patch b/0006-kdbus-add-code-to-gather-metadata.patch deleted file mode 100644 index 7ea1ba79a83f7b..00000000000000 --- a/0006-kdbus-add-code-to-gather-metadata.patch +++ /dev/null @@ -1,708 +0,0 @@ -From f44205c6d34fb7a5ff08160f63677801e05a2dfa Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 18:58:45 +0200 -Subject: [PATCH 06/12] kdbus: add code to gather metadata - -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. - -See kdbus.txt for more details on which metadata can currently be -attached to messages. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - drivers/misc/kdbus/metadata.c | 626 ++++++++++++++++++++++++++++++++++++++++++ - drivers/misc/kdbus/metadata.h | 51 +++ - 2 files changed, 677 insertions(+) - create mode 100644 drivers/misc/kdbus/metadata.c - create mode 100644 drivers/misc/kdbus/metadata.h - ---- /dev/null -+++ b/drivers/misc/kdbus/metadata.c -@@ -0,0 +1,626 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <linux/audit.h> -+#include <linux/capability.h> -+#include <linux/cgroup.h> -+#include <linux/cred.h> -+#include <linux/file.h> -+#include <linux/init.h> -+#include <linux/mutex.h> -+#include <linux/pid_namespace.h> -+#include <linux/sched.h> -+#include <linux/security.h> -+#include <linux/sizes.h> -+#include <linux/slab.h> -+#include <linux/uaccess.h> -+#include <linux/user_namespace.h> -+#include <linux/version.h> -+ -+#include "connection.h" -+#include "item.h" -+#include "message.h" -+#include "metadata.h" -+#include "names.h" -+ -+/** -+ * kdbus_meta_new() - create new metadata object -+ * @meta: New metadata object -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_meta_new(struct kdbus_meta **meta) -+{ -+ struct kdbus_meta *m; -+ -+ BUG_ON(*meta); -+ -+ m = kzalloc(sizeof(*m), GFP_KERNEL); -+ if (!m) -+ return -ENOMEM; -+ -+ /* -+ * Remember the PID and user namespaces our credentials belong to; -+ * we need to prevent leaking authorization and security-relevant -+ * data across different namespaces. -+ */ -+ m->pid_namespace = get_pid_ns(task_active_pid_ns(current)); -+ m->user_namespace = get_user_ns(current_user_ns()); -+ -+ *meta = m; -+ return 0; -+} -+ -+/** -+ * kdbus_meta_dup() - Duplicate a meta object -+ * -+ * @orig: The meta object to duplicate -+ * @copy: Return pointer for the duplicated object -+ * -+ * Return: 0 on success, -ENOMEM on memory allocation failures. -+ */ -+int kdbus_meta_dup(const struct kdbus_meta *orig, -+ struct kdbus_meta **copy) -+{ -+ struct kdbus_meta *m; -+ -+ BUG_ON(!orig || !copy); -+ -+ m = kmalloc(sizeof(*m), GFP_KERNEL); -+ if (!m) -+ return -ENOMEM; -+ -+ m->data = kmemdup(orig->data, orig->allocated_size, GFP_KERNEL); -+ if (!m->data) { -+ kfree(m); -+ return -ENOMEM; -+ } -+ -+ m->pid_namespace = get_pid_ns(orig->pid_namespace); -+ m->user_namespace = get_user_ns(orig->user_namespace); -+ -+ m->attached = orig->attached; -+ m->allocated_size = orig->allocated_size; -+ m->size = orig->size; -+ -+ *copy = m; -+ return 0; -+} -+ -+/** -+ * kdbus_meta_ns_eq() - check whether the namespaces of two metadata objects -+ * are equal. -+ * @meta_a: Metadata A -+ * @meta_b: Metadata B -+ * -+ * Return: true if the two objects have the same namespaces, false otherwise. -+ */ -+bool kdbus_meta_ns_eq(const struct kdbus_meta *meta_a, -+ const struct kdbus_meta *meta_b) -+{ -+ return (meta_a->pid_namespace == meta_b->pid_namespace && -+ meta_a->user_namespace == meta_b->user_namespace); -+} -+ -+/** -+ * kdbus_meta_free() - release metadata -+ * @meta: Metadata object -+ */ -+void kdbus_meta_free(struct kdbus_meta *meta) -+{ -+ if (!meta) -+ return; -+ -+ put_pid_ns(meta->pid_namespace); -+ put_user_ns(meta->user_namespace); -+ -+ kfree(meta->data); -+ kfree(meta); -+} -+ -+static struct kdbus_item * -+kdbus_meta_append_item(struct kdbus_meta *meta, u64 type, size_t payload_size) -+{ -+ size_t extra_size = KDBUS_ITEM_SIZE(payload_size); -+ struct kdbus_item *item; -+ size_t size; -+ -+ /* get new metadata buffer, pre-allocate at least 512 bytes */ -+ if (!meta->data) { -+ size = roundup_pow_of_two(256 + extra_size); -+ meta->data = kzalloc(size, GFP_KERNEL); -+ if (!meta->data) -+ return ERR_PTR(-ENOMEM); -+ -+ meta->allocated_size = size; -+ } -+ -+ /* double the pre-allocated buffer size if needed */ -+ size = meta->size + extra_size; -+ if (size > meta->allocated_size) { -+ size_t size_diff; -+ struct kdbus_item *data; -+ -+ size = roundup_pow_of_two(size); -+ size_diff = size - meta->allocated_size; -+ data = kmalloc(size, GFP_KERNEL); -+ if (!data) -+ return ERR_PTR(-ENOMEM); -+ -+ memcpy(data, meta->data, meta->size); -+ memset((u8 *)data + meta->allocated_size, 0, size_diff); -+ -+ kfree(meta->data); -+ meta->data = data; -+ meta->allocated_size = size; -+ } -+ -+ /* insert new record */ -+ item = (struct kdbus_item *)((u8 *)meta->data + meta->size); -+ item->type = type; -+ item->size = KDBUS_ITEM_HEADER_SIZE + payload_size; -+ -+ meta->size += extra_size; -+ -+ return item; -+} -+ -+/** -+ * kdbus_meta_append_data() - append given raw data to metadata object -+ * @meta: Metadata object -+ * @type: KDBUS_ITEM_* type -+ * @data: pointer to data to copy from. If it is NULL -+ * then just make space in the metadata buffer. -+ * @len: number of bytes to copy -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_meta_append_data(struct kdbus_meta *meta, u64 type, -+ const void *data, size_t len) -+{ -+ struct kdbus_item *item; -+ -+ if (len == 0) -+ return 0; -+ -+ item = kdbus_meta_append_item(meta, type, len); -+ if (IS_ERR(item)) -+ return PTR_ERR(item); -+ -+ if (data) -+ memcpy(item->data, data, len); -+ -+ return 0; -+} -+ -+static int kdbus_meta_append_str(struct kdbus_meta *meta, u64 type, -+ const char *str) -+{ -+ return kdbus_meta_append_data(meta, type, str, strlen(str) + 1); -+} -+ -+static int kdbus_meta_append_timestamp(struct kdbus_meta *meta, -+ u64 seq) -+{ -+ struct kdbus_item *item; -+ struct timespec ts; -+ -+ item = kdbus_meta_append_item(meta, KDBUS_ITEM_TIMESTAMP, -+ sizeof(struct kdbus_timestamp)); -+ if (IS_ERR(item)) -+ return PTR_ERR(item); -+ -+ if (seq > 0) -+ item->timestamp.seqnum = seq; -+ -+ ktime_get_ts(&ts); -+ item->timestamp.monotonic_ns = timespec_to_ns(&ts); -+ -+ ktime_get_real_ts(&ts); -+ item->timestamp.realtime_ns = timespec_to_ns(&ts); -+ -+ return 0; -+} -+ -+static int kdbus_meta_append_cred(struct kdbus_meta *meta) -+{ -+ struct kdbus_creds creds = { -+ .uid = from_kuid_munged(current_user_ns(), current_uid()), -+ .gid = from_kgid_munged(current_user_ns(), current_gid()), -+ .pid = task_pid_vnr(current), -+ .tid = task_tgid_vnr(current), -+ .starttime = current->start_time, -+ }; -+ -+ return kdbus_meta_append_data(meta, KDBUS_ITEM_CREDS, -+ &creds, sizeof(creds)); -+} -+ -+static int kdbus_meta_append_auxgroups(struct kdbus_meta *meta) -+{ -+ struct group_info *info; -+ struct kdbus_item *item; -+ int i, ret = 0; -+ u64 *gid; -+ -+ info = get_current_groups(); -+ item = kdbus_meta_append_item(meta, KDBUS_ITEM_AUXGROUPS, -+ info->ngroups * sizeof(*gid)); -+ if (IS_ERR(item)) { -+ ret = PTR_ERR(item); -+ goto exit_put_groups; -+ } -+ -+ gid = (u64 *) item->data; -+ -+ for (i = 0; i < info->ngroups; i++) -+ gid[i] = from_kgid_munged(current_user_ns(), GROUP_AT(info, i)); -+ -+exit_put_groups: -+ put_group_info(info); -+ -+ return ret; -+} -+ -+static int kdbus_meta_append_src_names(struct kdbus_meta *meta, -+ struct kdbus_conn *conn) -+{ -+ struct kdbus_name_entry *e; -+ int ret = 0; -+ -+ if (!conn) -+ return 0; -+ -+ mutex_lock(&conn->lock); -+ list_for_each_entry(e, &conn->names_list, conn_entry) { -+ struct kdbus_item *item; -+ size_t len; -+ -+ len = strlen(e->name) + 1; -+ item = kdbus_meta_append_item(meta, KDBUS_ITEM_NAME, -+ sizeof(struct kdbus_name) + len); -+ if (IS_ERR(item)) { -+ ret = PTR_ERR(item); -+ break; -+ } -+ -+ item->name.flags = e->flags; -+ memcpy(item->name.name, e->name, len); -+ } -+ mutex_unlock(&conn->lock); -+ -+ return ret; -+} -+ -+static int kdbus_meta_append_exe(struct kdbus_meta *meta) -+{ -+ struct mm_struct *mm = get_task_mm(current); -+ struct path *exe_path = NULL; -+ char *pathname; -+ int ret = 0; -+ size_t len; -+ char *tmp; -+ -+ if (!mm) -+ return -EFAULT; -+ -+ down_read(&mm->mmap_sem); -+ if (mm->exe_file) { -+ path_get(&mm->exe_file->f_path); -+ exe_path = &mm->exe_file->f_path; -+ } -+ up_read(&mm->mmap_sem); -+ -+ if (!exe_path) -+ goto exit_mmput; -+ -+ tmp = (char *)__get_free_page(GFP_TEMPORARY | __GFP_ZERO); -+ if (!tmp) { -+ ret = -ENOMEM; -+ goto exit_path_put; -+ } -+ -+ pathname = d_path(exe_path, tmp, PAGE_SIZE); -+ if (IS_ERR(pathname)) { -+ ret = PTR_ERR(pathname); -+ goto exit_free_page; -+ } -+ -+ len = tmp + PAGE_SIZE - pathname; -+ ret = kdbus_meta_append_data(meta, KDBUS_ITEM_EXE, pathname, len); -+ -+exit_free_page: -+ free_page((unsigned long) tmp); -+ -+exit_path_put: -+ path_put(exe_path); -+ -+exit_mmput: -+ mmput(mm); -+ -+ return ret; -+} -+ -+static int kdbus_meta_append_cmdline(struct kdbus_meta *meta) -+{ -+ struct mm_struct *mm; -+ int ret = 0; -+ size_t len; -+ char *tmp; -+ -+ tmp = (char *)__get_free_page(GFP_TEMPORARY | __GFP_ZERO); -+ if (!tmp) -+ return -ENOMEM; -+ -+ mm = get_task_mm(current); -+ if (!mm) { -+ ret = -EFAULT; -+ goto exit_free_page; -+ } -+ -+ if (!mm->arg_end) -+ goto exit_mmput; -+ -+ len = mm->arg_end - mm->arg_start; -+ if (len > PAGE_SIZE) -+ len = PAGE_SIZE; -+ -+ ret = copy_from_user(tmp, (const char __user *)mm->arg_start, len); -+ if (ret < 0) -+ goto exit_mmput; -+ -+ ret = kdbus_meta_append_data(meta, KDBUS_ITEM_CMDLINE, tmp, len); -+ -+exit_mmput: -+ mmput(mm); -+ -+exit_free_page: -+ free_page((unsigned long) tmp); -+ -+ return ret; -+} -+ -+static int kdbus_meta_append_caps(struct kdbus_meta *meta) -+{ -+ struct caps { -+ u32 last_cap; -+ struct { -+ u32 caps[_KERNEL_CAPABILITY_U32S]; -+ } set[4]; -+ } caps; -+ unsigned int i; -+ const struct cred *cred = current_cred(); -+ -+ caps.last_cap = CAP_LAST_CAP; -+ -+ for (i = 0; i < _KERNEL_CAPABILITY_U32S; i++) { -+ caps.set[0].caps[i] = cred->cap_inheritable.cap[i]; -+ caps.set[1].caps[i] = cred->cap_permitted.cap[i]; -+ caps.set[2].caps[i] = cred->cap_effective.cap[i]; -+ caps.set[3].caps[i] = cred->cap_bset.cap[i]; -+ } -+ -+ /* clear unused bits */ -+ for (i = 0; i < 4; i++) -+ caps.set[i].caps[CAP_TO_INDEX(CAP_LAST_CAP)] &= -+ CAP_TO_MASK(CAP_LAST_CAP + 1) - 1; -+ -+ return kdbus_meta_append_data(meta, KDBUS_ITEM_CAPS, -+ &caps, sizeof(caps)); -+} -+ -+#ifdef CONFIG_CGROUPS -+static int kdbus_meta_append_cgroup(struct kdbus_meta *meta) -+{ -+ char *buf, *path; -+ int ret; -+ -+ buf = (char *)__get_free_page(GFP_TEMPORARY | __GFP_ZERO); -+ if (!buf) -+ return -ENOMEM; -+ -+ path = task_cgroup_path(current, buf, PAGE_SIZE); -+ -+ if (path) -+ ret = kdbus_meta_append_str(meta, KDBUS_ITEM_CGROUP, path); -+ else -+ ret = -ENAMETOOLONG; -+ -+ free_page((unsigned long) buf); -+ -+ return ret; -+} -+#endif -+ -+#ifdef CONFIG_AUDITSYSCALL -+static int kdbus_meta_append_audit(struct kdbus_meta *meta) -+{ -+ struct kdbus_audit audit; -+ -+ audit.loginuid = from_kuid(current_user_ns(), -+ audit_get_loginuid(current)); -+ audit.sessionid = audit_get_sessionid(current); -+ -+ return kdbus_meta_append_data(meta, KDBUS_ITEM_AUDIT, -+ &audit, sizeof(audit)); -+} -+#endif -+ -+#ifdef CONFIG_SECURITY -+static int kdbus_meta_append_seclabel(struct kdbus_meta *meta) -+{ -+ u32 len, sid; -+ char *label; -+ int ret; -+ -+ security_task_getsecid(current, &sid); -+ ret = security_secid_to_secctx(sid, &label, &len); -+ if (ret == -EOPNOTSUPP) -+ return 0; -+ if (ret < 0) -+ return ret; -+ -+ if (label && len > 0) -+ ret = kdbus_meta_append_data(meta, KDBUS_ITEM_SECLABEL, -+ label, len); -+ security_release_secctx(label, len); -+ -+ return ret; -+} -+#endif -+ -+/** -+ * kdbus_meta_append() - collect metadata from current process -+ * @meta: Metadata object -+ * @conn: Current connection to read names from -+ * @seq: Message sequence number -+ * @which: KDBUS_ATTACH_* flags which typ of data to attach -+ * -+ * Collect the data specified in flags and allocate or extend -+ * the buffer in the metadata object. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_meta_append(struct kdbus_meta *meta, -+ struct kdbus_conn *conn, -+ u64 seq, u64 which) -+{ -+ int ret; -+ u64 mask; -+ -+ /* which metadata is wanted but not yet attached? */ -+ mask = which & ~meta->attached; -+ if (mask == 0) -+ return 0; -+ -+ if (mask & KDBUS_ATTACH_TIMESTAMP) { -+ ret = kdbus_meta_append_timestamp(meta, seq); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_TIMESTAMP; -+ } -+ -+ if (mask & KDBUS_ATTACH_CREDS) { -+ ret = kdbus_meta_append_cred(meta); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_CREDS; -+ } -+ -+ if (mask & KDBUS_ATTACH_AUXGROUPS) { -+ ret = kdbus_meta_append_auxgroups(meta); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_AUXGROUPS; -+ } -+ -+ if (mask & KDBUS_ATTACH_NAMES && conn) { -+ ret = kdbus_meta_append_src_names(meta, conn); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_NAMES; -+ } -+ -+ if (mask & KDBUS_ATTACH_TID_COMM) { -+ char comm[TASK_COMM_LEN]; -+ -+ get_task_comm(comm, current->group_leader); -+ ret = kdbus_meta_append_str(meta, KDBUS_ITEM_TID_COMM, comm); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_TID_COMM; -+ } -+ -+ if (mask & KDBUS_ATTACH_PID_COMM) { -+ char comm[TASK_COMM_LEN]; -+ -+ get_task_comm(comm, current); -+ ret = kdbus_meta_append_str(meta, KDBUS_ITEM_PID_COMM, comm); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_PID_COMM; -+ } -+ -+ if (mask & KDBUS_ATTACH_EXE) { -+ ret = kdbus_meta_append_exe(meta); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_EXE; -+ } -+ -+ if (mask & KDBUS_ATTACH_CMDLINE) { -+ ret = kdbus_meta_append_cmdline(meta); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_CMDLINE; -+ } -+ -+ /* we always return a 4 elements, the element size is 1/4 */ -+ if (mask & KDBUS_ATTACH_CAPS) { -+ ret = kdbus_meta_append_caps(meta); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_CAPS; -+ } -+ -+#ifdef CONFIG_CGROUPS -+ /* attach the path of the one group hierarchy specified for the bus */ -+ if (mask & KDBUS_ATTACH_CGROUP) { -+ ret = kdbus_meta_append_cgroup(meta); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_CGROUP; -+ } -+#endif -+ -+#ifdef CONFIG_AUDITSYSCALL -+ if (mask & KDBUS_ATTACH_AUDIT) { -+ ret = kdbus_meta_append_audit(meta); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_AUDIT; -+ } -+#endif -+ -+#ifdef CONFIG_SECURITY -+ if (mask & KDBUS_ATTACH_SECLABEL) { -+ ret = kdbus_meta_append_seclabel(meta); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_SECLABEL; -+ } -+#endif -+ -+ if ((mask & KDBUS_ATTACH_CONN_NAME) && conn && conn->name) { -+ ret = kdbus_meta_append_str(meta, KDBUS_ITEM_CONN_NAME, -+ conn->name); -+ if (ret < 0) -+ return ret; -+ -+ meta->attached |= KDBUS_ATTACH_CONN_NAME; -+ } -+ -+ return 0; -+} ---- /dev/null -+++ b/drivers/misc/kdbus/metadata.h -@@ -0,0 +1,51 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_METADATA_H -+#define __KDBUS_METADATA_H -+ -+/** -+ * struct kdbus_meta - metadata buffer -+ * @attached: Flags for already attached data -+ * @domain: Domain the metadata belongs to -+ * @data: Allocated buffer -+ * @size: Number of bytes used -+ * @allocated_size: Size of buffer -+ * -+ * Used to collect and store connection metadata in a pre-compiled -+ * buffer containing struct kdbus_item. -+ */ -+struct kdbus_meta { -+ u64 attached; -+ struct pid_namespace *pid_namespace; -+ struct user_namespace *user_namespace; -+ struct kdbus_item *data; -+ size_t size; -+ size_t allocated_size; -+}; -+ -+struct kdbus_conn; -+ -+int kdbus_meta_new(struct kdbus_meta **meta); -+int kdbus_meta_dup(const struct kdbus_meta *orig, -+ struct kdbus_meta **copy); -+int kdbus_meta_append_data(struct kdbus_meta *meta, u64 type, -+ const void *buf, size_t len); -+int kdbus_meta_append(struct kdbus_meta *meta, -+ struct kdbus_conn *conn, -+ u64 seq, -+ u64 which); -+void kdbus_meta_free(struct kdbus_meta *meta); -+bool kdbus_meta_ns_eq(const struct kdbus_meta *meta_a, -+ const struct kdbus_meta *meta_b); -+#endif diff --git a/0007-kdbus-add-code-for-notifications-and-matches.patch b/0007-kdbus-add-code-for-notifications-and-matches.patch deleted file mode 100644 index e01ede6a80d67d..00000000000000 --- a/0007-kdbus-add-code-for-notifications-and-matches.patch +++ /dev/null @@ -1,858 +0,0 @@ -From ae0b0080230d14bde69b2f99af0990d25bbccaf8 Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 18:59:16 +0200 -Subject: [PATCH 07/12] kdbus: add code for notifications and matches - -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. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - drivers/misc/kdbus/match.c | 521 ++++++++++++++++++++++++++++++++++++++++++++ - drivers/misc/kdbus/match.h | 30 ++ - drivers/misc/kdbus/notify.c | 235 +++++++++++++++++++ - drivers/misc/kdbus/notify.h | 28 ++ - 4 files changed, 814 insertions(+) - create mode 100644 drivers/misc/kdbus/match.c - create mode 100644 drivers/misc/kdbus/match.h - create mode 100644 drivers/misc/kdbus/notify.c - create mode 100644 drivers/misc/kdbus/notify.h - ---- /dev/null -+++ b/drivers/misc/kdbus/match.c -@@ -0,0 +1,521 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <linux/device.h> -+#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 "item.h" -+#include "match.h" -+#include "message.h" -+ -+/** -+ * struct kdbus_match_db - message filters -+ * @entries_list: List of matches -+ * @entries_lock: Match data lock -+ * @entries: Number of entries in database -+ */ -+struct kdbus_match_db { -+ struct list_head entries_list; -+ struct mutex entries_lock; -+ unsigned int entries; -+}; -+ -+/** -+ * 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) -+{ -+ 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; -+ -+ 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 -+ * @db: The match database -+ */ -+void kdbus_match_db_free(struct kdbus_match_db *db) -+{ -+ struct kdbus_match_entry *entry, *tmp; -+ -+ mutex_lock(&db->entries_lock); -+ list_for_each_entry_safe(entry, tmp, &db->entries_list, list_entry) -+ kdbus_match_entry_free(entry); -+ mutex_unlock(&db->entries_lock); -+ -+ kfree(db); -+} -+ -+/** -+ * kdbus_match_db_new() - create a new match database -+ * @db: Pointer location for the returned database -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_match_db_new(struct kdbus_match_db **db) -+{ -+ struct kdbus_match_db *d; -+ -+ d = kzalloc(sizeof(*d), GFP_KERNEL); -+ if (!d) -+ return -ENOMEM; -+ -+ mutex_init(&d->entries_lock); -+ INIT_LIST_HEAD(&d->entries_list); -+ -+ *db = d; -+ return 0; -+} -+ -+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->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; -+ -+ /* -+ * 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 == NULL) { -+ /* 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; -+ } -+ } else { -+ /* 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; -+ } -+ } -+ } -+ -+ return true; -+} -+ -+/** -+ * kdbus_match_db_match_kmsg() - match a kmsg object agains the database entries -+ * @db: 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. -+ * -+ * Return: true if there was a matching database entry, false otherwise. -+ */ -+bool kdbus_match_db_match_kmsg(struct kdbus_match_db *db, -+ struct kdbus_conn *conn_src, -+ struct kdbus_kmsg *kmsg) -+{ -+ struct kdbus_match_entry *entry; -+ bool matched = false; -+ -+ mutex_lock(&db->entries_lock); -+ list_for_each_entry(entry, &db->entries_list, list_entry) { -+ matched = kdbus_match_rules(entry, conn_src, kmsg); -+ if (matched) -+ break; -+ } -+ mutex_unlock(&db->entries_lock); -+ -+ return matched; -+} -+ -+static int __kdbus_match_db_remove_unlocked(struct kdbus_match_db *db, -+ uint64_t cookie) -+{ -+ struct kdbus_match_entry *entry, *tmp; -+ bool found = false; -+ -+ list_for_each_entry_safe(entry, tmp, &db->entries_list, list_entry) -+ if (entry->cookie == cookie) { -+ kdbus_match_entry_free(entry); -+ --db->entries; -+ found = true; -+ } -+ -+ return found ? 0 : -ENOENT; -+} -+ -+/** -+ * kdbus_match_db_add() - add an entry to the match database -+ * @conn: The connection that was used in the ioctl call -+ * @cmd: The command as provided by the ioctl call -+ * -+ * This function is used in the context of the KDBUS_CMD_MATCH_ADD ioctl -+ * interface. -+ * -+ * 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 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 errno on failure -+ */ -+int kdbus_match_db_add(struct kdbus_conn *conn, -+ struct kdbus_cmd_match *cmd) -+{ -+ struct kdbus_match_entry *entry = NULL; -+ struct kdbus_match_db *db = conn->match_db; -+ struct kdbus_item *item; -+ LIST_HEAD(list); -+ int ret = 0; -+ -+ lockdep_assert_held(conn); -+ -+ entry = kzalloc(sizeof(*entry), GFP_KERNEL); -+ if (!entry) { -+ ret = -ENOMEM; -+ goto exit_free; -+ } -+ -+ 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; -+ break; -+ } -+ -+ switch (item->type) { -+ case KDBUS_ITEM_BLOOM_MASK: { -+ u64 generations; -+ u64 remainder; -+ -+ generations = div64_u64_rem(size, conn->bus->bloom.size, -+ &remainder); -+ if (size < conn->bus->bloom.size || -+ remainder > 0) { -+ ret = -EDOM; -+ break; -+ } -+ -+ rule->bloom_mask.data = kmemdup(item->data, -+ size, GFP_KERNEL); -+ if (!rule->bloom_mask.data) { -+ ret = -ENOMEM; -+ break; -+ } -+ -+ /* we get an array of n generations of bloom masks */ -+ rule->bloom_mask.generations = generations; -+ -+ break; -+ } -+ case KDBUS_ITEM_NAME: -+ ret = kdbus_item_validate_name(item); -+ if (ret < 0) -+ 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; -+ -+ default: -+ kfree(rule); -+ continue; -+ } -+ -+ if (ret < 0) { -+ kfree(rule); -+ break; -+ } -+ -+ rule->type = item->type; -+ -+ list_add_tail(&rule->rules_entry, &entry->rules_list); -+ } -+ -+ mutex_lock(&db->entries_lock); -+ -+ /* Remove any entry that has the same cookie as the current one. */ -+ if (cmd->flags & KDBUS_MATCH_REPLACE) -+ __kdbus_match_db_remove_unlocked(db, entry->cookie); -+ -+ /* -+ * If the above removal caught any entry, there will be room for the -+ * new one. -+ */ -+ if (++db->entries > KDBUS_MATCH_MAX) { -+ --db->entries; -+ ret = -EMFILE; -+ } -+ if (ret == 0) -+ list_add_tail(&entry->list_entry, &db->entries_list); -+ else -+ kdbus_match_entry_free(entry); -+ mutex_unlock(&db->entries_lock); -+ -+exit_free: -+ return ret; -+} -+ -+/** -+ * kdbus_match_db_remove() - remove an entry from the match database -+ * @conn: The connection that was used in the ioctl call -+ * @cmd: Pointer to the match data structure -+ * -+ * This function is used in the context of the KDBUS_CMD_MATCH_REMOVE -+ * ioctl interface. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_match_db_remove(struct kdbus_conn *conn, -+ struct kdbus_cmd_match *cmd) -+{ -+ struct kdbus_match_db *db = conn->match_db; -+ int ret; -+ -+ lockdep_assert_held(conn); -+ -+ mutex_lock(&db->entries_lock); -+ ret = __kdbus_match_db_remove_unlocked(db, cmd->cookie); -+ mutex_unlock(&db->entries_lock); -+ -+ return ret; -+} ---- /dev/null -+++ b/drivers/misc/kdbus/match.h -@@ -0,0 +1,30 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_MATCH_H -+#define __KDBUS_MATCH_H -+ -+struct kdbus_conn; -+struct kdbus_kmsg; -+struct kdbus_match_db; -+ -+int kdbus_match_db_new(struct kdbus_match_db **db); -+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); -+#endif ---- /dev/null -+++ b/drivers/misc/kdbus/notify.c -@@ -0,0 +1,235 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <linux/device.h> -+#include <linux/fs.h> -+#include <linux/init.h> -+#include <linux/module.h> -+#include <linux/mutex.h> -+#include <linux/spinlock.h> -+#include <linux/sched.h> -+#include <linux/slab.h> -+ -+#include "bus.h" -+#include "connection.h" -+#include "endpoint.h" -+#include "item.h" -+#include "message.h" -+#include "notify.h" -+ -+static int kdbus_notify_reply(struct kdbus_bus *bus, u64 id, -+ u64 cookie, u64 msg_type) -+{ -+ struct kdbus_kmsg *kmsg = NULL; -+ int ret; -+ -+ BUG_ON(id == 0); -+ -+ ret = kdbus_kmsg_new(0, &kmsg); -+ if (ret < 0) -+ return ret; -+ -+ /* -+ * a kernel-generated notification can only contain one -+ * struct kdbus_item, so make a shortcut here for -+ * faster lookup in the match db. -+ */ -+ kmsg->notify_type = msg_type; -+ kmsg->msg.dst_id = id; -+ kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL; -+ kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL; -+ kmsg->msg.cookie_reply = cookie; -+ kmsg->msg.items[0].type = msg_type; -+ -+ spin_lock(&bus->notify_lock); -+ list_add_tail(&kmsg->queue_entry, &bus->notify_list); -+ spin_unlock(&bus->notify_lock); -+ return ret; -+} -+ -+/** -+ * kdbus_notify_reply_timeout() - queue a timeout reply -+ * @bus: Bus which queues the messages -+ * @id: The destination's connection ID -+ * @cookie: The cookie to set in the reply. -+ * -+ * Queues a message that has a KDBUS_ITEM_REPLY_TIMEOUT item attached. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_notify_reply_timeout(struct kdbus_bus *bus, u64 id, u64 cookie) -+{ -+ return kdbus_notify_reply(bus, id, cookie, KDBUS_ITEM_REPLY_TIMEOUT); -+} -+ -+/** -+ * kdbus_notify_reply_dead() - queue a 'dead' reply -+ * @bus: Bus which queues the messages -+ * @id: The destination's connection ID -+ * @cookie: The cookie to set in the reply. -+ * -+ * Queues a message that has a KDBUS_ITEM_REPLY_DEAD item attached. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_notify_reply_dead(struct kdbus_bus *bus, u64 id, u64 cookie) -+{ -+ return kdbus_notify_reply(bus, id, cookie, KDBUS_ITEM_REPLY_DEAD); -+} -+ -+/** -+ * kdbus_notify_name_change() - queue a notification about a name owner change -+ * @bus: Bus which queues the messages -+ * @type: The type if the notification; KDBUS_ITEM_NAME_ADD, -+ * KDBUS_ITEM_NAME_CHANGE or KDBUS_ITEM_NAME_REMOVE -+ * @old_id: The id of the connection that used to own the name -+ * @new_id: The id of the new owner connection -+ * @old_flags: The flags to pass in the KDBUS_ITEM flags field for -+ * the old owner -+ * @new_flags: The flags to pass in the KDBUS_ITEM flags field for -+ * the new owner -+ * @name: The name that was removed or assigned to a new owner -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+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) -+{ -+ struct kdbus_kmsg *kmsg = NULL; -+ size_t name_len, extra_size; -+ int ret; -+ -+ name_len = strlen(name) + 1; -+ extra_size = sizeof(struct kdbus_notify_name_change) + name_len; -+ ret = kdbus_kmsg_new(extra_size, &kmsg); -+ if (ret < 0) -+ return ret; -+ -+ kmsg->msg.dst_id = KDBUS_DST_ID_BROADCAST; -+ kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL; -+ kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL; -+ kmsg->notify_type = type; -+ kmsg->notify_old_id = old_id; -+ kmsg->notify_new_id = new_id; -+ kmsg->msg.items[0].type = type; -+ kmsg->msg.items[0].name_change.old_id.id = old_id; -+ kmsg->msg.items[0].name_change.old_id.flags = old_flags; -+ kmsg->msg.items[0].name_change.new_id.id = new_id; -+ kmsg->msg.items[0].name_change.new_id.flags = new_flags; -+ memcpy(kmsg->msg.items[0].name_change.name, name, name_len); -+ kmsg->notify_name = kmsg->msg.items[0].name_change.name; -+ -+ spin_lock(&bus->notify_lock); -+ list_add_tail(&kmsg->queue_entry, &bus->notify_list); -+ spin_unlock(&bus->notify_lock); -+ return ret; -+} -+ -+/** -+ * kdbus_notify_id_change() - queue a notification about a unique ID change -+ * @bus: Bus which queues the messages -+ * @type: The type if the notification; KDBUS_ITEM_ID_ADD or -+ * KDBUS_ITEM_ID_REMOVE -+ * @id: The id of the connection that was added or removed -+ * @flags: The flags to pass in the KDBUS_ITEM flags field -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_notify_id_change(struct kdbus_bus *bus, u64 type, u64 id, u64 flags) -+{ -+ struct kdbus_kmsg *kmsg = NULL; -+ int ret; -+ -+ ret = kdbus_kmsg_new(sizeof(struct kdbus_notify_id_change), &kmsg); -+ if (ret < 0) -+ return ret; -+ -+ kmsg->msg.dst_id = KDBUS_DST_ID_BROADCAST; -+ kmsg->msg.src_id = KDBUS_SRC_ID_KERNEL; -+ kmsg->msg.payload_type = KDBUS_PAYLOAD_KERNEL; -+ kmsg->notify_type = type; -+ -+ switch (type) { -+ case KDBUS_ITEM_ID_ADD: -+ kmsg->notify_new_id = id; -+ break; -+ -+ case KDBUS_ITEM_ID_REMOVE: -+ kmsg->notify_old_id = id; -+ break; -+ -+ default: -+ BUG(); -+ } -+ -+ kmsg->msg.items[0].type = type; -+ kmsg->msg.items[0].id_change.id = id; -+ kmsg->msg.items[0].id_change.flags = flags; -+ -+ spin_lock(&bus->notify_lock); -+ list_add_tail(&kmsg->queue_entry, &bus->notify_list); -+ spin_unlock(&bus->notify_lock); -+ return ret; -+} -+ -+/** -+ * kdbus_notify_flush() - send a list of collected messages -+ * @bus: Bus which queues the messages -+ * -+ * The list is empty after sending the messages. -+ */ -+void kdbus_notify_flush(struct kdbus_bus *bus) -+{ -+ LIST_HEAD(notify_list); -+ struct kdbus_kmsg *kmsg, *tmp; -+ struct kdbus_ep *ep = NULL; -+ -+ /* bus->ep is only valid as long as the bus is alive */ -+ mutex_lock(&bus->lock); -+ if (!bus->disconnected) -+ ep = kdbus_ep_ref(bus->ep); -+ mutex_unlock(&bus->lock); -+ -+ mutex_lock(&bus->notify_flush_lock); -+ -+ spin_lock(&bus->notify_lock); -+ list_splice_init(&bus->notify_list, ¬ify_list); -+ spin_unlock(&bus->notify_lock); -+ -+ list_for_each_entry_safe(kmsg, tmp, ¬ify_list, queue_entry) { -+ if (ep) -+ kdbus_conn_kmsg_send(ep, NULL, kmsg); -+ list_del(&kmsg->queue_entry); -+ kdbus_kmsg_free(kmsg); -+ } -+ -+ mutex_unlock(&bus->notify_flush_lock); -+ -+ kdbus_ep_unref(ep); -+} -+ -+/** -+ * kdbus_notify_free() - free a list of collected messages -+ * @bus: Bus which queues the messages -+ */ -+void kdbus_notify_free(struct kdbus_bus *bus) -+{ -+ struct kdbus_kmsg *kmsg, *tmp; -+ -+ list_for_each_entry_safe(kmsg, tmp, &bus->notify_list, queue_entry) { -+ list_del(&kmsg->queue_entry); -+ kdbus_kmsg_free(kmsg); -+ } -+} ---- /dev/null -+++ b/drivers/misc/kdbus/notify.h -@@ -0,0 +1,28 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __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 diff --git a/0008-kdbus-add-code-for-buses-domains-and-endpoints.patch b/0008-kdbus-add-code-for-buses-domains-and-endpoints.patch deleted file mode 100644 index 895b86f83e4243..00000000000000 --- a/0008-kdbus-add-code-for-buses-domains-and-endpoints.patch +++ /dev/null @@ -1,1873 +0,0 @@ -From 2d8a87d9f184df9f106a033139f8d71161ffb439 Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 18:59:39 +0200 -Subject: [PATCH 08/12] kdbus: add code for buses, domains and endpoints - -Add the logic to handle the following entities: - -Domain: - A domain is a named object containing a number of buses. A - system container that contains its own init system and - users usually also runs in its own kdbus domain. The - /dev/kdbus/domain/<container-name>/ directory shows up inside - the domain as /dev/kdbus/. Every domain offers its own "control" - device node to create new buses or new sub-domains. Domains have - no connection to each other and cannot see nor talk to each - other. See section 5 for more details. - -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 /dev/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. See section 5 for more details. - -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 Documentation/kdbus.txt for more details. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - drivers/misc/kdbus/bus.c | 450 +++++++++++++++++++++++++++++++++ - drivers/misc/kdbus/bus.h | 107 +++++++ - drivers/misc/kdbus/domain.c | 477 +++++++++++++++++++++++++++++++++++ - drivers/misc/kdbus/domain.h | 105 +++++++ - drivers/misc/kdbus/endpoint.c | 567 ++++++++++++++++++++++++++++++++++++++++++ - drivers/misc/kdbus/endpoint.h | 94 ++++++ - 6 files changed, 1800 insertions(+) - create mode 100644 drivers/misc/kdbus/bus.c - create mode 100644 drivers/misc/kdbus/bus.h - create mode 100644 drivers/misc/kdbus/domain.c - create mode 100644 drivers/misc/kdbus/domain.h - create mode 100644 drivers/misc/kdbus/endpoint.c - create mode 100644 drivers/misc/kdbus/endpoint.h - ---- /dev/null -+++ b/drivers/misc/kdbus/bus.c -@@ -0,0 +1,450 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <linux/device.h> -+#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 "bus.h" -+#include "notify.h" -+#include "connection.h" -+#include "domain.h" -+#include "endpoint.h" -+#include "item.h" -+#include "metadata.h" -+#include "names.h" -+#include "policy.h" -+ -+/** -+ * kdbus_bus_cred_is_privileged() - check whether the given credentials in -+ * combination with the capabilities of the -+ * current thead are privileged on the bus -+ * @bus: The bus to check -+ * @cred: The credentials to match -+ * -+ * Return: true if the credentials are privileged, otherwise false. -+ */ -+bool kdbus_bus_cred_is_privileged(const struct kdbus_bus *bus, -+ const struct cred *cred) -+{ -+ /* Capabilities are *ALWAYS* tested against the current thread, they're -+ * never remembered from conn-credentials. */ -+ if (ns_capable(&init_user_ns, CAP_IPC_OWNER)) -+ return true; -+ -+ return uid_eq(bus->uid_owner, cred->fsuid); -+} -+ -+/** -+ * kdbus_bus_uid_is_privileged() - check whether the current user is a -+ * priviledged bus user -+ * @bus: The bus to check -+ * -+ * Return: true if the current user has CAP_IPC_OWNER capabilities, or -+ * if it has the same UID as the user that created the bus. Otherwise, -+ * false is returned. -+ */ -+bool kdbus_bus_uid_is_privileged(const struct kdbus_bus *bus) -+{ -+ return kdbus_bus_cred_is_privileged(bus, current_cred()); -+} -+ -+/** -+ * 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) -+{ -+ kref_get(&bus->kref); -+ return bus; -+} -+ -+static void __kdbus_bus_free(struct kref *kref) -+{ -+ struct kdbus_bus *bus = container_of(kref, struct kdbus_bus, kref); -+ -+ BUG_ON(!bus->disconnected); -+ BUG_ON(!list_empty(&bus->ep_list)); -+ BUG_ON(!list_empty(&bus->monitors_list)); -+ BUG_ON(!hash_empty(bus->conn_hash)); -+ -+ kdbus_notify_free(bus); -+ atomic_dec(&bus->user->buses); -+ kdbus_domain_user_unref(bus->user); -+ kdbus_name_registry_free(bus->name_registry); -+ kdbus_domain_unref(bus->domain); -+ kdbus_policy_db_clear(&bus->policy_db); -+ kdbus_meta_free(bus->meta); -+ kfree(bus->name); -+ kfree(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) -+ return NULL; -+ -+ kref_put(&bus->kref, __kdbus_bus_free); -+ 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_disconnect() - disconnect a bus -+ * @bus: The kdbus reference -+ * -+ * The passed bus will be disconnected and the associated endpoint will be -+ * unref'ed. -+ */ -+void kdbus_bus_disconnect(struct kdbus_bus *bus) -+{ -+ mutex_lock(&bus->lock); -+ if (bus->disconnected) { -+ mutex_unlock(&bus->lock); -+ return; -+ } -+ bus->disconnected = true; -+ mutex_unlock(&bus->lock); -+ -+ /* disconnect from domain */ -+ mutex_lock(&bus->domain->lock); -+ list_del(&bus->domain_entry); -+ mutex_unlock(&bus->domain->lock); -+ -+ /* disconnect all endpoints attached to this bus */ -+ for (;;) { -+ struct kdbus_ep *ep; -+ -+ mutex_lock(&bus->lock); -+ ep = list_first_entry_or_null(&bus->ep_list, -+ struct kdbus_ep, -+ bus_entry); -+ if (!ep) { -+ mutex_unlock(&bus->lock); -+ break; -+ } -+ -+ /* take reference, release lock, disconnect without lock */ -+ kdbus_ep_ref(ep); -+ mutex_unlock(&bus->lock); -+ -+ kdbus_ep_disconnect(ep); -+ kdbus_ep_unref(ep); -+ } -+ -+ /* drop reference for our "bus" endpoint after we disconnected */ -+ bus->ep = kdbus_ep_unref(bus->ep); -+} -+ -+static struct kdbus_bus *kdbus_bus_find(struct kdbus_domain *domain, -+ const char *name) -+{ -+ struct kdbus_bus *bus = NULL; -+ struct kdbus_bus *b; -+ -+ mutex_lock(&domain->lock); -+ list_for_each_entry(b, &domain->bus_list, domain_entry) { -+ if (strcmp(b->name, name)) -+ continue; -+ -+ bus = kdbus_bus_ref(b); -+ break; -+ } -+ mutex_unlock(&domain->lock); -+ -+ return bus; -+} -+ -+/** -+ * kdbus_cmd_bus_creator_info() - get information on a bus creator -+ * @conn: The querying connection -+ * @cmd_info: The command buffer, as passed in from the ioctl -+ * -+ * Gather information on the creator of the bus @conn is connected to. -+ * -+ * Return: 0 on success, error otherwise. -+ */ -+int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, -+ struct kdbus_cmd_info *cmd_info) -+{ -+ struct kdbus_bus *bus = conn->bus; -+ struct kdbus_pool_slice *slice; -+ struct kdbus_info info = {}; -+ int ret; -+ -+ info.size = sizeof(info) + bus->meta->size; -+ info.id = bus->id; -+ info.flags = bus->bus_flags; -+ -+ if (!kdbus_meta_ns_eq(conn->meta, bus->meta)) -+ return -EPERM; -+ -+ ret = kdbus_pool_slice_alloc(conn->pool, &slice, info.size); -+ if (ret < 0) -+ return ret; -+ -+ ret = kdbus_pool_slice_copy(slice, 0, &info, sizeof(info)); -+ if (ret < 0) -+ goto exit_free_slice; -+ -+ ret = kdbus_pool_slice_copy(slice, sizeof(info), bus->meta->data, -+ bus->meta->size); -+ if (ret < 0) -+ goto exit_free_slice; -+ -+ /* write back the offset */ -+ cmd_info->offset = kdbus_pool_slice_offset(slice); -+ kdbus_pool_slice_flush(slice); -+ kdbus_pool_slice_make_public(slice); -+ -+ return 0; -+ -+exit_free_slice: -+ kdbus_pool_slice_free(slice); -+ return ret; -+} -+ -+/** -+ * kdbus_bus_new() - create a new bus -+ * @domain: The domain to work on -+ * @make: Pointer to a struct kdbus_cmd_make containing the -+ * details for the bus creation -+ * @name: Name of the bus -+ * @bloom: Bloom parameters for this bus -+ * @mode: The access mode for the device node -+ * @uid: The uid of the device node -+ * @gid: The gid of the device node -+ * @bus: Pointer to a reference where the new bus is stored -+ * -+ * This function will allocate a new kdbus_bus and link it to the given -+ * domain. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_bus_new(struct kdbus_domain *domain, -+ const struct kdbus_cmd_make *make, -+ const char *name, -+ const struct kdbus_bloom_parameter *bloom, -+ umode_t mode, kuid_t uid, kgid_t gid, -+ struct kdbus_bus **bus) -+{ -+ struct kdbus_bus *b; -+ char prefix[16]; -+ int ret; -+ -+ BUG_ON(*bus); -+ -+ /* enforce "$UID-" prefix */ -+ snprintf(prefix, sizeof(prefix), "%u-", -+ from_kuid(current_user_ns(), uid)); -+ if (strncmp(name, prefix, strlen(prefix) != 0)) -+ return -EINVAL; -+ -+ b = kdbus_bus_find(domain, name); -+ if (b) { -+ kdbus_bus_unref(b); -+ return -EEXIST; -+ } -+ -+ b = kzalloc(sizeof(*b), GFP_KERNEL); -+ if (!b) -+ return -ENOMEM; -+ -+ kref_init(&b->kref); -+ b->uid_owner = uid; -+ b->bus_flags = make->flags; -+ b->bloom = *bloom; -+ mutex_init(&b->lock); -+ init_rwsem(&b->conn_rwlock); -+ hash_init(b->conn_hash); -+ INIT_LIST_HEAD(&b->ep_list); -+ INIT_LIST_HEAD(&b->monitors_list); -+ INIT_LIST_HEAD(&b->notify_list); -+ spin_lock_init(&b->notify_lock); -+ mutex_init(&b->notify_flush_lock); -+ atomic64_set(&b->conn_seq_last, 0); -+ b->domain = kdbus_domain_ref(domain); -+ kdbus_policy_db_init(&b->policy_db); -+ -+ /* generate unique bus id */ -+ generate_random_uuid(b->id128); -+ -+ /* cache the metadata/credentials of the creator */ -+ ret = kdbus_meta_new(&b->meta); -+ if (ret < 0) -+ return ret; -+ -+ ret = kdbus_meta_append(b->meta, NULL, 0, -+ KDBUS_ATTACH_CREDS | -+ 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_free; -+ -+ b->name = kstrdup(name, GFP_KERNEL); -+ if (!b->name) { -+ ret = -ENOMEM; -+ goto exit_free; -+ } -+ -+ ret = kdbus_name_registry_new(&b->name_registry); -+ if (ret < 0) -+ goto exit_free_name; -+ -+ ret = kdbus_ep_new(b, "bus", mode, uid, gid, false, &b->ep); -+ if (ret < 0) -+ goto exit_free_reg; -+ -+ /* link into domain */ -+ mutex_lock(&domain->lock); -+ if (domain->disconnected) { -+ ret = -ESHUTDOWN; -+ goto exit_unref_user_unlock; -+ } -+ -+ /* account the bus against the user */ -+ ret = kdbus_domain_get_user_unlocked(domain, uid, &b->user); -+ if (ret < 0) -+ goto exit_unref_user_unlock; -+ -+ if (!capable(CAP_IPC_OWNER) && -+ atomic_inc_return(&b->user->buses) > KDBUS_USER_MAX_BUSES) { -+ atomic_dec(&b->user->buses); -+ ret = -EMFILE; -+ goto exit_unref_user_unlock; -+ } -+ -+ b->id = ++domain->bus_seq_last; -+ list_add_tail(&b->domain_entry, &domain->bus_list); -+ mutex_unlock(&domain->lock); -+ -+ *bus = b; -+ return 0; -+ -+exit_unref_user_unlock: -+ mutex_unlock(&domain->lock); -+ kdbus_domain_user_unref(b->user); -+ kdbus_ep_disconnect(b->ep); -+ kdbus_ep_unref(b->ep); -+exit_free_reg: -+ kdbus_name_registry_free(b->name_registry); -+exit_free_name: -+ kfree(b->name); -+exit_free: -+ kdbus_meta_free(b->meta); -+ kdbus_policy_db_clear(&b->policy_db); -+ kdbus_domain_unref(b->domain); -+ kfree(b); -+ return ret; -+} -+ -+/** -+ * kdbus_bus_make_user() - create a kdbus_cmd_make from user-supplied data -+ * @make: Reference to the location where to store the result -+ * @name: Shortcut to the requested name -+ * @bloom: Bloom parameters for this bus -+ * -+ * This function is part of the connection ioctl() interface and will parse -+ * the user-supplied data. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_bus_make_user(const struct kdbus_cmd_make *make, -+ char **name, struct kdbus_bloom_parameter *bloom) -+{ -+ const struct kdbus_item *item; -+ const char *n = NULL; -+ const struct kdbus_bloom_parameter *bl = NULL; -+ -+ KDBUS_ITEMS_FOREACH(item, make->items, KDBUS_ITEMS_SIZE(make, items)) { -+ switch (item->type) { -+ case KDBUS_ITEM_MAKE_NAME: -+ if (n) -+ return -EEXIST; -+ -+ n = item->str; -+ break; -+ -+ case KDBUS_ITEM_BLOOM_PARAMETER: -+ if (bl) -+ return -EEXIST; -+ -+ bl = &item->bloom_parameter; -+ break; -+ } -+ } -+ -+ if (!n || !bl) -+ return -EBADMSG; -+ -+ if (bl->size < 8 || bl->size > KDBUS_BUS_BLOOM_MAX_SIZE) -+ return -EINVAL; -+ if (!KDBUS_IS_ALIGNED8(bl->size)) -+ return -EINVAL; -+ if (bl->n_hash < 1) -+ return -EINVAL; -+ -+ *name = (char *)n; -+ *bloom = *bl; -+ return 0; -+} ---- /dev/null -+++ b/drivers/misc/kdbus/bus.h -@@ -0,0 +1,107 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_BUS_H -+#define __KDBUS_BUS_H -+ -+#include <linux/hashtable.h> -+#include <linux/spinlock.h> -+#include <linux/kref.h> -+#include <linux/rwsem.h> -+ -+#include "policy.h" -+#include "util.h" -+ -+/** -+ * struct kdbus_bus - bus in a domain -+ * @kref: Reference count -+ * @disconnected: Invalidated data -+ * @uid_owner: The uid of the owner of the bus -+ * @domain: Domain of this bus -+ * @name: The bus name -+ * @id: ID of this bus in the domain -+ * @lock: Bus data lock -+ * @ep: Default "bus" endpoint -+ * @ep_seq_last: Last used endpoint id sequence number -+ * @conn_seq_last: Last used connection id sequence number -+ * @ep_list: Endpoints on this bus -+ * @bus_flags: Simple pass-through flags from userspace to userspace -+ * @name_registry: Name registry of this bus -+ * @domain_entry: Entry in domain -+ * @bloom: Bloom parameters -+ * @id128: Unique random 128 bit ID of this bus -+ * @user: Owner of the bus -+ * @policy_db: Policy database for this bus -+ * @notify_list: List of pending kernel-generated messages -+ * @notify_lock: Notification list lock -+ * @notify_flush_lock: Notification flushing lock -+ * @conn_rwlock: Read/Write lock for all lists of child connections -+ * @conn_hash: Map of connection IDs -+ * @monitors_list: Connections that monitor this bus -+ * @meta: Meta information about the bus creator -+ * -+ * A bus provides a "bus" endpoint / device node. -+ * -+ * A bus is created by opening the control node and issuing the -+ * KDBUS_CMD_BUS_MAKE iotcl. Closing this file immediately destroys -+ * the bus. -+ */ -+struct kdbus_bus { -+ struct kref kref; -+ bool disconnected; -+ kuid_t uid_owner; -+ struct kdbus_domain *domain; -+ const char *name; -+ u64 id; -+ struct mutex lock; -+ struct kdbus_ep *ep; -+ u64 ep_seq_last; -+ atomic64_t conn_seq_last; -+ struct list_head ep_list; -+ u64 bus_flags; -+ struct kdbus_name_registry *name_registry; -+ struct list_head domain_entry; -+ struct kdbus_bloom_parameter bloom; -+ u8 id128[16]; -+ struct kdbus_domain_user *user; -+ struct kdbus_policy_db policy_db; -+ struct list_head notify_list; -+ spinlock_t notify_lock; -+ struct mutex notify_flush_lock; -+ -+ struct rw_semaphore conn_rwlock; -+ DECLARE_HASHTABLE(conn_hash, 8); -+ struct list_head monitors_list; -+ -+ struct kdbus_meta *meta; -+}; -+ -+int kdbus_bus_make_user(const struct kdbus_cmd_make *make, -+ char **name, struct kdbus_bloom_parameter *bloom); -+int kdbus_bus_new(struct kdbus_domain *domain, -+ const struct kdbus_cmd_make *make, -+ const char *name, -+ const struct kdbus_bloom_parameter *bloom, -+ umode_t mode, kuid_t uid, kgid_t gid, -+ struct kdbus_bus **bus); -+int kdbus_cmd_bus_creator_info(struct kdbus_conn *conn, -+ struct kdbus_cmd_info *cmd_info); -+struct kdbus_bus *kdbus_bus_ref(struct kdbus_bus *bus); -+struct kdbus_bus *kdbus_bus_unref(struct kdbus_bus *bus); -+void kdbus_bus_disconnect(struct kdbus_bus *bus); -+ -+bool kdbus_bus_cred_is_privileged(const struct kdbus_bus *bus, -+ const struct cred *cred); -+bool kdbus_bus_uid_is_privileged(const struct kdbus_bus *bus); -+struct kdbus_conn *kdbus_bus_find_conn_by_id(struct kdbus_bus *bus, u64 id); -+#endif ---- /dev/null -+++ b/drivers/misc/kdbus/domain.c -@@ -0,0 +1,477 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <linux/device.h> -+#include <linux/fs.h> -+#include <linux/idr.h> -+#include <linux/init.h> -+#include <linux/module.h> -+#include <linux/sched.h> -+#include <linux/sizes.h> -+#include <linux/slab.h> -+#include <linux/uaccess.h> -+ -+#include "bus.h" -+#include "domain.h" -+#include "handle.h" -+#include "item.h" -+#include "limits.h" -+#include "util.h" -+ -+/* previous domain id sequence number */ -+static atomic64_t kdbus_domain_seq_last; -+ -+/* kdbus sysfs subsystem */ -+struct bus_type kdbus_subsys = { -+ .name = KBUILD_MODNAME, -+}; -+ -+/* control nodes are world accessible */ -+static char *kdbus_devnode_control(struct device *dev, umode_t *mode, -+ kuid_t *uid, kgid_t *gid) -+{ -+ struct kdbus_domain *domain = container_of(dev, struct kdbus_domain, -+ dev); -+ -+ if (mode) -+ *mode = domain->mode; -+ -+ return NULL; -+} -+ -+static void kdbus_dev_release(struct device *dev) -+{ -+ kfree(dev); -+} -+ -+static struct device_type kdbus_devtype_control = { -+ .name = "control", -+ .release = kdbus_dev_release, -+ .devnode = kdbus_devnode_control, -+}; -+ -+/** -+ * kdbus_domain_ref() - take a domain reference -+ * @domain: Domain -+ * -+ * Return: the domain itself -+ */ -+struct kdbus_domain *kdbus_domain_ref(struct kdbus_domain *domain) -+{ -+ get_device(&domain->dev); -+ return domain; -+} -+ -+/** -+ * kdbus_domain_disconnect() - invalidate a domain -+ * @domain: Domain -+ */ -+void kdbus_domain_disconnect(struct kdbus_domain *domain) -+{ -+ mutex_lock(&domain->lock); -+ if (domain->disconnected) { -+ mutex_unlock(&domain->lock); -+ return; -+ } -+ domain->disconnected = true; -+ mutex_unlock(&domain->lock); -+ -+ /* disconnect from parent domain */ -+ if (domain->parent) { -+ mutex_lock(&domain->parent->lock); -+ list_del(&domain->domain_entry); -+ mutex_unlock(&domain->parent->lock); -+ } -+ -+ if (device_is_registered(&domain->dev)) -+ device_del(&domain->dev); -+ -+ kdbus_minor_set(domain->dev.devt, KDBUS_MINOR_CONTROL, NULL); -+ -+ /* disconnect all sub-domains */ -+ for (;;) { -+ struct kdbus_domain *dom; -+ -+ mutex_lock(&domain->lock); -+ dom = list_first_entry_or_null(&domain->domain_list, -+ struct kdbus_domain, -+ domain_entry); -+ if (!dom) { -+ mutex_unlock(&domain->lock); -+ break; -+ } -+ -+ /* take reference, release lock, disconnect without lock */ -+ kdbus_domain_ref(dom); -+ mutex_unlock(&domain->lock); -+ -+ kdbus_domain_disconnect(dom); -+ kdbus_domain_unref(dom); -+ } -+ -+ /* disconnect all buses in this domain */ -+ for (;;) { -+ struct kdbus_bus *bus; -+ -+ mutex_lock(&domain->lock); -+ bus = list_first_entry_or_null(&domain->bus_list, -+ struct kdbus_bus, -+ domain_entry); -+ if (!bus) { -+ mutex_unlock(&domain->lock); -+ break; -+ } -+ -+ /* take reference, release lock, disconnect without lock */ -+ kdbus_bus_ref(bus); -+ mutex_unlock(&domain->lock); -+ -+ kdbus_bus_disconnect(bus); -+ kdbus_bus_unref(bus); -+ } -+} -+ -+static void __kdbus_domain_free(struct device *dev) -+{ -+ struct kdbus_domain *domain = container_of(dev, struct kdbus_domain, -+ dev); -+ -+ BUG_ON(!domain->disconnected); -+ BUG_ON(!list_empty(&domain->domain_list)); -+ BUG_ON(!list_empty(&domain->bus_list)); -+ BUG_ON(!hash_empty(domain->user_hash)); -+ -+ kdbus_minor_free(domain->dev.devt); -+ kdbus_domain_unref(domain->parent); -+ idr_destroy(&domain->user_idr); -+ kfree(domain->name); -+ kfree(domain->devpath); -+ kfree(domain); -+} -+ -+/** -+ * kdbus_domain_unref() - drop a domain reference -+ * @domain: Domain -+ * -+ * When the last reference is dropped, the domain internal structure -+ * is freed. -+ * -+ * Return: NULL -+ */ -+struct kdbus_domain *kdbus_domain_unref(struct kdbus_domain *domain) -+{ -+ if (domain) -+ put_device(&domain->dev); -+ return NULL; -+} -+ -+static struct kdbus_domain *kdbus_domain_find(struct kdbus_domain *parent, -+ const char *name) -+{ -+ struct kdbus_domain *n; -+ -+ list_for_each_entry(n, &parent->domain_list, domain_entry) -+ if (!strcmp(n->name, name)) -+ return n; -+ -+ return NULL; -+} -+ -+/** -+ * kdbus_domain_new() - create a new domain -+ * @parent: Parent domain, NULL for initial one -+ * @name: Name of the domain, NULL for the initial one -+ * @mode: The access mode for the "control" device node -+ * @domain: The returned domain -+ * -+ * Return: 0 on success, negative errno on failure -+ */ -+int kdbus_domain_new(struct kdbus_domain *parent, const char *name, -+ umode_t mode, struct kdbus_domain **domain) -+{ -+ struct kdbus_domain *d; -+ int ret; -+ -+ BUG_ON(*domain); -+ -+ if ((parent && !name) || (!parent && name)) -+ return -EINVAL; -+ -+ d = kzalloc(sizeof(*d), GFP_KERNEL); -+ if (!d) -+ return -ENOMEM; -+ -+ d->disconnected = true; -+ INIT_LIST_HEAD(&d->bus_list); -+ INIT_LIST_HEAD(&d->domain_list); -+ d->mode = mode; -+ mutex_init(&d->lock); -+ atomic64_set(&d->msg_seq_last, 0); -+ idr_init(&d->user_idr); -+ -+ device_initialize(&d->dev); -+ d->dev.bus = &kdbus_subsys; -+ d->dev.type = &kdbus_devtype_control; -+ d->dev.release = __kdbus_domain_free; -+ -+ /* compose name and path of base directory in /dev */ -+ if (parent) { -+ d->devpath = kasprintf(GFP_KERNEL, "%s/domain/%s", -+ parent->devpath, name); -+ if (!d->devpath) { -+ ret = -ENOMEM; -+ goto exit_put; -+ } -+ -+ d->name = kstrdup(name, GFP_KERNEL); -+ if (!d->name) { -+ ret = -ENOMEM; -+ goto exit_put; -+ } -+ } else { -+ /* initial domain */ -+ d->devpath = kstrdup(KBUILD_MODNAME, GFP_KERNEL); -+ if (!d->devpath) { -+ ret = -ENOMEM; -+ goto exit_put; -+ } -+ } -+ -+ ret = dev_set_name(&d->dev, "%s/control", d->devpath); -+ if (ret < 0) -+ goto exit_put; -+ -+ ret = kdbus_minor_alloc(KDBUS_MINOR_CONTROL, NULL, &d->dev.devt); -+ if (ret < 0) -+ goto exit_put; -+ -+ if (parent) { -+ /* lock order: parent domain -> domain */ -+ mutex_lock(&parent->lock); -+ -+ if (parent->disconnected) { -+ mutex_unlock(&parent->lock); -+ ret = -ESHUTDOWN; -+ goto exit_put; -+ } -+ -+ if (kdbus_domain_find(parent, name)) { -+ mutex_unlock(&parent->lock); -+ ret = -EEXIST; -+ goto exit_put; -+ } -+ -+ d->parent = kdbus_domain_ref(parent); -+ list_add_tail(&d->domain_entry, &parent->domain_list); -+ } -+ -+ d->id = atomic64_inc_return(&kdbus_domain_seq_last); -+ -+ /* -+ * We have to mark the domain as enabled _before_ running device_add(). -+ * Otherwise, there's a race between UEVENT_ADD (generated by -+ * device_add()) and us enabling the minor. -+ * However, this means user-space can open the minor before we called -+ * device_add(). This is fine, as we never require the device to be -+ * registered, anyway. -+ */ -+ -+ d->disconnected = false; -+ kdbus_minor_set_control(d->dev.devt, d); -+ -+ ret = device_add(&d->dev); -+ -+ if (parent) -+ mutex_unlock(&parent->lock); -+ -+ if (ret < 0) { -+ kdbus_domain_disconnect(d); -+ kdbus_domain_unref(d); -+ return ret; -+ } -+ -+ *domain = d; -+ return 0; -+ -+exit_put: -+ put_device(&d->dev); -+ return ret; -+} -+ -+/** -+ * kdbus_domain_user_assign_id() - allocate ID and assign it to the -+ * domain user -+ * @domain: The domain of the user -+ * @user: The kdbus_domain_user object of the user -+ * -+ * Returns 0 if ID in [0, INT_MAX] is successfully assigned to the -+ * domain user. Negative errno on failure. -+ * -+ * The user index is used in arrays for accounting user quota in -+ * receiver queues. -+ * -+ * Caller must have the domain lock held and must ensure that the -+ * domain was not disconnected. -+ */ -+static int kdbus_domain_user_assign_id(struct kdbus_domain *domain, -+ struct kdbus_domain_user *user) -+{ -+ int ret; -+ -+ /* -+ * Allocate the smallest possible index for this user; used -+ * in arrays for accounting user quota in receiver queues. -+ */ -+ ret = idr_alloc(&domain->user_idr, user, 0, 0, GFP_KERNEL); -+ if (ret < 0) -+ return ret; -+ -+ user->idr = ret; -+ -+ return 0; -+} -+ -+/** -+ * kdbus_domain_get_user_unlocked() - get a kdbus_domain_user object -+ * @domain: The domain of the user -+ * @uid: The uid of the user; INVALID_UID for an -+ * anonymous user like a custom endpoint -+ * @user: Pointer to a reference where the accounted -+ * domain user will be stored. -+ * -+ * Return: 0 on success, negative errno on failure. -+ * -+ * If there is a uid matching, then use the already accounted -+ * kdbus_domain_user, increment its reference counter and -+ * return it in the @user argument. Otherwise allocate a new one, -+ * link it into the domain and return it. -+ */ -+int kdbus_domain_get_user_unlocked(struct kdbus_domain *domain, -+ kuid_t uid, -+ struct kdbus_domain_user **user) -+{ -+ int ret; -+ struct kdbus_domain_user *tmp_user; -+ struct kdbus_domain_user *u = NULL; -+ -+ BUG_ON(!mutex_is_locked(&domain->lock)); -+ -+ /* find uid and reference it */ -+ if (uid_valid(uid)) { -+ hash_for_each_possible(domain->user_hash, tmp_user, -+ hentry, __kuid_val(uid)) { -+ if (!uid_eq(tmp_user->uid, uid)) -+ continue; -+ -+ u = kdbus_domain_user_ref(tmp_user); -+ goto out; -+ } -+ } -+ -+ ret = -ENOMEM; -+ u = kzalloc(sizeof(*u), GFP_KERNEL); -+ if (!u) -+ return ret; -+ -+ kref_init(&u->kref); -+ u->domain = kdbus_domain_ref(domain); -+ u->uid = uid; -+ atomic_set(&u->buses, 0); -+ atomic_set(&u->connections, 0); -+ -+ /* Assign user ID and link into domain */ -+ ret = kdbus_domain_user_assign_id(domain, u); -+ if (ret < 0) -+ goto exit_free; -+ -+ /* UID hash map */ -+ hash_add(domain->user_hash, &u->hentry, __kuid_val(u->uid)); -+ -+out: -+ *user = u; -+ return 0; -+ -+exit_free: -+ kdbus_domain_unref(u->domain); -+ kfree(u); -+ return ret; -+} -+ -+/** -+ * kdbus_domain_get_user() - get a kdbus_domain_user object -+ * @domain: The domain of the user -+ * @uid: The uid of the user; INVALID_UID for an -+ * anonymous user like a custom endpoint -+ * @user: Pointer to a reference where the accounted -+ * domain user will be stored. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_domain_get_user(struct kdbus_domain *domain, -+ kuid_t uid, -+ struct kdbus_domain_user **user) -+{ -+ int ret = -ESHUTDOWN; -+ -+ mutex_lock(&domain->lock); -+ if (!domain->disconnected) -+ ret = kdbus_domain_get_user_unlocked(domain, uid, user); -+ mutex_unlock(&domain->lock); -+ -+ return ret; -+} -+ -+static void __kdbus_domain_user_free(struct kref *kref) -+{ -+ struct kdbus_domain_user *user = -+ container_of(kref, struct kdbus_domain_user, kref); -+ -+ BUG_ON(atomic_read(&user->buses) > 0); -+ BUG_ON(atomic_read(&user->connections) > 0); -+ -+ mutex_lock(&user->domain->lock); -+ idr_remove(&user->domain->user_idr, user->idr); -+ hash_del(&user->hentry); -+ mutex_unlock(&user->domain->lock); -+ -+ kdbus_domain_unref(user->domain); -+ kfree(user); -+} -+ -+/** -+ * kdbus_domain_user_ref() - take a domain user reference -+ * @u: User -+ * -+ * Return: the domain user itself -+ */ -+struct kdbus_domain_user *kdbus_domain_user_ref(struct kdbus_domain_user *u) -+{ -+ kref_get(&u->kref); -+ return u; -+} -+ -+/** -+ * kdbus_domain_user_unref() - drop a domain user eference -+ * @u: User -+ * -+ * When the last reference is dropped, the domain internal structure -+ * is freed. -+ * -+ * Return: NULL -+ */ -+struct kdbus_domain_user *kdbus_domain_user_unref(struct kdbus_domain_user *u) -+{ -+ if (u) -+ kref_put(&u->kref, __kdbus_domain_user_free); -+ return NULL; -+} ---- /dev/null -+++ b/drivers/misc/kdbus/domain.h -@@ -0,0 +1,105 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_DOMAIN_H -+#define __KDBUS_DOMAIN_H -+ -+#include <linux/device.h> -+#include <linux/hashtable.h> -+#include <linux/idr.h> -+ -+/** -+ * struct kdbus_domain - domain for buses -+ * @dev: Underlying device -+ * @disconnected: Invalidated data -+ * @name: Name of the domain -+ * @devpath: /dev base directory path -+ * @parent: Parent domain -+ * @id: Global id of this domain -+ * @mode: Device node access mode -+ * @lock: Domain data lock -+ * @bus_seq_last: Last used bus id sequence number -+ * @msg_seq_last: Last used message id sequence number -+ * @domain_list: List of child domains -+ * @domain_entry: Entry in parent domain -+ * @bus_list: Buses in this domain -+ * @user_hash: Accounting of user resources -+ * @user_idr: Map of all users; smallest possible index -+ * -+ * A domain provides a "control" device node. Every domain has its -+ * own major number for its endpoint device nodes. -+ * -+ * The initial domain is created at initialization time, is unnamed and -+ * stays around for forver. -+ * -+ * A domain is created by opening the "control" device node of the -+ * parent domain and issuing the KDBUS_CMD_DOMAIN_MAKE iotcl. Closing this -+ * file immediately destroys the entire domain. -+ */ -+struct kdbus_domain { -+ struct device dev; -+ bool disconnected; -+ const char *name; -+ const char *devpath; -+ struct kdbus_domain *parent; -+ u64 id; -+ umode_t mode; -+ struct mutex lock; -+ u64 bus_seq_last; -+ atomic64_t msg_seq_last; -+ struct list_head domain_list; -+ struct list_head domain_entry; -+ struct list_head bus_list; -+ DECLARE_HASHTABLE(user_hash, 6); -+ struct idr user_idr; -+}; -+ -+/** -+ * struct kdbus_domain_user - resource accounting for users -+ * @kref: Reference counter -+ * @domain: Domain of the user -+ * @hentry: Entry in domain user map -+ * @idr: Smallest possible index number of all users -+ * @uid: UID of the user -+ * @buses: Number of buses the user has created -+ * @connections: Number of connections the user has created -+ */ -+struct kdbus_domain_user { -+ struct kref kref; -+ struct kdbus_domain *domain; -+ struct hlist_node hentry; -+ unsigned int idr; -+ kuid_t uid; -+ atomic_t buses; -+ atomic_t connections; -+}; -+ -+extern struct bus_type kdbus_subsys; -+ -+struct kdbus_domain *kdbus_domain_ref(struct kdbus_domain *domain); -+struct kdbus_domain *kdbus_domain_unref(struct kdbus_domain *domain); -+void kdbus_domain_disconnect(struct kdbus_domain *domain); -+int kdbus_domain_new(struct kdbus_domain *parent, const char *name, -+ umode_t mode, struct kdbus_domain **domain); -+ -+int kdbus_domain_get_user_unlocked(struct kdbus_domain *domain, -+ kuid_t uid, -+ struct kdbus_domain_user **user); -+ -+int kdbus_domain_get_user(struct kdbus_domain *domain, -+ kuid_t uid, -+ struct kdbus_domain_user **user); -+ -+struct kdbus_domain_user *kdbus_domain_user_ref(struct kdbus_domain_user *u); -+struct kdbus_domain_user *kdbus_domain_user_unref(struct kdbus_domain_user *u); -+#endif ---- /dev/null -+++ b/drivers/misc/kdbus/endpoint.c -@@ -0,0 +1,567 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <linux/device.h> -+#include <linux/fs.h> -+#include <linux/idr.h> -+#include <linux/init.h> -+#include <linux/module.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 "endpoint.h" -+#include "handle.h" -+#include "item.h" -+#include "message.h" -+#include "policy.h" -+ -+/* endpoints are by default owned by the bus owner */ -+static char *kdbus_devnode_ep(struct device *dev, umode_t *mode, -+ kuid_t *uid, kgid_t *gid) -+{ -+ struct kdbus_ep *ep = container_of(dev, struct kdbus_ep, dev); -+ -+ if (mode) -+ *mode = ep->mode; -+ if (uid) -+ *uid = ep->uid; -+ if (gid) -+ *gid = ep->gid; -+ -+ return NULL; -+} -+ -+static void kdbus_dev_release(struct device *dev) -+{ -+ kfree(dev); -+} -+ -+static struct device_type kdbus_devtype_ep = { -+ .name = "endpoint", -+ .release = kdbus_dev_release, -+ .devnode = kdbus_devnode_ep, -+}; -+ -+struct kdbus_ep *kdbus_ep_ref(struct kdbus_ep *ep) -+{ -+ get_device(&ep->dev); -+ return ep; -+} -+ -+/** -+ * kdbus_ep_disconnect() - disconnect an endpoint -+ * @ep: Endpoint -+ */ -+void kdbus_ep_disconnect(struct kdbus_ep *ep) -+{ -+ mutex_lock(&ep->lock); -+ if (ep->disconnected) { -+ mutex_unlock(&ep->lock); -+ return; -+ } -+ ep->disconnected = true; -+ mutex_unlock(&ep->lock); -+ -+ /* disconnect from bus */ -+ mutex_lock(&ep->bus->lock); -+ list_del(&ep->bus_entry); -+ mutex_unlock(&ep->bus->lock); -+ -+ if (device_is_registered(&ep->dev)) -+ device_del(&ep->dev); -+ -+ kdbus_minor_set(ep->dev.devt, KDBUS_MINOR_EP, NULL); -+ -+ /* disconnect all connections to this endpoint */ -+ for (;;) { -+ struct kdbus_conn *conn; -+ -+ mutex_lock(&ep->lock); -+ conn = list_first_entry_or_null(&ep->conn_list, -+ struct kdbus_conn, -+ ep_entry); -+ if (!conn) { -+ mutex_unlock(&ep->lock); -+ break; -+ } -+ -+ /* take reference, release lock, disconnect without lock */ -+ kdbus_conn_ref(conn); -+ mutex_unlock(&ep->lock); -+ -+ kdbus_conn_disconnect(conn, false); -+ kdbus_conn_unref(conn); -+ } -+} -+ -+static void __kdbus_ep_free(struct device *dev) -+{ -+ struct kdbus_ep *ep = container_of(dev, struct kdbus_ep, dev); -+ -+ BUG_ON(!ep->disconnected); -+ BUG_ON(!list_empty(&ep->conn_list)); -+ -+ kdbus_policy_db_clear(&ep->policy_db); -+ kdbus_minor_free(ep->dev.devt); -+ kdbus_bus_unref(ep->bus); -+ kdbus_domain_user_unref(ep->user); -+ kfree(ep->name); -+ kfree(ep); -+} -+ -+struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep) -+{ -+ if (ep) -+ put_device(&ep->dev); -+ return NULL; -+} -+ -+static struct kdbus_ep *kdbus_ep_find(struct kdbus_bus *bus, const char *name) -+{ -+ struct kdbus_ep *e; -+ -+ list_for_each_entry(e, &bus->ep_list, bus_entry) -+ if (!strcmp(e->name, name)) -+ return e; -+ -+ return NULL; -+} -+ -+/** -+ * kdbus_ep_new() - create a new endpoint -+ * @bus: The bus this endpoint will be created for -+ * @name: The name of the endpoint -+ * @mode: The access mode for the device node -+ * @uid: The uid of the device node -+ * @gid: The gid of the device node -+ * @policy: Whether or not the endpoint should have a policy db -+ * @ep: Pointer to a reference where the new endpoint is stored -+ * -+ * This function will create a new enpoint with the given -+ * name and properties for a given bus. -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_ep_new(struct kdbus_bus *bus, const char *name, -+ umode_t mode, kuid_t uid, kgid_t gid, -+ bool policy, struct kdbus_ep **ep) -+{ -+ struct kdbus_ep *e; -+ int ret; -+ -+ e = kzalloc(sizeof(*e), GFP_KERNEL); -+ if (!e) -+ return -ENOMEM; -+ -+ e->disconnected = true; -+ mutex_init(&e->lock); -+ INIT_LIST_HEAD(&e->conn_list); -+ kdbus_policy_db_init(&e->policy_db); -+ e->uid = uid; -+ e->gid = gid; -+ e->mode = mode; -+ e->has_policy = policy; -+ -+ device_initialize(&e->dev); -+ e->dev.bus = &kdbus_subsys; -+ e->dev.type = &kdbus_devtype_ep; -+ e->dev.release = __kdbus_ep_free; -+ -+ e->name = kstrdup(name, GFP_KERNEL); -+ if (!e->name) { -+ ret = -ENOMEM; -+ goto exit_put; -+ } -+ -+ ret = dev_set_name(&e->dev, "%s/%s/%s", -+ bus->domain->devpath, bus->name, name); -+ if (ret < 0) -+ goto exit_put; -+ -+ ret = kdbus_minor_alloc(KDBUS_MINOR_EP, NULL, &e->dev.devt); -+ if (ret < 0) -+ goto exit_put; -+ -+ mutex_lock(&bus->lock); -+ -+ if (bus->disconnected) { -+ mutex_unlock(&bus->lock); -+ ret = -ESHUTDOWN; -+ goto exit_put; -+ } -+ -+ if (kdbus_ep_find(bus, name)) { -+ mutex_unlock(&bus->lock); -+ ret = -EEXIST; -+ goto exit_put; -+ } -+ -+ e->bus = kdbus_bus_ref(bus); -+ list_add_tail(&e->bus_entry, &bus->ep_list); -+ -+ e->id = ++bus->ep_seq_last; -+ -+ /* -+ * Same as with domains, we have to mark it enabled _before_ running -+ * device_add() to avoid messing with state after UEVENT_ADD was sent. -+ */ -+ -+ e->disconnected = false; -+ kdbus_minor_set_ep(e->dev.devt, e); -+ -+ ret = device_add(&e->dev); -+ -+ mutex_unlock(&bus->lock); -+ -+ if (ret < 0) { -+ kdbus_ep_disconnect(e); -+ kdbus_ep_unref(e); -+ return ret; -+ } -+ -+ if (ep) -+ *ep = e; -+ return 0; -+ -+exit_put: -+ put_device(&e->dev); -+ return ret; -+} -+ -+/** -+ * kdbus_ep_policy_set() - set policy for an endpoint -+ * @ep: The endpoint -+ * @items: The kdbus items containing policy information -+ * @items_size: The total length of the items -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_ep_policy_set(struct kdbus_ep *ep, -+ const struct kdbus_item *items, -+ size_t items_size) -+{ -+ return kdbus_policy_set(&ep->policy_db, items, items_size, 0, true, ep); -+} -+ -+/** -+ * kdbus_ep_policy_check_see_access_unlocked() - verify a connection can see -+ * the passed name -+ * @ep: Endpoint to operate on -+ * @conn: Connection that lists names -+ * @name: Name that is tried to be listed -+ * -+ * This verifies that @conn is allowed to see the well-known name @name via the -+ * endpoint @ep. -+ * -+ * Return: 0 if allowed, negative error code if not. -+ */ -+int kdbus_ep_policy_check_see_access_unlocked(struct kdbus_ep *ep, -+ struct kdbus_conn *conn, -+ const char *name) -+{ -+ int ret; -+ -+ /* -+ * Check policy, if the endpoint of the connection has a db. -+ * Note that policy DBs instanciated along with connections -+ * don't have SEE rules, so it's sufficient to check the -+ * endpoint's database. -+ * -+ * The lock for the policy db is held across all calls of -+ * kdbus_name_list_all(), so the entries in both writing -+ * and non-writing runs of kdbus_name_list_write() are the -+ * same. -+ */ -+ -+ if (!ep->has_policy) -+ return 0; -+ -+ ret = kdbus_policy_check_see_access_unlocked(&ep->policy_db, -+ conn, name); -+ -+ /* don't leak hints whether a name exists on a custom endpoint. */ -+ if (ret == -EPERM) -+ return -ENOENT; -+ -+ return ret; -+} -+ -+/** -+ * kdbus_ep_policy_check_see_access() - verify a connection can see -+ * the passed name -+ * @ep: Endpoint to operate on -+ * @conn: Connection that lists names -+ * @name: Name that is tried to be listed -+ * -+ * This verifies that @conn is allowed to see the well-known name @name via the -+ * endpoint @ep. -+ * -+ * Return: 0 if allowed, negative error code if not. -+ */ -+int kdbus_ep_policy_check_see_access(struct kdbus_ep *ep, -+ struct kdbus_conn *conn, -+ const char *name) -+{ -+ int ret; -+ -+ down_read(&ep->policy_db.entries_rwlock); -+ mutex_lock(&conn->lock); -+ -+ ret = kdbus_ep_policy_check_see_access_unlocked(ep, conn, name); -+ -+ mutex_unlock(&conn->lock); -+ up_read(&ep->policy_db.entries_rwlock); -+ -+ return ret; -+} -+ -+/** -+ * kdbus_ep_policy_check_notification() - verify a connection is allowed to see -+ * the name in a notification -+ * @ep: Endpoint to operate on -+ * @conn: Connection connected to the endpoint -+ * @kmsg: The message carrying the notification -+ * -+ * This function verifies that @conn is allowed to see the well-known name -+ * inside a name-change notification contained in @msg via the endpoint @ep. -+ * If @msg is not a notification for name changes, this function does nothing -+ * but return 0. -+ * -+ * Return: 0 if allowed, negative error code if not. -+ */ -+int kdbus_ep_policy_check_notification(struct kdbus_ep *ep, -+ struct kdbus_conn *conn, -+ const struct kdbus_kmsg *kmsg) -+{ -+ int ret = 0; -+ -+ if (kmsg->msg.src_id != KDBUS_SRC_ID_KERNEL || !ep->has_policy) -+ return 0; -+ -+ switch (kmsg->notify_type) { -+ case KDBUS_ITEM_NAME_ADD: -+ case KDBUS_ITEM_NAME_REMOVE: -+ case KDBUS_ITEM_NAME_CHANGE: -+ ret = kdbus_ep_policy_check_see_access(ep, conn, -+ kmsg->notify_name); -+ break; -+ default: -+ break; -+ } -+ -+ return ret; -+} -+ -+/** -+ * kdbus_ep_policy_check_src_names() - check whether a connection's endpoint -+ * is allowed to see any of another -+ * connection's currently owned names -+ * @ep: Endpoint to operate on -+ * @conn_src: Connection that owns the names -+ * @conn_dst: Destination connection to check credentials against -+ * -+ * This function checks whether @ep is allowed to see any of the names -+ * currently owned by @conn_src. -+ * -+ * Return: 0 if allowed, negative error code if not. -+ */ -+int kdbus_ep_policy_check_src_names(struct kdbus_ep *ep, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst) -+{ -+ struct kdbus_name_entry *e; -+ int ret = -ENOENT; -+ -+ if (!ep->has_policy) -+ return 0; -+ -+ down_read(&ep->policy_db.entries_rwlock); -+ mutex_lock(&conn_src->lock); -+ -+ list_for_each_entry(e, &conn_src->names_list, conn_entry) { -+ ret = kdbus_ep_policy_check_see_access_unlocked(ep, conn_dst, -+ e->name); -+ if (ret == 0) -+ break; -+ } -+ -+ mutex_unlock(&conn_src->lock); -+ up_read(&ep->policy_db.entries_rwlock); -+ -+ return ret; -+} -+ -+static int -+kdbus_custom_ep_check_talk_access(struct kdbus_ep *ep, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst) -+{ -+ int ret; -+ -+ if (!ep->has_policy) -+ return 0; -+ -+ /* Custom endpoints have stricter policies */ -+ ret = kdbus_policy_check_talk_access(&ep->policy_db, -+ conn_src, conn_dst); -+ -+ /* -+ * Don't leak hints whether a name exists on a custom -+ * endpoint. -+ */ -+ if (ret == -EPERM) -+ ret = -ENOENT; -+ -+ return ret; -+} -+ -+static bool -+kdbus_ep_has_default_talk_access(struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst) -+{ -+ if (kdbus_bus_cred_is_privileged(conn_src->bus, conn_src->cred)) -+ return true; -+ -+ if (uid_eq(conn_src->cred->fsuid, conn_dst->cred->uid)) -+ return true; -+ -+ return false; -+} -+ -+/** -+ * kdbus_ep_policy_check_talk_access() - verify a connection can talk to the -+ * the passed connection -+ * @ep: Endpoint to operate on -+ * @conn_src: Connection that tries to talk -+ * @conn_dst: Connection that is talked to -+ * -+ * This verifies that @conn_src is allowed to talk to @conn_dst via the -+ * endpoint @ep. -+ * -+ * Return: 0 if allowed, negative error code if not. -+ */ -+int kdbus_ep_policy_check_talk_access(struct kdbus_ep *ep, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst) -+{ -+ int ret; -+ -+ /* First check the custom endpoint with its policies */ -+ ret = kdbus_custom_ep_check_talk_access(ep, conn_src, conn_dst); -+ if (ret < 0) -+ return ret; -+ -+ /* Then check if it satisfies the implicit policies */ -+ if (kdbus_ep_has_default_talk_access(conn_src, conn_dst)) -+ return 0; -+ -+ /* Fallback to the default endpoint policy */ -+ ret = kdbus_policy_check_talk_access(&ep->bus->policy_db, -+ conn_src, conn_dst); -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} -+ -+/** -+ * kdbus_ep_policy_check_broadcast() - verify a connection can send -+ * broadcast messages to the -+ * passed connection -+ * @ep: Endpoint to operate on -+ * @conn_src: Connection that tries to talk -+ * @conn_dst: Connection that is talked to -+ * -+ * This verifies that @conn_src is allowed to send broadcast messages -+ * to @conn_dst via the endpoint @ep. -+ * -+ * Return: 0 if allowed, negative error code if not. -+ */ -+int kdbus_ep_policy_check_broadcast(struct kdbus_ep *ep, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst) -+{ -+ int ret; -+ -+ /* First check the custom endpoint with its policies */ -+ ret = kdbus_custom_ep_check_talk_access(ep, conn_src, conn_dst); -+ if (ret < 0) -+ return ret; -+ -+ /* Then check if it satisfies the implicit policies */ -+ if (kdbus_ep_has_default_talk_access(conn_src, conn_dst)) -+ return 0; -+ -+ /* -+ * If conn_src owns names on the bus, and the conn_dst does -+ * not own any name, then allow conn_src to signal to -+ * conn_dst. Otherwise fallback and perform the bus policy -+ * check on conn_dst. -+ * -+ * This way we allow services to signal on the bus, and we -+ * block broadcasts directed to services that own names and -+ * do not want to receive these messages unless there is a -+ * policy entry to permit it. By this we try to follow the -+ * same logic used for unicat messages. -+ */ -+ if (atomic_read(&conn_src->name_count) > 0 && -+ atomic_read(&conn_dst->name_count) == 0) -+ return 0; -+ -+ /* Fallback to the default endpoint policy */ -+ ret = kdbus_policy_check_talk_access(&ep->bus->policy_db, -+ conn_src, conn_dst); -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} -+ -+/** -+ * kdbus_ep_policy_check_own_access() - verify a connection can own the passed -+ * name -+ * @ep: Endpoint to operate on -+ * @conn: Connection that acquires a name -+ * @name: Name that is about to be acquired -+ * -+ * This verifies that @conn is allowed to acquire the well-known name @name via -+ * the endpoint @ep. -+ * -+ * Return: 0 if allowed, negative error code if not. -+ */ -+int kdbus_ep_policy_check_own_access(struct kdbus_ep *ep, -+ const struct kdbus_conn *conn, -+ const char *name) -+{ -+ int ret; -+ -+ if (ep->has_policy) { -+ ret = kdbus_policy_check_own_access(&ep->policy_db, conn, name); -+ if (ret < 0) -+ return ret; -+ } -+ -+ if (kdbus_bus_cred_is_privileged(conn->bus, conn->cred)) -+ return 0; -+ -+ ret = kdbus_policy_check_own_access(&ep->bus->policy_db, conn, name); -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} ---- /dev/null -+++ b/drivers/misc/kdbus/endpoint.h -@@ -0,0 +1,94 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_ENDPOINT_H -+#define __KDBUS_ENDPOINT_H -+ -+#include <linux/device.h> -+#include "limits.h" -+#include "names.h" -+#include "policy.h" -+#include "util.h" -+ -+/* -+ * struct kdbus_endpoint - enpoint to access a bus -+ * @dev: Device -+ * @bus: Bus behind this endpoint -+ * @name: Name of the endpoint -+ * @id: ID of this endpoint on the bus -+ * @mode: File mode of this endpoint device node -+ * @uid: UID owning this endpoint -+ * @gid: GID owning this endpoint -+ * @conn_list: Connections of this endpoint -+ * @bus_entry: bus' endpoints -+ * @lock: Endpoint data lock -+ * @user: Custom enpoints account against an anonymous user -+ * @policy_db: Uploaded policy -+ * @disconnected: Invalidated data -+ * @has_policy: The policy-db is valid and should be used -+ * -+ * An enpoint offers access to a bus; the default device node name is "bus". -+ * Additional custom endpoints to the same bus can be created and they can -+ * carry their own policies/filters. -+ */ -+struct kdbus_ep { -+ struct device dev; -+ struct kdbus_bus *bus; -+ const char *name; -+ u64 id; -+ umode_t mode; -+ kuid_t uid; -+ kgid_t gid; -+ struct list_head conn_list; -+ struct list_head bus_entry; -+ struct mutex lock; -+ struct kdbus_domain_user *user; -+ struct kdbus_policy_db policy_db; -+ -+ bool disconnected : 1; -+ bool has_policy : 1; -+}; -+ -+int kdbus_ep_new(struct kdbus_bus *bus, const char *name, -+ umode_t mode, kuid_t uid, kgid_t gid, -+ bool policy, struct kdbus_ep **ep); -+struct kdbus_ep *kdbus_ep_ref(struct kdbus_ep *ep); -+struct kdbus_ep *kdbus_ep_unref(struct kdbus_ep *ep); -+void kdbus_ep_disconnect(struct kdbus_ep *ep); -+int kdbus_ep_policy_set(struct kdbus_ep *ep, -+ const struct kdbus_item *items, -+ size_t items_size); -+ -+int kdbus_ep_policy_check_see_access_unlocked(struct kdbus_ep *ep, -+ struct kdbus_conn *conn, -+ const char *name); -+int kdbus_ep_policy_check_see_access(struct kdbus_ep *ep, -+ struct kdbus_conn *conn, -+ const char *name); -+int kdbus_ep_policy_check_notification(struct kdbus_ep *ep, -+ struct kdbus_conn *conn, -+ const struct kdbus_kmsg *kmsg); -+int kdbus_ep_policy_check_src_names(struct kdbus_ep *ep, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst); -+int kdbus_ep_policy_check_talk_access(struct kdbus_ep *ep, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst); -+int kdbus_ep_policy_check_broadcast(struct kdbus_ep *ep, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst); -+int kdbus_ep_policy_check_own_access(struct kdbus_ep *ep, -+ const struct kdbus_conn *conn, -+ const char *name); -+ -+#endif diff --git a/0009-kdbus-add-name-registry-implementation.patch b/0009-kdbus-add-name-registry-implementation.patch deleted file mode 100644 index 4fd3bbf84d1b59..00000000000000 --- a/0009-kdbus-add-name-registry-implementation.patch +++ /dev/null @@ -1,1031 +0,0 @@ -From 3b19fd71a00da0518dd0ec749c5d1970d828a8a4 Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 19:00:00 +0200 -Subject: [PATCH 09/12] kdbus: add name registry implementation - -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. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - drivers/misc/kdbus/names.c | 920 +++++++++++++++++++++++++++++++++++++++++++++ - drivers/misc/kdbus/names.h | 81 +++ - 2 files changed, 1001 insertions(+) - create mode 100644 drivers/misc/kdbus/names.c - create mode 100644 drivers/misc/kdbus/names.h - ---- /dev/null -+++ b/drivers/misc/kdbus/names.c -@@ -0,0 +1,920 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 Linux Foundation -+ * Copyright (C) 2014 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/ctype.h> -+#include <linux/device.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 "bus.h" -+#include "connection.h" -+#include "endpoint.h" -+#include "item.h" -+#include "names.h" -+#include "notify.h" -+#include "policy.h" -+ -+/** -+ * struct kdbus_name_queue_item - a queue item for a name -+ * @conn: The associated connection -+ * @entry: Name entry queuing up for -+ * @entry_entry: List element for the list in @entry -+ * @conn_entry: List element for the list in @conn -+ * @flags: The queuing flags -+ */ -+struct kdbus_name_queue_item { -+ struct kdbus_conn *conn; -+ struct kdbus_name_entry *entry; -+ struct list_head entry_entry; -+ struct list_head conn_entry; -+ u64 flags; -+}; -+ -+static void kdbus_name_entry_free(struct kdbus_name_entry *e) -+{ -+ hash_del(&e->hentry); -+ kfree(e->name); -+ kfree(e); -+} -+ -+/** -+ * kdbus_name_registry_free() - drop a name reg's reference -+ * @reg: The name registry -+ * -+ * Cleanup the name registry's internal structures. -+ */ -+void kdbus_name_registry_free(struct kdbus_name_registry *reg) -+{ -+ struct kdbus_name_entry *e; -+ struct hlist_node *tmp; -+ unsigned int i; -+ -+ hash_for_each_safe(reg->entries_hash, i, tmp, e, hentry) -+ kdbus_name_entry_free(e); -+ -+ kfree(reg); -+} -+ -+/** -+ * kdbus_name_registry_new() - create a new name registry -+ * @reg: The returned name registry -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_name_registry_new(struct kdbus_name_registry **reg) -+{ -+ struct kdbus_name_registry *r; -+ -+ r = kzalloc(sizeof(*r), GFP_KERNEL); -+ if (!r) -+ return -ENOMEM; -+ -+ hash_init(r->entries_hash); -+ init_rwsem(&r->rwlock); -+ -+ *reg = r; -+ return 0; -+} -+ -+static struct kdbus_name_entry * -+kdbus_name_lookup(struct kdbus_name_registry *reg, u32 hash, const char *name) -+{ -+ struct kdbus_name_entry *e; -+ -+ hash_for_each_possible(reg->entries_hash, e, hentry, hash) -+ if (strcmp(e->name, name) == 0) -+ return e; -+ -+ return NULL; -+} -+ -+static void kdbus_name_queue_item_free(struct kdbus_name_queue_item *q) -+{ -+ list_del(&q->entry_entry); -+ list_del(&q->conn_entry); -+ kfree(q); -+} -+ -+/* -+ * The caller needs to hold its own reference, so the connection does not go -+ * away while the entry's reference is dropped under lock. -+ */ -+static void kdbus_name_entry_remove_owner(struct kdbus_name_entry *e) -+{ -+ BUG_ON(!e->conn); -+ BUG_ON(!mutex_is_locked(&e->conn->lock)); -+ -+ atomic_dec(&e->conn->name_count); -+ list_del(&e->conn_entry); -+ e->conn = kdbus_conn_unref(e->conn); -+} -+ -+static void kdbus_name_entry_set_owner(struct kdbus_name_entry *e, -+ struct kdbus_conn *conn) -+{ -+ BUG_ON(e->conn); -+ BUG_ON(!mutex_is_locked(&conn->lock)); -+ -+ e->conn = kdbus_conn_ref(conn); -+ list_add_tail(&e->conn_entry, &e->conn->names_list); -+ atomic_inc(&conn->name_count); -+} -+ -+static int kdbus_name_replace_owner(struct kdbus_name_entry *e, -+ struct kdbus_conn *conn, u64 flags) -+{ -+ struct kdbus_conn *conn_old = kdbus_conn_ref(e->conn); -+ int ret; -+ -+ BUG_ON(conn == conn_old); -+ BUG_ON(!conn_old); -+ -+ /* take lock of both connections in a defined order */ -+ if (conn < conn_old) { -+ mutex_lock(&conn->lock); -+ mutex_lock_nested(&conn_old->lock, 1); -+ } else { -+ mutex_lock(&conn_old->lock); -+ mutex_lock_nested(&conn->lock, 1); -+ } -+ -+ if (!kdbus_conn_active(conn)) { -+ ret = -ECONNRESET; -+ goto exit_unlock; -+ } -+ -+ ret = kdbus_notify_name_change(conn->bus, KDBUS_ITEM_NAME_CHANGE, -+ e->conn->id, conn->id, -+ e->flags, flags, e->name); -+ if (ret < 0) -+ goto exit_unlock; -+ -+ /* hand over name ownership */ -+ kdbus_name_entry_remove_owner(e); -+ kdbus_name_entry_set_owner(e, conn); -+ e->flags = flags; -+ -+exit_unlock: -+ mutex_unlock(&conn_old->lock); -+ mutex_unlock(&conn->lock); -+ -+ kdbus_conn_unref(conn_old); -+ return ret; -+} -+ -+static int kdbus_name_entry_release(struct kdbus_name_entry *e, -+ struct kdbus_bus *bus) -+{ -+ struct kdbus_conn *conn; -+ -+ /* give it to first active waiter in the queue */ -+ while (!list_empty(&e->queue_list)) { -+ struct kdbus_name_queue_item *q; -+ int ret; -+ -+ q = list_first_entry(&e->queue_list, -+ struct kdbus_name_queue_item, -+ entry_entry); -+ -+ ret = kdbus_name_replace_owner(e, q->conn, q->flags); -+ if (ret < 0) -+ continue; -+ -+ kdbus_name_queue_item_free(q); -+ return 0; -+ } -+ -+ /* hand it back to an active activator connection */ -+ if (e->activator && e->activator != e->conn) { -+ u64 flags = KDBUS_NAME_ACTIVATOR; -+ int ret; -+ -+ /* -+ * Move messages still queued in the old connection -+ * and addressed to that name to the new connection. -+ * This allows a race and loss-free name and message -+ * takeover and exit-on-idle services. -+ */ -+ ret = kdbus_conn_move_messages(e->activator, e->conn, -+ e->name_id); -+ if (ret < 0) -+ goto exit_release; -+ -+ return kdbus_name_replace_owner(e, e->activator, flags); -+ } -+ -+exit_release: -+ /* release the name */ -+ kdbus_notify_name_change(e->conn->bus, KDBUS_ITEM_NAME_REMOVE, -+ e->conn->id, 0, -+ e->flags, 0, e->name); -+ -+ conn = kdbus_conn_ref(e->conn); -+ mutex_lock(&conn->lock); -+ kdbus_name_entry_remove_owner(e); -+ mutex_unlock(&conn->lock); -+ kdbus_conn_unref(conn); -+ -+ kdbus_conn_unref(e->activator); -+ kdbus_name_entry_free(e); -+ -+ return 0; -+} -+ -+static int kdbus_name_release(struct kdbus_name_registry *reg, -+ struct kdbus_conn *conn, -+ const char *name) -+{ -+ struct kdbus_name_queue_item *q_tmp, *q; -+ struct kdbus_name_entry *e = NULL; -+ u32 hash; -+ int ret = 0; -+ -+ hash = kdbus_str_hash(name); -+ -+ /* lock order: domain -> bus -> ep -> names -> connection */ -+ mutex_lock(&conn->bus->lock); -+ down_write(®->rwlock); -+ -+ e = kdbus_name_lookup(reg, hash, name); -+ if (!e) { -+ ret = -ESRCH; -+ goto exit_unlock; -+ } -+ -+ /* Is the connection already the real owner of the name? */ -+ if (e->conn == conn) { -+ ret = kdbus_name_entry_release(e, conn->bus); -+ } else { -+ /* -+ * Otherwise, walk the list of queued entries and search -+ * for items for connection. -+ */ -+ -+ /* In case the name belongs to somebody else */ -+ ret = -EADDRINUSE; -+ -+ list_for_each_entry_safe(q, q_tmp, -+ &e->queue_list, -+ entry_entry) { -+ if (q->conn != conn) -+ continue; -+ -+ kdbus_name_queue_item_free(q); -+ ret = 0; -+ break; -+ } -+ } -+ -+ /* -+ * Now that the connection has lost a name, purge all cached policy -+ * entries, so upon the next message, TALK access will be checked -+ * against the names the connection actually owns. -+ */ -+ if (ret == 0) -+ kdbus_conn_purge_policy_cache(conn); -+ -+exit_unlock: -+ up_write(®->rwlock); -+ mutex_unlock(&conn->bus->lock); -+ -+ return ret; -+} -+ -+/** -+ * kdbus_name_remove_by_conn() - remove all name entries of a given connection -+ * @reg: The name registry -+ * @conn: The connection which entries to remove -+ * -+ * This function removes all name entry held by a given connection. -+ */ -+void kdbus_name_remove_by_conn(struct kdbus_name_registry *reg, -+ struct kdbus_conn *conn) -+{ -+ struct kdbus_name_queue_item *q_tmp, *q; -+ struct kdbus_conn *activator = NULL; -+ struct kdbus_name_entry *e_tmp, *e; -+ LIST_HEAD(names_queue_list); -+ LIST_HEAD(names_list); -+ -+ /* lock order: domain -> bus -> ep -> names -> conn */ -+ mutex_lock(&conn->bus->lock); -+ down_write(®->rwlock); -+ -+ mutex_lock(&conn->lock); -+ list_splice_init(&conn->names_list, &names_list); -+ list_splice_init(&conn->names_queue_list, &names_queue_list); -+ mutex_unlock(&conn->lock); -+ -+ if (kdbus_conn_is_activator(conn)) { -+ activator = conn->activator_of->activator; -+ conn->activator_of->activator = NULL; -+ } -+ list_for_each_entry_safe(q, q_tmp, &names_queue_list, conn_entry) -+ kdbus_name_queue_item_free(q); -+ list_for_each_entry_safe(e, e_tmp, &names_list, conn_entry) -+ kdbus_name_entry_release(e, conn->bus); -+ -+ up_write(®->rwlock); -+ mutex_unlock(&conn->bus->lock); -+ -+ kdbus_conn_unref(activator); -+ kdbus_notify_flush(conn->bus); -+} -+ -+/** -+ * kdbus_name_lock() - look up a name in a name registry and lock it -+ * @reg: The name registry -+ * @name: The name to look up -+ * -+ * Search for a name in a given name registry and return it with the -+ * registry-lock held. If the object is not found, the lock is not acquired and -+ * NULL is returned. The caller is responsible of unlocking the name via -+ * kdbus_name_unlock() again. Note that kdbus_name_unlock() can be safely called -+ * with NULL as name. In this case, it's a no-op as nothing was locked. -+ * -+ * The *_lock() + *_unlock() logic is only required for callers that need to -+ * protect their code against concurrent activator/implementor name changes. -+ * Multiple readers can lock names concurrently. However, you may not change -+ * name-ownership while holding a name-lock. -+ * -+ * Return: NULL if name is unknown, otherwise return a pointer to the name -+ * entry with the name-lock held (reader lock only). -+ */ -+struct kdbus_name_entry *kdbus_name_lock(struct kdbus_name_registry *reg, -+ const char *name) -+{ -+ struct kdbus_name_entry *e = NULL; -+ u32 hash = kdbus_str_hash(name); -+ -+ down_read(®->rwlock); -+ e = kdbus_name_lookup(reg, hash, name); -+ if (e) -+ return e; -+ up_read(®->rwlock); -+ -+ return NULL; -+} -+ -+/** -+ * kdbus_name_unlock() - unlock one name in a name registry -+ * @reg: The name registry -+ * @entry: The locked name entry or NULL -+ * -+ * This is the unlock-counterpart of kdbus_name_lock(). It unlocks a name that -+ * was previously successfully locked. You can safely pass NULL as entry and -+ * this will become a no-op. Therefore, it's safe to always call this on the -+ * return-value of kdbus_name_lock(). -+ * -+ * Return: This always returns NULL. -+ */ -+struct kdbus_name_entry *kdbus_name_unlock(struct kdbus_name_registry *reg, -+ struct kdbus_name_entry *entry) -+{ -+ if (entry) { -+ BUG_ON(!rwsem_is_locked(®->rwlock)); -+ up_read(®->rwlock); -+ } -+ -+ return NULL; -+} -+ -+static int kdbus_name_queue_conn(struct kdbus_conn *conn, u64 flags, -+ struct kdbus_name_entry *e) -+{ -+ struct kdbus_name_queue_item *q; -+ -+ q = kzalloc(sizeof(*q), GFP_KERNEL); -+ if (!q) -+ return -ENOMEM; -+ -+ q->conn = conn; -+ q->flags = flags; -+ q->entry = e; -+ -+ list_add_tail(&q->entry_entry, &e->queue_list); -+ list_add_tail(&q->conn_entry, &conn->names_queue_list); -+ -+ return 0; -+} -+ -+/** -+ * 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_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_*) -+ * @entry: Return pointer for the entry (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 on success, negative errno on failure. -+ */ -+int kdbus_name_acquire(struct kdbus_name_registry *reg, -+ struct kdbus_conn *conn, -+ const char *name, u64 *flags, -+ struct kdbus_name_entry **entry) -+{ -+ struct kdbus_name_entry *e = NULL; -+ u32 hash; -+ int ret = 0; -+ -+ /* lock order: domain -> bus -> ep -> names -> conn */ -+ mutex_lock(&conn->bus->lock); -+ down_write(®->rwlock); -+ -+ hash = kdbus_str_hash(name); -+ e = kdbus_name_lookup(reg, hash, name); -+ if (e) { -+ /* connection already owns that name */ -+ if (e->conn == conn) { -+ ret = -EALREADY; -+ goto exit_unlock; -+ } -+ -+ if (kdbus_conn_is_activator(conn)) { -+ /* An activator can only own a single name */ -+ if (conn->activator_of) { -+ if (conn->activator_of == e) -+ ret = -EALREADY; -+ else -+ ret = -EINVAL; -+ } else if (!e->activator && !conn->activator_of) { -+ /* -+ * Activator registers for name that is -+ * already owned -+ */ -+ e->activator = kdbus_conn_ref(conn); -+ conn->activator_of = e; -+ } -+ -+ goto exit_unlock; -+ } -+ -+ /* take over the name of an activator connection */ -+ if (e->flags & KDBUS_NAME_ACTIVATOR) { -+ /* -+ * Take over the messages queued in the activator -+ * connection, the activator itself never reads them. -+ */ -+ ret = kdbus_conn_move_messages(conn, e->activator, 0); -+ if (ret < 0) -+ goto exit_unlock; -+ -+ ret = kdbus_name_replace_owner(e, conn, *flags); -+ goto exit_unlock; -+ } -+ -+ /* take over the name if both parties agree */ -+ if ((*flags & KDBUS_NAME_REPLACE_EXISTING) && -+ (e->flags & KDBUS_NAME_ALLOW_REPLACEMENT)) { -+ /* -+ * Move name back to the queue, in case we take it away -+ * from a connection which asked for queuing. -+ */ -+ if (e->flags & KDBUS_NAME_QUEUE) { -+ ret = kdbus_name_queue_conn(e->conn, -+ e->flags, e); -+ if (ret < 0) -+ goto exit_unlock; -+ } -+ -+ ret = kdbus_name_replace_owner(e, conn, *flags); -+ goto exit_unlock; -+ } -+ -+ /* add it to the queue waiting for the name */ -+ if (*flags & KDBUS_NAME_QUEUE) { -+ ret = kdbus_name_queue_conn(conn, *flags, e); -+ -+ /* tell the caller that we queued it */ -+ *flags |= KDBUS_NAME_IN_QUEUE; -+ -+ goto exit_unlock; -+ } -+ -+ /* the name is busy, return a failure */ -+ ret = -EEXIST; -+ goto exit_unlock; -+ } else { -+ /* An activator can only own a single name */ -+ if (kdbus_conn_is_activator(conn) && -+ conn->activator_of) { -+ ret = -EINVAL; -+ goto exit_unlock; -+ } -+ } -+ -+ /* new name entry */ -+ e = kzalloc(sizeof(*e), GFP_KERNEL); -+ if (!e) { -+ ret = -ENOMEM; -+ goto exit_unlock; -+ } -+ -+ e->name = kstrdup(name, GFP_KERNEL); -+ if (!e->name) { -+ kfree(e); -+ ret = -ENOMEM; -+ goto exit_unlock; -+ } -+ -+ if (kdbus_conn_is_activator(conn)) { -+ e->activator = kdbus_conn_ref(conn); -+ conn->activator_of = e; -+ } -+ -+ e->flags = *flags; -+ INIT_LIST_HEAD(&e->queue_list); -+ e->name_id = ++reg->name_seq_last; -+ -+ mutex_lock(&conn->lock); -+ if (!kdbus_conn_active(conn)) { -+ mutex_unlock(&conn->lock); -+ kfree(e); -+ ret = -ECONNRESET; -+ goto exit_unlock; -+ } -+ hash_add(reg->entries_hash, &e->hentry, hash); -+ kdbus_name_entry_set_owner(e, conn); -+ mutex_unlock(&conn->lock); -+ -+ kdbus_notify_name_change(e->conn->bus, KDBUS_ITEM_NAME_ADD, -+ 0, e->conn->id, -+ 0, e->flags, e->name); -+ -+ if (entry) -+ *entry = e; -+ -+exit_unlock: -+ up_write(®->rwlock); -+ mutex_unlock(&conn->bus->lock); -+ kdbus_notify_flush(conn->bus); -+ -+ return ret; -+} -+ -+/** -+ * kdbus_cmd_name_acquire() - acquire a name from a ioctl command buffer -+ * @reg: The name registry -+ * @conn: The connection to pin this entry to -+ * @cmd: The command as passed in by the ioctl -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_cmd_name_acquire(struct kdbus_name_registry *reg, -+ struct kdbus_conn *conn, -+ struct kdbus_cmd_name *cmd) -+{ -+ struct kdbus_name_entry *e = NULL; -+ const char *name; -+ int ret; -+ -+ ret = kdbus_items_get_str(cmd->items, KDBUS_ITEMS_SIZE(cmd, items), -+ KDBUS_ITEM_NAME, &name); -+ if (ret < 0) -+ return -EINVAL; -+ -+ if (!kdbus_name_is_valid(name, false)) -+ return -EINVAL; -+ -+ /* -+ * Do atomic_inc_return here to reserve our slot, then decrement -+ * it before returning. -+ */ -+ ret = -E2BIG; -+ if (atomic_inc_return(&conn->name_count) > KDBUS_CONN_MAX_NAMES) -+ goto out_dec; -+ -+ ret = kdbus_ep_policy_check_own_access(conn->ep, conn, name); -+ if (ret < 0) -+ goto out_dec; -+ -+ ret = kdbus_name_acquire(reg, conn, name, &cmd->flags, &e); -+ kdbus_notify_flush(conn->bus); -+ -+out_dec: -+ /* Decrement the previous allocated slot */ -+ atomic_dec(&conn->name_count); -+ return ret; -+} -+ -+/** -+ * kdbus_cmd_name_release() - release a name entry from a ioctl command buffer -+ * @reg: The name registry -+ * @conn: The connection that holds the name -+ * @cmd: The command as passed in by the ioctl -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_cmd_name_release(struct kdbus_name_registry *reg, -+ struct kdbus_conn *conn, -+ const struct kdbus_cmd_name *cmd) -+{ -+ int ret; -+ const char *name; -+ -+ ret = kdbus_items_get_str(cmd->items, KDBUS_ITEMS_SIZE(cmd, items), -+ KDBUS_ITEM_NAME, &name); -+ if (ret < 0) -+ return -EINVAL; -+ -+ if (!kdbus_name_is_valid(name, false)) -+ return -EINVAL; -+ -+ ret = kdbus_ep_policy_check_see_access(conn->ep, conn, name); -+ if (ret < 0) -+ return ret; -+ -+ ret = kdbus_name_release(reg, conn, name); -+ -+ kdbus_notify_flush(conn->bus); -+ return ret; -+} -+ -+static int kdbus_name_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) -+{ -+ const size_t len = sizeof(struct kdbus_name_info); -+ size_t p = *pos; -+ size_t nlen = 0; -+ -+ if (e) { -+ nlen = strlen(e->name) + 1; -+ -+ if (kdbus_ep_policy_check_see_access_unlocked(conn->ep, conn, -+ e->name) < 0) -+ return 0; -+ } -+ -+ if (write) { -+ int ret; -+ struct kdbus_name_info info = { -+ .size = len, -+ .owner_id = c->id, -+ .flags = e ? e->flags : 0, -+ .conn_flags = c->flags, -+ }; -+ -+ if (nlen) -+ info.size += KDBUS_ITEM_SIZE(nlen); -+ -+ /* write record */ -+ ret = kdbus_pool_slice_copy(slice, p, &info, len); -+ if (ret < 0) -+ return ret; -+ p += len; -+ -+ /* append name */ -+ if (e) { -+ struct kdbus_item_header { -+ __u64 size; -+ __u64 type; -+ } h; -+ -+ h.size = KDBUS_ITEM_HEADER_SIZE + nlen; -+ h.type = KDBUS_ITEM_NAME; -+ -+ ret = kdbus_pool_slice_copy(slice, p, &h, sizeof(h)); -+ if (ret < 0) -+ return ret; -+ -+ p += sizeof(h); -+ -+ ret = kdbus_pool_slice_copy(slice, p, e->name, nlen); -+ if (ret < 0) -+ return ret; -+ -+ p += KDBUS_ALIGN8(nlen); -+ } -+ } else { -+ p += len; -+ if (nlen) -+ p += KDBUS_ITEM_SIZE(nlen); -+ } -+ -+ *pos = p; -+ return 0; -+} -+ -+static int kdbus_name_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->bus->conn_hash, i, c, hentry) { -+ bool added = false; -+ -+ /* skip activators */ -+ if (!(flags & KDBUS_NAME_LIST_ACTIVATORS) && -+ kdbus_conn_is_activator(c)) -+ continue; -+ -+ /* all names the connection owns */ -+ if (flags & (KDBUS_NAME_LIST_NAMES | -+ KDBUS_NAME_LIST_ACTIVATORS)) { -+ struct kdbus_name_entry *e; -+ -+ mutex_lock(&c->lock); -+ list_for_each_entry(e, &c->names_list, conn_entry) { -+ struct kdbus_conn *a = e->activator; -+ -+ if ((flags & KDBUS_NAME_LIST_ACTIVATORS) && -+ a && a != c) { -+ ret = kdbus_name_list_write(conn, a, -+ slice, &p, e, write); -+ if (ret < 0) { -+ mutex_unlock(&c->lock); -+ return ret; -+ } -+ -+ added = true; -+ } -+ -+ if (flags & KDBUS_NAME_LIST_NAMES || -+ kdbus_conn_is_activator(c)) { -+ ret = kdbus_name_list_write(conn, c, -+ slice, &p, e, write); -+ if (ret < 0) { -+ mutex_unlock(&c->lock); -+ return ret; -+ } -+ -+ added = true; -+ } -+ } -+ mutex_unlock(&c->lock); -+ } -+ -+ /* queue of names the connection is currently waiting for */ -+ if (flags & KDBUS_NAME_LIST_QUEUED) { -+ struct kdbus_name_queue_item *q; -+ -+ mutex_lock(&c->lock); -+ list_for_each_entry(q, &c->names_queue_list, -+ conn_entry) { -+ ret = kdbus_name_list_write(conn, c, -+ slice, &p, q->entry, write); -+ if (ret < 0) { -+ mutex_unlock(&c->lock); -+ return ret; -+ } -+ -+ added = true; -+ } -+ mutex_unlock(&c->lock); -+ } -+ -+ /* nothing added so far, just add the unique ID */ -+ if (!added && flags & KDBUS_NAME_LIST_UNIQUE) { -+ ret = kdbus_name_list_write(conn, c, -+ slice, &p, NULL, write); -+ if (ret < 0) -+ return ret; -+ } -+ } -+ -+ *pos = p; -+ return 0; -+} -+ -+/** -+ * kdbus_cmd_name_list() - list names of a connection -+ * @reg: The name registry -+ * @conn: The connection holding the name entries -+ * @cmd: The command as passed in by the ioctl -+ * -+ * Return: 0 on success, negative errno on failure. -+ */ -+int kdbus_cmd_name_list(struct kdbus_name_registry *reg, -+ struct kdbus_conn *conn, -+ struct kdbus_cmd_name_list *cmd) -+{ -+ struct kdbus_policy_db *policy_db; -+ struct kdbus_name_list list = {}; -+ struct kdbus_pool_slice *slice; -+ size_t pos; -+ int ret; -+ -+ policy_db = &conn->ep->policy_db; -+ -+ /* lock order: domain -> bus -> ep -> names -> conn */ -+ down_read(®->rwlock); -+ down_read(&conn->bus->conn_rwlock); -+ down_read(&policy_db->entries_rwlock); -+ -+ /* size of header + records */ -+ pos = sizeof(struct kdbus_name_list); -+ ret = kdbus_name_list_all(conn, cmd->flags, NULL, &pos, false); -+ if (ret < 0) -+ goto exit_unlock; -+ -+ ret = kdbus_pool_slice_alloc(conn->pool, &slice, pos); -+ if (ret < 0) -+ goto exit_unlock; -+ -+ /* copy the header, specifying the overall size */ -+ list.size = pos; -+ ret = kdbus_pool_slice_copy(slice, 0, &list, sizeof(list)); -+ if (ret < 0) -+ goto exit_pool_free; -+ -+ /* copy the records */ -+ pos = sizeof(struct kdbus_name_list); -+ ret = kdbus_name_list_all(conn, cmd->flags, slice, &pos, true); -+ if (ret < 0) -+ goto exit_pool_free; -+ -+ cmd->offset = kdbus_pool_slice_offset(slice); -+ kdbus_pool_slice_flush(slice); -+ kdbus_pool_slice_make_public(slice); -+ -+exit_pool_free: -+ if (ret < 0) -+ kdbus_pool_slice_free(slice); -+exit_unlock: -+ up_read(&policy_db->entries_rwlock); -+ up_read(&conn->bus->conn_rwlock); -+ up_read(®->rwlock); -+ return ret; -+} ---- /dev/null -+++ b/drivers/misc/kdbus/names.h -@@ -0,0 +1,81 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_NAMES_H -+#define __KDBUS_NAMES_H -+ -+#include <linux/hashtable.h> -+#include <linux/rwsem.h> -+ -+/** -+ * struct kdbus_name_registry - names registered for a bus -+ * @entries_hash: Map of entries -+ * @lock: Registry data lock -+ * @name_seq_last: Last used sequence number to assign to a name entry -+ */ -+struct kdbus_name_registry { -+ DECLARE_HASHTABLE(entries_hash, 8); -+ struct rw_semaphore rwlock; -+ u64 name_seq_last; -+}; -+ -+/** -+ * struct kdbus_name_entry - well-know name entry -+ * @name: The well-known name -+ * @name_id: Sequence number of name entry to be able to uniquely -+ * identify a name over its registration lifetime -+ * @flags: KDBUS_NAME_* flags -+ * @queue_list: List of queued waiters for the well-known name -+ * @conn_entry: Entry in connection -+ * @hentry: Entry in registry map -+ * @conn: Connection owning the name -+ * @activator: Connection of the activator queuing incoming messages -+ */ -+struct kdbus_name_entry { -+ char *name; -+ u64 name_id; -+ u64 flags; -+ struct list_head queue_list; -+ struct list_head conn_entry; -+ struct hlist_node hentry; -+ struct kdbus_conn *conn; -+ struct kdbus_conn *activator; -+}; -+ -+int kdbus_name_registry_new(struct kdbus_name_registry **reg); -+void kdbus_name_registry_free(struct kdbus_name_registry *reg); -+ -+int kdbus_name_acquire(struct kdbus_name_registry *reg, -+ struct kdbus_conn *conn, -+ const char *name, u64 *flags, -+ struct kdbus_name_entry **entry); -+int kdbus_cmd_name_acquire(struct kdbus_name_registry *reg, -+ struct kdbus_conn *conn, -+ struct kdbus_cmd_name *cmd); -+int kdbus_cmd_name_release(struct kdbus_name_registry *reg, -+ struct kdbus_conn *conn, -+ const struct kdbus_cmd_name *cmd); -+int kdbus_cmd_name_list(struct kdbus_name_registry *reg, -+ struct kdbus_conn *conn, -+ struct kdbus_cmd_name_list *cmd); -+ -+struct kdbus_name_entry *kdbus_name_lock(struct kdbus_name_registry *reg, -+ const char *name); -+struct kdbus_name_entry *kdbus_name_unlock(struct kdbus_name_registry *reg, -+ struct kdbus_name_entry *entry); -+ -+void kdbus_name_remove_by_conn(struct kdbus_name_registry *reg, -+ struct kdbus_conn *conn); -+ -+bool kdbus_name_is_valid(const char *p, bool allow_wildcard); -+#endif diff --git a/0010-kdbus-add-policy-database-implementation.patch b/0010-kdbus-add-policy-database-implementation.patch deleted file mode 100644 index c5971278b3215b..00000000000000 --- a/0010-kdbus-add-policy-database-implementation.patch +++ /dev/null @@ -1,715 +0,0 @@ -From d23d4333e3672ca553cbf37fcd5aeb2ec850b216 Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 19:00:43 +0200 -Subject: [PATCH 10/12] kdbus: add policy database implementation - -This patch adds the policy database implementation. - -A policy databases restrict 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 Documentation/kdbus.txt for more details. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - drivers/misc/kdbus/policy.c | 617 ++++++++++++++++++++++++++++++++++++++++++++ - drivers/misc/kdbus/policy.h | 60 ++++ - 2 files changed, 677 insertions(+) - create mode 100644 drivers/misc/kdbus/policy.c - create mode 100644 drivers/misc/kdbus/policy.h - ---- /dev/null -+++ b/drivers/misc/kdbus/policy.c -@@ -0,0 +1,617 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 Linux Foundation -+ * Copyright (C) 2014 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/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_cache_entry - a cached entry -+ * @conn_a: Connection A -+ * @conn_b: Connection B -+ * @owner: Owner of policy-entry that produced this cache-entry -+ * @hentry: The hash table entry for the database's entries_hash -+ */ -+struct kdbus_policy_db_cache_entry { -+ struct kdbus_conn *conn_a; -+ struct kdbus_conn *conn_b; -+ const void *owner; -+ struct hlist_node hentry; -+}; -+ -+/** -+ * 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 const struct kdbus_policy_db_entry * -+kdbus_policy_lookup(struct kdbus_policy_db *db, -+ const char *name, u32 hash, bool wildcard) -+{ -+ struct kdbus_policy_db_entry *e, *found = NULL; -+ -+ hash_for_each_possible(db->entries_hash, e, hentry, hash) -+ if (strcmp(e->name, name) == 0 && !e->wildcard) -+ return e; -+ -+ if (wildcard) { -+ const char *tmp; -+ char *dot; -+ -+ tmp = kstrdup(name, GFP_KERNEL); -+ if (!tmp) -+ return NULL; -+ -+ dot = strrchr(tmp, '.'); -+ if (!dot) -+ goto exit_free; -+ -+ *dot = '\0'; -+ hash = kdbus_str_hash(tmp); -+ -+ hash_for_each_possible(db->entries_hash, e, hentry, hash) -+ if (strcmp(e->name, tmp) == 0 && e->wildcard) { -+ found = e; -+ /* never "break;" in hash_for_each() */ -+ goto exit_free; -+ } -+ -+exit_free: -+ kfree(tmp); -+ } -+ -+ return found; -+} -+ -+/** -+ * 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_cache_entry *ce; -+ struct kdbus_policy_db_entry *e; -+ struct hlist_node *tmp; -+ unsigned int i; -+ -+ BUG_ON(!db); -+ -+ /* 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); -+ -+ /* purge cache */ -+ mutex_lock(&db->cache_lock); -+ hash_for_each_safe(db->talk_access_hash, i, tmp, ce, hentry) { -+ hash_del(&ce->hentry); -+ kfree(ce); -+ } -+ mutex_unlock(&db->cache_lock); -+} -+ -+/** -+ * 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); -+ hash_init(db->talk_access_hash); -+ init_rwsem(&db->entries_rwlock); -+ mutex_init(&db->cache_lock); -+} -+ -+static int kdbus_policy_check_access(const struct kdbus_policy_db_entry *e, -+ const struct cred *cred, -+ unsigned int access) -+{ -+ struct kdbus_policy_db_entry_access *a; -+ struct group_info *group_info; -+ int i; -+ -+ if (!e) -+ return -EPERM; -+ -+ group_info = cred->group_info; -+ -+ list_for_each_entry(a, &e->access_list, list) { -+ if (a->access >= access) { -+ switch (a->type) { -+ case KDBUS_POLICY_ACCESS_USER: -+ if (uid_eq(cred->uid, a->uid)) -+ return 0; -+ break; -+ case KDBUS_POLICY_ACCESS_GROUP: -+ if (gid_eq(cred->gid, a->gid)) -+ return 0; -+ -+ for (i = 0; i < group_info->ngroups; i++) { -+ kgid_t gid = GROUP_AT(group_info, i); -+ -+ if (gid_eq(gid, a->gid)) -+ return 0; -+ } -+ -+ break; -+ case KDBUS_POLICY_ACCESS_WORLD: -+ return 0; -+ } -+ } -+ } -+ -+ return -EPERM; -+} -+ -+/** -+ * kdbus_policy_check_own_access() - check whether a connection is allowed -+ * to own a name -+ * @db: The policy database -+ * @conn: The connection to check -+ * @name: The name to check -+ * -+ * Return: 0 if the connection is allowed to own the name, -EPERM otherwise -+ */ -+int kdbus_policy_check_own_access(struct kdbus_policy_db *db, -+ const struct kdbus_conn *conn, -+ const char *name) -+{ -+ const struct kdbus_policy_db_entry *e; -+ int ret; -+ -+ down_read(&db->entries_rwlock); -+ e = kdbus_policy_lookup(db, name, kdbus_str_hash(name), true); -+ ret = kdbus_policy_check_access(e, conn->cred, KDBUS_POLICY_OWN); -+ up_read(&db->entries_rwlock); -+ -+ return ret; -+} -+ -+/** -+ * kdbus_policy_check_talk_access() - check if one connection is allowed -+ * to send a message to another connection -+ * @db: The policy database -+ * @conn_src: The source connection -+ * @conn_dst: The destination connection -+ * -+ * Return: 0 if access is granted, -EPERM if not, negative errno on failure -+ */ -+int kdbus_policy_check_talk_access(struct kdbus_policy_db *db, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst) -+{ -+ struct kdbus_policy_db_cache_entry *ce; -+ struct kdbus_name_entry *name_entry; -+ unsigned int hash = 0; -+ const void *owner; -+ int ret; -+ -+ /* -+ * If there was a positive match for these two connections before, -+ * there's an entry in the hash table for them. -+ */ -+ hash ^= hash_ptr(conn_src, KDBUS_POLICY_HASH_SIZE); -+ hash ^= hash_ptr(conn_dst, KDBUS_POLICY_HASH_SIZE); -+ -+ mutex_lock(&db->cache_lock); -+ hash_for_each_possible(db->talk_access_hash, ce, hentry, hash) -+ if (ce->conn_a == conn_src && ce->conn_b == conn_dst) { -+ mutex_unlock(&db->cache_lock); -+ return 0; -+ } -+ mutex_unlock(&db->cache_lock); -+ -+ /* -+ * Otherwise, walk the connection list and store a hash-table entry if -+ * send access is granted. -+ */ -+ -+ down_read(&db->entries_rwlock); -+ -+ ret = -EPERM; -+ mutex_lock(&conn_dst->lock); -+ list_for_each_entry(name_entry, &conn_dst->names_list, conn_entry) { -+ u32 hash = kdbus_str_hash(name_entry->name); -+ const struct kdbus_policy_db_entry *e; -+ -+ e = kdbus_policy_lookup(db, name_entry->name, hash, true); -+ if (kdbus_policy_check_access(e, conn_src->cred, -+ KDBUS_POLICY_TALK) == 0) { -+ owner = e->owner; -+ ret = 0; -+ break; -+ } -+ } -+ mutex_unlock(&conn_dst->lock); -+ -+ if (ret >= 0) { -+ ret = -ENOMEM; -+ ce = kmalloc(sizeof(*ce), GFP_KERNEL); -+ if (ce) { -+ ce->conn_a = conn_src; -+ ce->conn_b = conn_dst; -+ ce->owner = owner; -+ INIT_HLIST_NODE(&ce->hentry); -+ -+ mutex_lock(&db->cache_lock); -+ hash_add(db->talk_access_hash, &ce->hentry, hash); -+ mutex_unlock(&db->cache_lock); -+ -+ ret = 0; -+ } -+ } -+ -+ up_read(&db->entries_rwlock); -+ -+ return ret; -+} -+ -+/** -+ * kdbus_policy_check_see_access_unlocked() - Check whether a connection is -+ * allowed to see a given name -+ * @db: The policy database -+ * @conn: The connection performing the lookup -+ * @name: The name -+ * -+ * Return: 0 if permission to see the name is granted, -EPERM otherwise -+ */ -+int kdbus_policy_check_see_access_unlocked(struct kdbus_policy_db *db, -+ struct kdbus_conn *conn, -+ const char *name) -+{ -+ const struct kdbus_policy_db_entry *e; -+ -+ e = kdbus_policy_lookup(db, name, kdbus_str_hash(name), true); -+ return kdbus_policy_check_access(e, conn->cred, KDBUS_POLICY_SEE); -+} -+ -+static void __kdbus_policy_remove_owner_cache(struct kdbus_policy_db *db, -+ const void *owner) -+{ -+ struct kdbus_policy_db_cache_entry *ce; -+ struct hlist_node *tmp; -+ int i; -+ -+ mutex_lock(&db->cache_lock); -+ hash_for_each_safe(db->talk_access_hash, i, tmp, ce, hentry) -+ if (ce->owner == owner) { -+ hash_del(&ce->hentry); -+ kfree(ce); -+ } -+ mutex_unlock(&db->cache_lock); -+} -+ -+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); -+ __kdbus_policy_remove_owner_cache(db, owner); -+ up_write(&db->entries_rwlock); -+} -+ -+/** -+ * kdbus_policy_purge_cache_for_conn() - remove all cached entries related to -+ * a connection -+ * @db: The policy database -+ * @conn: The connection which items to remove -+ */ -+void kdbus_policy_purge_cache(struct kdbus_policy_db *db, -+ const struct kdbus_conn *conn) -+{ -+ struct kdbus_policy_db_cache_entry *ce; -+ struct hlist_node *tmp; -+ int i; -+ -+ mutex_lock(&db->cache_lock); -+ hash_for_each_safe(db->talk_access_hash, i, tmp, ce, hentry) -+ if (ce->conn_a == conn || ce->conn_b == conn) { -+ hash_del(&ce->hentry); -+ kfree(ce); -+ } -+ mutex_unlock(&db->cache_lock); -+} -+ -+/* -+ * Convert user provided policy access to internal kdbus policy -+ * access -+ */ -+static int -+kdbus_policy_make_access(const struct kdbus_policy_access *uaccess, -+ struct kdbus_policy_db_entry_access **entry) -+{ -+ int ret; -+ struct kdbus_policy_db_entry_access *a; -+ -+ a = kzalloc(sizeof(*a), GFP_KERNEL); -+ if (!a) -+ return -ENOMEM; -+ -+ ret = -EINVAL; -+ 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; -+ } -+ -+ a->type = uaccess->type; -+ a->access = uaccess->access; -+ -+ *entry = a; -+ -+ return 0; -+ -+err: -+ kfree(a); -+ return 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. -+ * -+ * 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; -+ -+ if (items_size > KDBUS_POLICY_MAX_SIZE) -+ return -E2BIG; -+ -+ /* 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; -+ } -+ -+ ret = kdbus_policy_make_access(&item->policy_access, -+ &a); -+ if (ret < 0) -+ 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_str_hash(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); -+ } -+ -+ /* purge all cache-entries produced by previous rules */ -+ __kdbus_policy_remove_owner_cache(db, owner); -+ -+restore: -+ /* if we failed, flush all entries we added so far, but keep cache */ -+ 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_str_hash(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; -+} ---- /dev/null -+++ b/drivers/misc/kdbus/policy.h -@@ -0,0 +1,60 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+ * Copyright (C) 2013-2014 Daniel Mack <daniel@zonque.org> -+ * Copyright (C) 2013-2014 David Herrmann <dh.herrmann@gmail.com> -+ * Copyright (C) 2013-2014 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 __KDBUS_POLICY_H -+#define __KDBUS_POLICY_H -+ -+#include <linux/hashtable.h> -+#include <linux/mutex.h> -+#include <linux/rwsem.h> -+ -+struct kdbus_conn; -+struct kdbus_item; -+ -+/** -+ * struct kdbus_policy_db - policy database -+ * @entries_hash: Hashtable of entries -+ * @talk_access_hash: Hashtable of send access elements -+ * @entries_lock: Mutex to protect the database's access entries -+ * @cache_lock: Mutex to protect the database's cache -+ */ -+struct kdbus_policy_db { -+ DECLARE_HASHTABLE(entries_hash, 6); -+ DECLARE_HASHTABLE(talk_access_hash, 6); -+ struct rw_semaphore entries_rwlock; -+ struct mutex cache_lock; -+}; -+ -+void kdbus_policy_db_init(struct kdbus_policy_db *db); -+void kdbus_policy_db_clear(struct kdbus_policy_db *db); -+ -+int kdbus_policy_check_see_access_unlocked(struct kdbus_policy_db *db, -+ struct kdbus_conn *conn, -+ const char *name); -+int kdbus_policy_check_talk_access(struct kdbus_policy_db *db, -+ struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst); -+int kdbus_policy_check_own_access(struct kdbus_policy_db *db, -+ const struct kdbus_conn *conn, -+ const char *name); -+void kdbus_policy_purge_cache(struct kdbus_policy_db *db, -+ const struct kdbus_conn *conn); -+void kdbus_policy_remove_owner(struct kdbus_policy_db *db, -+ const void *owner); -+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); -+#endif diff --git a/0011-kdbus-add-Makefile-Kconfig-and-MAINTAINERS-entry.patch b/0011-kdbus-add-Makefile-Kconfig-and-MAINTAINERS-entry.patch deleted file mode 100644 index 0d3031ff46b71e..00000000000000 --- a/0011-kdbus-add-Makefile-Kconfig-and-MAINTAINERS-entry.patch +++ /dev/null @@ -1,94 +0,0 @@ -From f5117b5641e32f1a4b96ea8745e172ab8ba8733f Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Thu, 11 Sep 2014 18:48:06 +0200 -Subject: [PATCH 11/12] kdbus: add Makefile, Kconfig and MAINTAINERS entry - -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. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - MAINTAINERS | 12 ++++++++++++ - drivers/misc/Kconfig | 1 + - drivers/misc/Makefile | 1 + - drivers/misc/kdbus/Kconfig | 11 +++++++++++ - drivers/misc/kdbus/Makefile | 19 +++++++++++++++++++ - 5 files changed, 44 insertions(+) - create mode 100644 drivers/misc/kdbus/Kconfig - create mode 100644 drivers/misc/kdbus/Makefile - ---- a/MAINTAINERS -+++ b/MAINTAINERS -@@ -5255,6 +5255,18 @@ S: Maintained - F: Documentation/kbuild/kconfig-language.txt - F: scripts/kconfig/ - -+KDBUS -+M: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -+M: Daniel Mack <daniel@zonque.org> -+M: David Herrmann <dh.herrmann@googlemail.com> -+M: Djalal Harouni <tixxdz@opendz.org> -+L: linux-kernel@vger.kernel.org -+S: Maintained -+F: drivers/misc/kdbus/* -+F: Documentation/kdbus.txt -+F: include/uapi/linux/kdbus.h -+F: tools/testing/selftests/kdbus/ -+ - KDUMP - M: Vivek Goyal <vgoyal@redhat.com> - M: Haren Myneni <hbabu@us.ibm.com> ---- a/drivers/misc/Kconfig -+++ b/drivers/misc/Kconfig -@@ -528,4 +528,5 @@ source "drivers/misc/mic/Kconfig" - source "drivers/misc/genwqe/Kconfig" - source "drivers/misc/echo/Kconfig" - source "drivers/misc/cxl/Kconfig" -+source "drivers/misc/kdbus/Kconfig" - endmenu ---- a/drivers/misc/Makefile -+++ b/drivers/misc/Makefile -@@ -56,3 +56,4 @@ obj-$(CONFIG_GENWQE) += genwqe/ - obj-$(CONFIG_ECHO) += echo/ - obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o - obj-$(CONFIG_CXL_BASE) += cxl/ -+obj-$(CONFIG_KDBUS) += kdbus/ ---- /dev/null -+++ b/drivers/misc/kdbus/Kconfig -@@ -0,0 +1,11 @@ -+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. ---- /dev/null -+++ b/drivers/misc/kdbus/Makefile -@@ -0,0 +1,19 @@ -+kdbus-y := \ -+ bus.o \ -+ connection.o \ -+ endpoint.o \ -+ handle.o \ -+ item.o \ -+ main.o \ -+ match.o \ -+ message.o \ -+ metadata.o \ -+ names.o \ -+ notify.o \ -+ domain.o \ -+ policy.o \ -+ pool.o \ -+ queue.o \ -+ util.o -+ -+obj-$(CONFIG_KDBUS) += kdbus.o diff --git a/0012-kdbus-add-selftests.patch b/0012-kdbus-add-selftests.patch deleted file mode 100644 index 8c62d93cd2db42..00000000000000 --- a/0012-kdbus-add-selftests.patch +++ /dev/null @@ -1,7855 +0,0 @@ -From 646fb0e79b4b3e5451e08934138952814084b678 Mon Sep 17 00:00:00 2001 -From: Daniel Mack <daniel@zonque.org> -Date: Sat, 13 Sep 2014 23:15:02 +0200 -Subject: [PATCH 12/12] kdbus: add selftests - -This patch adds a quite extensive test suite for kdbus that checks -the most important code pathes in the driver. The idea is to extend -the test suite over time. - -Also, this code can serve as an example implementation to show how to -use the kernel API from userspace. - -Signed-off-by: Daniel Mack <daniel@zonque.org> -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> ---- - tools/testing/selftests/Makefile | 1 - tools/testing/selftests/kdbus/.gitignore | 11 - tools/testing/selftests/kdbus/Makefile | 46 - tools/testing/selftests/kdbus/kdbus-enum.c | 90 + - tools/testing/selftests/kdbus/kdbus-enum.h | 14 - tools/testing/selftests/kdbus/kdbus-test.c | 474 +++++++++ - tools/testing/selftests/kdbus/kdbus-test.h | 79 + - tools/testing/selftests/kdbus/kdbus-util.c | 1173 +++++++++++++++++++++++ - tools/testing/selftests/kdbus/kdbus-util.h | 139 ++ - tools/testing/selftests/kdbus/test-activator.c | 317 ++++++ - tools/testing/selftests/kdbus/test-benchmark.c | 417 ++++++++ - tools/testing/selftests/kdbus/test-bus.c | 117 ++ - tools/testing/selftests/kdbus/test-chat.c | 123 ++ - tools/testing/selftests/kdbus/test-connection.c | 258 +++++ - tools/testing/selftests/kdbus/test-daemon.c | 66 + - tools/testing/selftests/kdbus/test-domain.c | 65 + - tools/testing/selftests/kdbus/test-endpoint.c | 221 ++++ - tools/testing/selftests/kdbus/test-fd.c | 473 +++++++++ - tools/testing/selftests/kdbus/test-free.c | 34 - tools/testing/selftests/kdbus/test-match.c | 385 +++++++ - tools/testing/selftests/kdbus/test-message.c | 126 ++ - tools/testing/selftests/kdbus/test-metadata-ns.c | 236 ++++ - tools/testing/selftests/kdbus/test-monitor.c | 156 +++ - tools/testing/selftests/kdbus/test-names.c | 184 +++ - tools/testing/selftests/kdbus/test-policy-ns.c | 578 +++++++++++ - tools/testing/selftests/kdbus/test-policy-priv.c | 1168 ++++++++++++++++++++++ - tools/testing/selftests/kdbus/test-policy.c | 81 + - tools/testing/selftests/kdbus/test-race.c | 313 ++++++ - tools/testing/selftests/kdbus/test-sync.c | 241 ++++ - tools/testing/selftests/kdbus/test-timeout.c | 97 + - 30 files changed, 7683 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-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-domain.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-race.c - create mode 100644 tools/testing/selftests/kdbus/test-sync.c - create mode 100644 tools/testing/selftests/kdbus/test-timeout.c - ---- a/tools/testing/selftests/Makefile -+++ b/tools/testing/selftests/Makefile -@@ -2,6 +2,7 @@ TARGETS = breakpoints - TARGETS += cpu-hotplug - TARGETS += efivarfs - TARGETS += kcmp -+TARGETS += kdbus - TARGETS += memfd - TARGETS += memory-hotplug - TARGETS += mqueue ---- /dev/null -+++ b/tools/testing/selftests/kdbus/.gitignore -@@ -0,0 +1,11 @@ -+*.cmd -+*.ko -+*.mod.c -+modules.order -+Module.symvers -+*.o -+*.swp -+.tmp_versions -+tags -+tools/kdbus-monitor -+test/kdbus-test ---- /dev/null -+++ b/tools/testing/selftests/kdbus/Makefile -@@ -0,0 +1,46 @@ -+CFLAGS += -I../../../../usr/include/ -+CFLAGS += -I../../../../include/uapi/ -+CFLAGS += -std=gnu99 -+CFLAGS += -DKBUILD_MODNAME=\"kdbus\" -D_GNU_SOURCE -+LDFLAGS = -pthread -lcap -+ -+OBJS= \ -+ kdbus-enum.o \ -+ kdbus-util.o \ -+ kdbus-test.o \ -+ kdbus-test.o \ -+ test-activator.o \ -+ test-benchmark.o \ -+ test-bus.o \ -+ test-chat.o \ -+ test-connection.o \ -+ test-daemon.o \ -+ test-domain.o \ -+ test-endpoint.o \ -+ test-fd.o \ -+ test-free.o \ -+ test-match.o \ -+ test-message.o \ -+ test-metadata-ns.o \ -+ test-monitor.o \ -+ test-names.o \ -+ test-policy.o \ -+ test-policy-ns.o \ -+ test-policy-priv.o \ -+ test-race.o \ -+ test-sync.o \ -+ test-timeout.o -+ -+all: kdbus-test -+ -+%.o: %.c -+ gcc $(CFLAGS) -c $< -o $@ -+ -+kdbus-test: $(OBJS) -+ gcc $(CFLAGS) $(LDFLAGS) $^ -o $@ -+ -+run_tests: -+ ./kdbus-test -+ -+clean: -+ rm -f *.o kdbus-test ---- /dev/null -+++ b/tools/testing/selftests/kdbus/kdbus-enum.c -@@ -0,0 +1,90 @@ -+/* -+ * Copyright (C) 2013-2014 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. -+ */ -+ -+#include <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <sys/ioctl.h> -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+struct kdbus_enum_table { -+ long long id; -+ const char *name; -+}; -+ -+#define TABLE(what) static struct kdbus_enum_table kdbus_table_##what[] -+#define ENUM(_id) { .id = _id, .name = STRINGIFY(_id) } -+#define LOOKUP(what) \ -+ const char *enum_##what(long long id) \ -+ { \ -+ for (size_t i = 0; i < ELEMENTSOF(kdbus_table_##what); i++) \ -+ if (id == kdbus_table_##what[i].id) \ -+ return kdbus_table_##what[i].name; \ -+ return "UNKNOWN"; \ -+ } -+ -+TABLE(CMD) = { -+ ENUM(KDBUS_CMD_BUS_MAKE), -+ ENUM(KDBUS_CMD_DOMAIN_MAKE), -+ ENUM(KDBUS_CMD_ENDPOINT_MAKE), -+ ENUM(KDBUS_CMD_HELLO), -+ ENUM(KDBUS_CMD_MSG_SEND), -+ ENUM(KDBUS_CMD_MSG_RECV), -+ ENUM(KDBUS_CMD_NAME_LIST), -+ ENUM(KDBUS_CMD_NAME_RELEASE), -+ ENUM(KDBUS_CMD_CONN_INFO), -+ ENUM(KDBUS_CMD_MATCH_ADD), -+ ENUM(KDBUS_CMD_MATCH_REMOVE), -+}; -+LOOKUP(CMD); -+ -+TABLE(MSG) = { -+ ENUM(_KDBUS_ITEM_NULL), -+ ENUM(KDBUS_ITEM_PAYLOAD_VEC), -+ ENUM(KDBUS_ITEM_PAYLOAD_OFF), -+ ENUM(KDBUS_ITEM_PAYLOAD_MEMFD), -+ ENUM(KDBUS_ITEM_FDS), -+ ENUM(KDBUS_ITEM_BLOOM_PARAMETER), -+ ENUM(KDBUS_ITEM_BLOOM_FILTER), -+ ENUM(KDBUS_ITEM_DST_NAME), -+ ENUM(KDBUS_ITEM_CREDS), -+ ENUM(KDBUS_ITEM_AUXGROUPS), -+ ENUM(KDBUS_ITEM_PID_COMM), -+ ENUM(KDBUS_ITEM_TID_COMM), -+ ENUM(KDBUS_ITEM_EXE), -+ ENUM(KDBUS_ITEM_CMDLINE), -+ ENUM(KDBUS_ITEM_CGROUP), -+ ENUM(KDBUS_ITEM_CAPS), -+ ENUM(KDBUS_ITEM_SECLABEL), -+ ENUM(KDBUS_ITEM_AUDIT), -+ ENUM(KDBUS_ITEM_CONN_NAME), -+ ENUM(KDBUS_ITEM_NAME), -+ ENUM(KDBUS_ITEM_TIMESTAMP), -+ ENUM(KDBUS_ITEM_NAME_ADD), -+ ENUM(KDBUS_ITEM_NAME_REMOVE), -+ ENUM(KDBUS_ITEM_NAME_CHANGE), -+ ENUM(KDBUS_ITEM_ID_ADD), -+ ENUM(KDBUS_ITEM_ID_REMOVE), -+ ENUM(KDBUS_ITEM_REPLY_TIMEOUT), -+ ENUM(KDBUS_ITEM_REPLY_DEAD), -+}; -+LOOKUP(MSG); -+ -+TABLE(PAYLOAD) = { -+ ENUM(KDBUS_PAYLOAD_KERNEL), -+ ENUM(KDBUS_PAYLOAD_DBUS), -+}; -+LOOKUP(PAYLOAD); ---- /dev/null -+++ b/tools/testing/selftests/kdbus/kdbus-enum.h -@@ -0,0 +1,14 @@ -+/* -+ * Copyright (C) 2013-2014 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); ---- /dev/null -+++ b/tools/testing/selftests/kdbus/kdbus-test.c -@@ -0,0 +1,474 @@ -+#include <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <time.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <assert.h> -+#include <getopt.h> -+#include <stdbool.h> -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+#include "kdbus-test.h" -+ -+enum { -+ TEST_CREATE_BUS = 1 << 0, -+ TEST_CREATE_CONN = 1 << 1, -+}; -+ -+struct kdbus_test { -+ const char *name; -+ const char *desc; -+ int (*func)(struct kdbus_test_env *env); -+ unsigned int flags; -+}; -+ -+static const struct kdbus_test tests[] = { -+ { -+ .name = "bus-make", -+ .desc = "bus make functions", -+ .func = kdbus_test_bus_make, -+ .flags = 0, -+ }, -+ { -+ .name = "hello", -+ .desc = "the HELLO command", -+ .func = kdbus_test_hello, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "byebye", -+ .desc = "the BYEBYE command", -+ .func = kdbus_test_byebye, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "chat", -+ .desc = "a chat pattern", -+ .func = kdbus_test_chat, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "daemon", -+ .desc = "a simple dameon", -+ .func = kdbus_test_daemon, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "fd-passing", -+ .desc = "file descriptor passing", -+ .func = kdbus_test_fd_passing, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "endpoint", -+ .desc = "custom endpoint", -+ .func = kdbus_test_custom_endpoint, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "monitor", -+ .desc = "monitor functionality", -+ .func = kdbus_test_monitor, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "name-basics", -+ .desc = "basic name registry functions", -+ .func = kdbus_test_name_basic, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "name-conflict", -+ .desc = "name registry conflict details", -+ .func = kdbus_test_name_conflict, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "name-queue", -+ .desc = "queuing of names", -+ .func = kdbus_test_name_queue, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "message-basic", -+ .desc = "basic message handling", -+ .func = kdbus_test_message_basic, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "message-prio", -+ .desc = "handling of messages with priority", -+ .func = kdbus_test_message_prio, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "timeout", -+ .desc = "timeout", -+ .func = kdbus_test_timeout, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "sync-byebye", -+ .desc = "synchronous replies vs. BYEBYE", -+ .func = kdbus_test_sync_byebye, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "sync-reply", -+ .desc = "synchronous replies", -+ .func = kdbus_test_sync_reply, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "message-free", -+ .desc = "freeing of memory", -+ .func = kdbus_test_free, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "connection-info", -+ .desc = "retrieving connection information", -+ .func = kdbus_test_conn_info, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "connection-update", -+ .desc = "updating connection information", -+ .func = kdbus_test_conn_update, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "writable-pool", -+ .desc = "verifying pools are never writable", -+ .func = kdbus_test_writable_pool, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "policy", -+ .desc = "policy", -+ .func = kdbus_test_policy, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "policy-priv", -+ .desc = "unprivileged bus access", -+ .func = kdbus_test_policy_priv, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "policy-ns", -+ .desc = "policy in user namespaces", -+ .func = kdbus_test_policy_ns, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "metadata-ns", -+ .desc = "metadata in user namespaces", -+ .func = kdbus_test_metadata_ns, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "match-id-add", -+ .desc = "adding of matches by id", -+ .func = kdbus_test_match_id_add, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "match-id-remove", -+ .desc = "removing of matches by id", -+ .func = kdbus_test_match_id_remove, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "match-name-add", -+ .desc = "adding of matches by name", -+ .func = kdbus_test_match_name_add, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "match-name-remove", -+ .desc = "removing of matches by name", -+ .func = kdbus_test_match_name_remove, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "match-name-change", -+ .desc = "matching for name changes", -+ .func = kdbus_test_match_name_change, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "match-bloom", -+ .desc = "matching with bloom filters", -+ .func = kdbus_test_match_bloom, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "activator", -+ .desc = "activator connections", -+ .func = kdbus_test_activator, -+ .flags = TEST_CREATE_BUS | TEST_CREATE_CONN, -+ }, -+ { -+ .name = "domain-make", -+ .desc = "creating a domain", -+ .func = kdbus_test_domain_make, -+ .flags = 0, -+ }, -+ { -+ .name = "benchmark", -+ .desc = "benchmark", -+ .func = kdbus_test_benchmark, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "race-byebye", -+ .desc = "race multiple byebyes", -+ .func = kdbus_test_race_byebye, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { -+ .name = "race-byebye-match", -+ .desc = "race byebye vs match removal", -+ .func = kdbus_test_race_byebye_match, -+ .flags = TEST_CREATE_BUS, -+ }, -+ { NULL } /* sentinel */ -+}; -+ -+static int test_prepare_env(const struct kdbus_test *t, -+ struct kdbus_test_env *env, -+ const char *busname) -+{ -+ if (t->flags & TEST_CREATE_BUS) { -+ unsigned int i; -+ char n[16]; -+ int ret; -+ -+ env->control_fd = -+ open("/dev/" KBUILD_MODNAME "/control", O_RDWR); -+ ASSERT_RETURN(env->control_fd >= 0); -+ -+ if (!busname) { -+ srand(time(NULL)); -+ -+ for (i = 0; i < sizeof(n) - 1; i++) -+ n[i] = 'a' + (rand() % ('z' - 'a')); -+ n[sizeof(n) - 1] = 0; -+ } -+ -+ ret = kdbus_create_bus(env->control_fd, busname ?: n, -+ &env->buspath); -+ ASSERT_RETURN(ret == 0); -+ } -+ -+ if (t->flags & TEST_CREATE_CONN) { -+ env->conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(env->conn); -+ } -+ -+ return 0; -+} -+ -+void test_unprepare_env(const struct kdbus_test *t, struct kdbus_test_env *env) -+{ -+ if (env->conn) { -+ kdbus_conn_free(env->conn); -+ env->conn = NULL; -+ } -+ -+ if (env->control_fd >= 0) { -+ close(env->control_fd); -+ env->control_fd = -1; -+ } -+ -+ if (env->buspath) { -+ free(env->buspath); -+ env->buspath = NULL; -+ } -+} -+ -+static int test_run(const struct kdbus_test *t, const char *busname, int wait) -+{ -+ int ret; -+ struct kdbus_test_env env = {}; -+ -+ ret = test_prepare_env(t, &env, busname); -+ if (ret != TEST_OK) -+ return ret; -+ -+ if (wait > 0) { -+ printf("Sleeping %d seconds before running test ...\n", wait); -+ sleep(wait); -+ } -+ -+ ret = t->func(&env); -+ test_unprepare_env(t, &env); -+ -+ return ret; -+} -+ -+static void print_test_result(int ret) -+{ -+ switch (ret) { -+ case TEST_OK: -+ printf("OK"); -+ break; -+ case TEST_SKIP: -+ printf("SKIPPED"); -+ break; -+ case TEST_ERR: -+ printf("ERROR"); -+ break; -+ } -+} -+ -+static int run_all_tests(const char *busname) -+{ -+ int ret; -+ unsigned int fail_cnt = 0; -+ unsigned int skip_cnt = 0; -+ unsigned int ok_cnt = 0; -+ unsigned int i; -+ const struct kdbus_test *t; -+ -+ kdbus_util_verbose = false; -+ -+ for (t = tests; t->name; t++) { -+ printf("Testing %s (%s) ", t->desc, t->name); -+ for (i = 0; i < 60 - strlen(t->desc) - strlen(t->name); i++) -+ printf("."); -+ printf(" "); -+ -+ ret = test_run(t, busname, 0); -+ switch (ret) { -+ case TEST_OK: -+ ok_cnt++; -+ break; -+ case TEST_SKIP: -+ skip_cnt++; -+ break; -+ case TEST_ERR: -+ fail_cnt++; -+ break; -+ } -+ -+ print_test_result(ret); -+ printf("\n"); -+ } -+ -+ printf("\nSUMMARY: %d tests passed, %d skipped, %d failed\n", -+ ok_cnt, skip_cnt, fail_cnt); -+ -+ return fail_cnt > 0 ? EXIT_FAILURE : EXIT_SUCCESS; -+} -+ -+static void usage(const char *argv0) -+{ -+ const struct kdbus_test *t; -+ unsigned int i; -+ -+ printf("Usage: %s [options]\n" -+ "Options:\n" -+ "\t-x, --loop Run in a loop\n" -+ "\t-h, --help Print this help\n" -+ "\t-t, --test <test-id> Run one specific test only, in verbose mode\n" -+ "\t-b, --bus <busname> Instead of generating a random bus name, take <busname>.\n" -+ "\t-w, --wait <secs> Wait <secs> before actually starting test\n" -+ "\n", argv0); -+ -+ printf("By default, all test are run once, and a summary is printed.\n" -+ "Available tests for --test:\n\n"); -+ -+ for (t = tests; t->name; t++) { -+ printf("\t%s", t->name); -+ -+ for (i = 0; i < 24 - strlen(t->name); i++) -+ printf(" "); -+ -+ printf("Test %s\n", t->desc); -+ } -+ -+ printf("\n"); -+ printf("Note that some tests may, if run specifically by --test, " -+ "behave differently, and not terminate by themselves.\n"); -+ -+ exit(EXIT_FAILURE); -+} -+ -+int main(int argc, char *argv[]) -+{ -+ int t, ret = 0; -+ int arg_loop = 0; -+ char *arg_test = NULL; -+ char *arg_busname = NULL; -+ int arg_wait = 0; -+ -+ static const struct option options[] = { -+ { "loop", no_argument, NULL, 'x' }, -+ { "help", no_argument, NULL, 'h' }, -+ { "test", required_argument, NULL, 't' }, -+ { "bus", required_argument, NULL, 'b' }, -+ { "wait", required_argument, NULL, 'w' }, -+ {} -+ }; -+ -+ while ((t = getopt_long(argc, argv, "hxt:b:w:", options, NULL)) >= 0) { -+ switch (t) { -+ case 'x': -+ arg_loop = 1; -+ break; -+ -+ case 't': -+ arg_test = optarg; -+ break; -+ -+ case 'b': -+ arg_busname = optarg; -+ break; -+ -+ case 'w': -+ arg_wait = strtol(optarg, NULL, 10); -+ break; -+ -+ default: -+ case 'h': -+ usage(argv[0]); -+ } -+ } -+ -+ if (arg_test) { -+ const struct kdbus_test *t; -+ -+ for (t = tests; t->name; t++) { -+ if (!strcmp(t->name, arg_test)) { -+ do { -+ ret = test_run(t, arg_busname, arg_wait); -+ printf("Testing %s: ", t->desc); -+ print_test_result(ret); -+ printf("\n"); -+ -+ if (ret != TEST_OK) -+ break; -+ } while (arg_loop); -+ -+ return ret == TEST_OK ? 0 : EXIT_FAILURE; -+ } -+ } -+ -+ printf("Unknown test-id '%s'\n", arg_test); -+ return EXIT_FAILURE; -+ } -+ -+ do { -+ ret = run_all_tests(arg_busname); -+ if (ret != TEST_OK) -+ break; -+ } while (arg_loop); -+ -+ return 0; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/kdbus-test.h -@@ -0,0 +1,79 @@ -+#ifndef _TEST_KDBUS_H_ -+#define _TEST_KDBUS_H_ -+ -+struct kdbus_test_env { -+ char *buspath; -+ int control_fd; -+ struct kdbus_conn *conn; -+}; -+ -+enum { -+ TEST_OK, -+ TEST_SKIP, -+ TEST_ERR, -+}; -+ -+#define ASSERT_RETURN_VAL(cond, val) \ -+ if (!(cond)) { \ -+ fprintf(stderr, "Assertion '%s' failed in %s(), %s:%d\n", \ -+ #cond, __func__, __FILE__, __LINE__); \ -+ return val; \ -+ } -+ -+#define ASSERT_EXIT_VAL(cond, val) \ -+ if (!(cond)) { \ -+ fprintf(stderr, "Assertion '%s' failed in %s(), %s:%d\n", \ -+ #cond, __func__, __FILE__, __LINE__); \ -+ _exit(val); \ -+ } -+ -+#define ASSERT_BREAK(cond) \ -+ if (!(cond)) { \ -+ fprintf(stderr, "Assertion '%s' failed in %s(), %s:%d\n", \ -+ #cond, __func__, __FILE__, __LINE__); \ -+ break; \ -+ } -+ -+#define ASSERT_RETURN(cond) \ -+ ASSERT_RETURN_VAL(cond, TEST_ERR) -+ -+#define ASSERT_EXIT(cond) \ -+ ASSERT_EXIT_VAL(cond, EXIT_FAILURE) -+ -+int kdbus_test_activator(struct kdbus_test_env *env); -+int kdbus_test_benchmark(struct kdbus_test_env *env); -+int kdbus_test_bus_make(struct kdbus_test_env *env); -+int kdbus_test_byebye(struct kdbus_test_env *env); -+int kdbus_test_chat(struct kdbus_test_env *env); -+int kdbus_test_conn_info(struct kdbus_test_env *env); -+int kdbus_test_conn_update(struct kdbus_test_env *env); -+int kdbus_test_daemon(struct kdbus_test_env *env); -+int kdbus_test_domain_make(struct kdbus_test_env *env); -+int kdbus_test_custom_endpoint(struct kdbus_test_env *env); -+int kdbus_test_fd_passing(struct kdbus_test_env *env); -+int kdbus_test_free(struct kdbus_test_env *env); -+int kdbus_test_hello(struct kdbus_test_env *env); -+int kdbus_test_match_bloom(struct kdbus_test_env *env); -+int kdbus_test_match_id_add(struct kdbus_test_env *env); -+int kdbus_test_match_id_remove(struct kdbus_test_env *env); -+int kdbus_test_match_name_add(struct kdbus_test_env *env); -+int kdbus_test_match_name_change(struct kdbus_test_env *env); -+int kdbus_test_match_name_remove(struct kdbus_test_env *env); -+int kdbus_test_message_basic(struct kdbus_test_env *env); -+int kdbus_test_message_prio(struct kdbus_test_env *env); -+int kdbus_test_metadata_ns(struct kdbus_test_env *env); -+int kdbus_test_monitor(struct kdbus_test_env *env); -+int kdbus_test_name_basic(struct kdbus_test_env *env); -+int kdbus_test_name_conflict(struct kdbus_test_env *env); -+int kdbus_test_name_queue(struct kdbus_test_env *env); -+int kdbus_test_policy(struct kdbus_test_env *env); -+int kdbus_test_policy_ns(struct kdbus_test_env *env); -+int kdbus_test_policy_priv(struct kdbus_test_env *env); -+int kdbus_test_race_byebye(struct kdbus_test_env *env); -+int kdbus_test_race_byebye_match(struct kdbus_test_env *env); -+int kdbus_test_sync_byebye(struct kdbus_test_env *env); -+int kdbus_test_sync_reply(struct kdbus_test_env *env); -+int kdbus_test_timeout(struct kdbus_test_env *env); -+int kdbus_test_writable_pool(struct kdbus_test_env *env); -+ -+#endif /* _TEST_KDBUS_H_ */ ---- /dev/null -+++ b/tools/testing/selftests/kdbus/kdbus-util.c -@@ -0,0 +1,1173 @@ -+/* -+ * Copyright (C) 2013-2014 Daniel Mack -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2014 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 <stdio.h> -+#include <stdarg.h> -+#include <string.h> -+#include <time.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <stdbool.h> -+#include <errno.h> -+#include <assert.h> -+#include <poll.h> -+#include <grp.h> -+#include <sys/capability.h> -+#include <sys/ioctl.h> -+#include <sys/mman.h> -+#include <sys/stat.h> -+#include <sys/time.h> -+#include <linux/unistd.h> -+#include <linux/memfd.h> -+ -+#ifndef __NR_memfd_create -+ #ifdef __x86_64__ -+ #define __NR_memfd_create 319 -+ #elif defined __arm__ -+ #define __NR_memfd_create 385 -+ #else -+ #define __NR_memfd_create 356 -+ #endif -+#endif -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+#ifndef F_ADD_SEALS -+#define F_LINUX_SPECIFIC_BASE 1024 -+#define F_ADD_SEALS (F_LINUX_SPECIFIC_BASE + 9) -+#define F_GET_SEALS (F_LINUX_SPECIFIC_BASE + 10) -+ -+#define F_SEAL_SEAL 0x0001 /* prevent further seals from being set */ -+#define F_SEAL_SHRINK 0x0002 /* prevent file from shrinking */ -+#define F_SEAL_GROW 0x0004 /* prevent file from growing */ -+#define F_SEAL_WRITE 0x0008 /* prevent writes */ -+#endif -+ -+int kdbus_util_verbose = true; -+ -+int kdbus_create_bus(int control_fd, const char *name, char **path) -+{ -+ struct { -+ struct kdbus_cmd_make head; -+ -+ /* bloom size item */ -+ struct { -+ uint64_t size; -+ uint64_t type; -+ struct kdbus_bloom_parameter bloom; -+ } bp; -+ -+ /* name item */ -+ struct { -+ uint64_t size; -+ uint64_t type; -+ char str[64]; -+ } name; -+ } bus_make; -+ int ret; -+ -+ memset(&bus_make, 0, sizeof(bus_make)); -+ bus_make.bp.size = sizeof(bus_make.bp); -+ bus_make.bp.type = KDBUS_ITEM_BLOOM_PARAMETER; -+ bus_make.bp.bloom.size = 64; -+ bus_make.bp.bloom.n_hash = 1; -+ -+ snprintf(bus_make.name.str, sizeof(bus_make.name.str), -+ "%u-%s", getuid(), name); -+ -+ bus_make.name.type = KDBUS_ITEM_MAKE_NAME; -+ bus_make.name.size = KDBUS_ITEM_HEADER_SIZE + -+ strlen(bus_make.name.str) + 1; -+ -+ bus_make.head.flags = KDBUS_MAKE_ACCESS_WORLD; -+ bus_make.head.size = sizeof(bus_make.head) + -+ sizeof(bus_make.bp) + -+ bus_make.name.size; -+ -+ kdbus_printf("Creating bus with name >%s< on control fd %d ...\n", -+ name, control_fd); -+ -+ ret = ioctl(control_fd, KDBUS_CMD_BUS_MAKE, &bus_make); -+ -+ if (ret == 0 && path) -+ asprintf(path, "/dev/" KBUILD_MODNAME "/%s/bus", -+ bus_make.name.str); -+ -+ return ret; -+} -+ -+struct kdbus_conn * -+kdbus_hello(const char *path, uint64_t flags, -+ const struct kdbus_item *item, size_t item_size) -+{ -+ int fd, ret; -+ struct { -+ struct kdbus_cmd_hello hello; -+ -+ struct { -+ uint64_t size; -+ uint64_t type; -+ char str[16]; -+ } conn_name; -+ -+ uint8_t extra_items[item_size]; -+ } h; -+ struct kdbus_conn *conn; -+ -+ memset(&h, 0, sizeof(h)); -+ -+ if (item_size > 0) -+ memcpy(h.extra_items, item, item_size); -+ -+ kdbus_printf("-- opening bus connection %s\n", path); -+ fd = open(path, O_RDWR|O_CLOEXEC); -+ if (fd < 0) { -+ kdbus_printf("--- error %d (%m)\n", fd); -+ return NULL; -+ } -+ -+ h.hello.flags = flags | KDBUS_HELLO_ACCEPT_FD; -+ h.hello.attach_flags = _KDBUS_ATTACH_ALL; -+ h.conn_name.type = KDBUS_ITEM_CONN_NAME; -+ strcpy(h.conn_name.str, "this-is-my-name"); -+ h.conn_name.size = KDBUS_ITEM_HEADER_SIZE + strlen(h.conn_name.str) + 1; -+ -+ h.hello.size = sizeof(h); -+ h.hello.pool_size = POOL_SIZE; -+ -+ ret = ioctl(fd, KDBUS_CMD_HELLO, &h.hello); -+ if (ret < 0) { -+ kdbus_printf("--- error when saying hello: %d (%m)\n", ret); -+ return NULL; -+ } -+ kdbus_printf("-- Our peer ID for %s: %llu -- bus uuid: '%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x'\n", -+ path, (unsigned long long)h.hello.id, -+ h.hello.id128[0], h.hello.id128[1], h.hello.id128[2], -+ h.hello.id128[3], h.hello.id128[4], h.hello.id128[5], -+ h.hello.id128[6], h.hello.id128[7], h.hello.id128[8], -+ h.hello.id128[9], h.hello.id128[10], h.hello.id128[11], -+ h.hello.id128[12], h.hello.id128[13], h.hello.id128[14], -+ h.hello.id128[15]); -+ -+ conn = malloc(sizeof(*conn)); -+ if (!conn) { -+ kdbus_printf("unable to malloc()!?\n"); -+ return NULL; -+ } -+ -+ conn->buf = mmap(NULL, POOL_SIZE, PROT_READ, MAP_PRIVATE, fd, 0); -+ if (conn->buf == MAP_FAILED) { -+ free(conn); -+ close(fd); -+ kdbus_printf("--- error mmap (%m)\n"); -+ return NULL; -+ } -+ -+ conn->fd = fd; -+ conn->id = h.hello.id; -+ return conn; -+} -+ -+struct kdbus_conn * -+kdbus_hello_registrar(const char *path, const char *name, -+ const struct kdbus_policy_access *access, -+ size_t num_access, uint64_t flags) -+{ -+ struct kdbus_item *item, *items; -+ size_t i, size; -+ -+ size = KDBUS_ITEM_SIZE(strlen(name) + 1) + -+ num_access * KDBUS_ITEM_SIZE(sizeof(*access)); -+ -+ items = alloca(size); -+ -+ item = items; -+ item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; -+ item->type = KDBUS_ITEM_NAME; -+ strcpy(item->str, name); -+ item = KDBUS_ITEM_NEXT(item); -+ -+ for (i = 0; i < num_access; i++) { -+ item->size = KDBUS_ITEM_HEADER_SIZE + -+ sizeof(struct kdbus_policy_access); -+ item->type = KDBUS_ITEM_POLICY_ACCESS; -+ -+ item->policy_access.type = access[i].type; -+ item->policy_access.access = access[i].access; -+ item->policy_access.id = access[i].id; -+ -+ item = KDBUS_ITEM_NEXT(item); -+ } -+ -+ return kdbus_hello(path, flags, items, size); -+} -+ -+struct kdbus_conn *kdbus_hello_activator(const char *path, const char *name, -+ const struct kdbus_policy_access *access, -+ size_t num_access) -+{ -+ return kdbus_hello_registrar(path, name, access, num_access, -+ KDBUS_HELLO_ACTIVATOR); -+} -+ -+int kdbus_info(struct kdbus_conn *conn, uint64_t id, -+ const char *name, uint64_t *offset) -+{ -+ struct kdbus_cmd_info *cmd; -+ size_t size = sizeof(*cmd); -+ int ret; -+ -+ if (name) -+ size += KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; -+ -+ cmd = alloca(size); -+ memset(cmd, 0, size); -+ cmd->size = size; -+ -+ if (name) { -+ cmd->items[0].size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; -+ cmd->items[0].type = KDBUS_ITEM_NAME; -+ strcpy(cmd->items[0].str, name); -+ } else { -+ cmd->id = id; -+ } -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_CONN_INFO, cmd); -+ if (ret < 0) -+ return -errno; -+ -+ if (offset) -+ *offset = cmd->offset; -+ else -+ kdbus_free(conn, cmd->offset); -+ -+ return 0; -+} -+ -+void kdbus_conn_free(struct kdbus_conn *conn) -+{ -+ if (!conn) -+ return; -+ -+ if (conn->buf) -+ munmap(conn->buf, POOL_SIZE); -+ -+ if (conn->fd >= 0) -+ close(conn->fd); -+ -+ free(conn); -+} -+ -+int sys_memfd_create(const char *name, __u64 size) -+{ -+ int ret, fd; -+ -+ ret = syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING); -+ if (ret < 0) -+ return ret; -+ -+ fd = ret; -+ -+ ret = ftruncate(fd, size); -+ if (ret < 0) { -+ close(fd); -+ return ret; -+ } -+ -+ return fd; -+} -+ -+int sys_memfd_seal_set(int fd) -+{ -+ return fcntl(fd, F_ADD_SEALS, F_SEAL_SHRINK | -+ F_SEAL_GROW | F_SEAL_WRITE); -+} -+ -+off_t sys_memfd_get_size(int fd, off_t *size) -+{ -+ struct stat stat; -+ int ret; -+ -+ ret = fstat(fd, &stat); -+ if (ret < 0) { -+ kdbus_printf("stat() failed: %m\n"); -+ return ret; -+ } -+ -+ *size = stat.st_size; -+ return 0; -+} -+ -+int kdbus_msg_send(const struct kdbus_conn *conn, -+ const char *name, -+ uint64_t cookie, -+ uint64_t flags, -+ uint64_t timeout, -+ int64_t priority, -+ uint64_t dst_id) -+{ -+ struct kdbus_msg *msg; -+ const char ref1[1024 * 128 + 3] = "0123456789_0"; -+ const char ref2[] = "0123456789_1"; -+ struct kdbus_item *item; -+ struct timespec now; -+ uint64_t size; -+ int memfd = -1; -+ int ret; -+ -+ size = sizeof(struct kdbus_msg); -+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); -+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); -+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); -+ -+ if (dst_id == KDBUS_DST_ID_BROADCAST) -+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64; -+ else { -+ memfd = sys_memfd_create("my-name-is-nice", 1024 * 1024); -+ if (memfd < 0) { -+ kdbus_printf("failed to create memfd: %m\n"); -+ return memfd; -+ } -+ -+ if (write(memfd, "kdbus memfd 1234567", 19) != 19) { -+ ret = -errno; -+ kdbus_printf("writing to memfd failed: %m\n"); -+ return ret; -+ } -+ -+ ret = sys_memfd_seal_set(memfd); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("memfd sealing failed: %m\n"); -+ return ret; -+ } -+ -+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd)); -+ } -+ -+ if (name) -+ size += KDBUS_ITEM_SIZE(strlen(name) + 1); -+ -+ msg = malloc(size); -+ if (!msg) { -+ ret = -errno; -+ kdbus_printf("unable to malloc()!?\n"); -+ return ret; -+ } -+ -+ memset(msg, 0, size); -+ msg->flags = flags; -+ msg->priority = priority; -+ msg->size = size; -+ msg->src_id = conn->id; -+ msg->dst_id = name ? 0 : dst_id; -+ msg->cookie = cookie; -+ msg->payload_type = KDBUS_PAYLOAD_DBUS; -+ -+ if (timeout) { -+ ret = clock_gettime(CLOCK_MONOTONIC_COARSE, &now); -+ if (ret < 0) -+ return ret; -+ -+ msg->timeout_ns = now.tv_sec * 1000000000ULL + -+ now.tv_nsec + timeout; -+ } -+ -+ item = msg->items; -+ -+ if (name) { -+ item->type = KDBUS_ITEM_DST_NAME; -+ item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; -+ strcpy(item->str, name); -+ item = KDBUS_ITEM_NEXT(item); -+ } -+ -+ item->type = KDBUS_ITEM_PAYLOAD_VEC; -+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); -+ item->vec.address = (uintptr_t)&ref1; -+ item->vec.size = sizeof(ref1); -+ item = KDBUS_ITEM_NEXT(item); -+ -+ /* data padding for ref1 */ -+ item->type = KDBUS_ITEM_PAYLOAD_VEC; -+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); -+ item->vec.address = (uintptr_t)NULL; -+ item->vec.size = KDBUS_ALIGN8(sizeof(ref1)) - sizeof(ref1); -+ item = KDBUS_ITEM_NEXT(item); -+ -+ item->type = KDBUS_ITEM_PAYLOAD_VEC; -+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); -+ item->vec.address = (uintptr_t)&ref2; -+ item->vec.size = sizeof(ref2); -+ item = KDBUS_ITEM_NEXT(item); -+ -+ if (dst_id == KDBUS_DST_ID_BROADCAST) { -+ item->type = KDBUS_ITEM_BLOOM_FILTER; -+ item->size = KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64; -+ item->bloom_filter.generation = 0; -+ } else { -+ item->type = KDBUS_ITEM_PAYLOAD_MEMFD; -+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_memfd); -+ item->memfd.size = 16; -+ item->memfd.fd = memfd; -+ } -+ item = KDBUS_ITEM_NEXT(item); -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, msg); -+ if (memfd >= 0) -+ close(memfd); -+ -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error sending message: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ if (flags & KDBUS_MSG_FLAGS_SYNC_REPLY) { -+ struct kdbus_msg *reply; -+ -+ kdbus_printf("SYNC REPLY @offset %llu:\n", msg->offset_reply); -+ reply = (struct kdbus_msg *)(conn->buf + msg->offset_reply); -+ kdbus_msg_dump(conn, reply); -+ -+ kdbus_msg_free(reply); -+ -+ ret = kdbus_free(conn, msg->offset_reply); -+ if (ret < 0) -+ return ret; -+ } -+ -+ free(msg); -+ -+ return 0; -+} -+ -+static char *msg_id(uint64_t id, char *buf) -+{ -+ if (id == 0) -+ return "KERNEL"; -+ if (id == ~0ULL) -+ return "BROADCAST"; -+ sprintf(buf, "%llu", (unsigned long long)id); -+ return buf; -+} -+ -+int kdbus_msg_dump(const struct kdbus_conn *conn, const struct kdbus_msg *msg) -+{ -+ const struct kdbus_item *item = msg->items; -+ char buf_src[32]; -+ char buf_dst[32]; -+ uint64_t timeout = 0; -+ uint64_t cookie_reply = 0; -+ int ret = 0; -+ -+ if (msg->flags & KDBUS_MSG_FLAGS_EXPECT_REPLY) -+ timeout = msg->timeout_ns; -+ else -+ cookie_reply = msg->cookie_reply; -+ -+ kdbus_printf("MESSAGE: %s (%llu bytes) flags=0x%08llx, %s → %s, " -+ "cookie=%llu, timeout=%llu cookie_reply=%llu priority=%lli\n", -+ enum_PAYLOAD(msg->payload_type), (unsigned long long)msg->size, -+ (unsigned long long)msg->flags, -+ msg_id(msg->src_id, buf_src), msg_id(msg->dst_id, buf_dst), -+ (unsigned long long)msg->cookie, (unsigned long long)timeout, -+ (unsigned long long)cookie_reply, (long long)msg->priority); -+ -+ KDBUS_ITEM_FOREACH(item, msg, items) { -+ if (item->size < KDBUS_ITEM_HEADER_SIZE) { -+ kdbus_printf(" +%s (%llu bytes) invalid data record\n", -+ enum_MSG(item->type), item->size); -+ ret = -EINVAL; -+ break; -+ } -+ -+ switch (item->type) { -+ case KDBUS_ITEM_PAYLOAD_OFF: { -+ char *s; -+ -+ if (item->vec.offset == ~0ULL) -+ s = "[\\0-bytes]"; -+ else -+ s = (char *)msg + item->vec.offset; -+ -+ kdbus_printf(" +%s (%llu bytes) off=%llu size=%llu '%s'\n", -+ enum_MSG(item->type), item->size, -+ (unsigned long long)item->vec.offset, -+ (unsigned long long)item->vec.size, s); -+ break; -+ } -+ -+ case KDBUS_ITEM_PAYLOAD_MEMFD: { -+ char *buf; -+ off_t size; -+ -+ buf = mmap(NULL, item->memfd.size, PROT_READ, -+ MAP_PRIVATE, item->memfd.fd, 0); -+ if (buf == MAP_FAILED) { -+ kdbus_printf("mmap() fd=%i size=%llu failed: %m\n", -+ item->memfd.fd, item->memfd.size); -+ break; -+ } -+ -+ if (sys_memfd_get_size(item->memfd.fd, &size) < 0) { -+ kdbus_printf("KDBUS_CMD_MEMFD_SIZE_GET failed: %m\n"); -+ break; -+ } -+ -+ kdbus_printf(" +%s (%llu bytes) fd=%i size=%llu filesize=%llu '%s'\n", -+ enum_MSG(item->type), item->size, item->memfd.fd, -+ (unsigned long long)item->memfd.size, -+ (unsigned long long)size, buf); -+ munmap(buf, item->memfd.size); -+ break; -+ } -+ -+ case KDBUS_ITEM_CREDS: -+ kdbus_printf(" +%s (%llu bytes) uid=%lld, gid=%lld, pid=%lld, tid=%lld, starttime=%lld\n", -+ enum_MSG(item->type), item->size, -+ item->creds.uid, item->creds.gid, -+ item->creds.pid, item->creds.tid, -+ item->creds.starttime); -+ break; -+ -+ case KDBUS_ITEM_AUXGROUPS: { -+ int i, n; -+ -+ kdbus_printf(" +%s (%llu bytes)\n", -+ enum_MSG(item->type), item->size); -+ n = (item->size - KDBUS_ITEM_HEADER_SIZE) / -+ sizeof(uint64_t); -+ -+ for (i = 0; i < n; i++) -+ kdbus_printf(" gid[%d] = %lld\n", -+ i, item->data64[i]); -+ break; -+ } -+ -+ case KDBUS_ITEM_PID_COMM: -+ case KDBUS_ITEM_TID_COMM: -+ case KDBUS_ITEM_EXE: -+ case KDBUS_ITEM_CGROUP: -+ case KDBUS_ITEM_SECLABEL: -+ case KDBUS_ITEM_DST_NAME: -+ case KDBUS_ITEM_CONN_NAME: -+ kdbus_printf(" +%s (%llu bytes) '%s' (%zu)\n", -+ enum_MSG(item->type), item->size, -+ item->str, strlen(item->str)); -+ break; -+ -+ case KDBUS_ITEM_NAME: { -+ kdbus_printf(" +%s (%llu bytes) '%s' (%zu) flags=0x%08llx\n", -+ enum_MSG(item->type), item->size, -+ item->name.name, strlen(item->name.name), -+ item->name.flags); -+ break; -+ } -+ -+ case KDBUS_ITEM_CMDLINE: { -+ size_t size = item->size - KDBUS_ITEM_HEADER_SIZE; -+ const char *str = item->str; -+ int count = 0; -+ -+ kdbus_printf(" +%s (%llu bytes) ", -+ enum_MSG(item->type), item->size); -+ while (size) { -+ kdbus_printf("'%s' ", str); -+ size -= strlen(str) + 1; -+ str += strlen(str) + 1; -+ count++; -+ } -+ -+ kdbus_printf("(%d string%s)\n", -+ count, (count == 1) ? "" : "s"); -+ break; -+ } -+ -+ case KDBUS_ITEM_AUDIT: -+ kdbus_printf(" +%s (%llu bytes) loginuid=%llu sessionid=%llu\n", -+ enum_MSG(item->type), item->size, -+ (unsigned long long)item->audit.loginuid, -+ (unsigned long long)item->audit.sessionid); -+ break; -+ -+ case KDBUS_ITEM_CAPS: { -+ const uint32_t *cap; -+ int n, i; -+ -+ kdbus_printf(" +%s (%llu bytes) len=%llu bytes, last_cap %d\n", -+ enum_MSG(item->type), item->size, -+ (unsigned long long)item->size - -+ KDBUS_ITEM_HEADER_SIZE, -+ (int) item->caps.last_cap); -+ -+ cap = item->caps.caps; -+ n = (item->size - offsetof(struct kdbus_item, caps.caps)) -+ / 4 / sizeof(uint32_t); -+ -+ kdbus_printf(" CapInh="); -+ for (i = 0; i < n; i++) -+ kdbus_printf("%08x", cap[(0 * n) + (n - i - 1)]); -+ -+ kdbus_printf(" CapPrm="); -+ for (i = 0; i < n; i++) -+ kdbus_printf("%08x", cap[(1 * n) + (n - i - 1)]); -+ -+ kdbus_printf(" CapEff="); -+ for (i = 0; i < n; i++) -+ kdbus_printf("%08x", cap[(2 * n) + (n - i - 1)]); -+ -+ kdbus_printf(" CapBnd="); -+ for (i = 0; i < n; i++) -+ kdbus_printf("%08x", cap[(3 * n) + (n - i - 1)]); -+ kdbus_printf("\n"); -+ break; -+ } -+ -+ case KDBUS_ITEM_TIMESTAMP: -+ kdbus_printf(" +%s (%llu bytes) seq=%llu realtime=%lluns monotonic=%lluns\n", -+ enum_MSG(item->type), item->size, -+ (unsigned long long)item->timestamp.seqnum, -+ (unsigned long long)item->timestamp.realtime_ns, -+ (unsigned long long)item->timestamp.monotonic_ns); -+ break; -+ -+ case KDBUS_ITEM_REPLY_TIMEOUT: -+ kdbus_printf(" +%s (%llu bytes) cookie=%llu\n", -+ enum_MSG(item->type), item->size, -+ msg->cookie_reply); -+ break; -+ -+ case KDBUS_ITEM_NAME_ADD: -+ case KDBUS_ITEM_NAME_REMOVE: -+ case KDBUS_ITEM_NAME_CHANGE: -+ kdbus_printf(" +%s (%llu bytes) '%s', old id=%lld, now id=%lld, old_flags=0x%llx new_flags=0x%llx\n", -+ enum_MSG(item->type), -+ (unsigned long long) item->size, -+ item->name_change.name, -+ item->name_change.old_id.id, -+ item->name_change.new_id.id, -+ item->name_change.old_id.flags, -+ item->name_change.new_id.flags); -+ break; -+ -+ case KDBUS_ITEM_ID_ADD: -+ case KDBUS_ITEM_ID_REMOVE: -+ kdbus_printf(" +%s (%llu bytes) id=%llu flags=%llu\n", -+ enum_MSG(item->type), -+ (unsigned long long) item->size, -+ (unsigned long long) item->id_change.id, -+ (unsigned long long) item->id_change.flags); -+ break; -+ -+ default: -+ kdbus_printf(" +%s (%llu bytes)\n", -+ enum_MSG(item->type), item->size); -+ break; -+ } -+ } -+ -+ if ((char *)item - ((char *)msg + msg->size) >= 8) { -+ kdbus_printf("invalid padding at end of message\n"); -+ ret = -EINVAL; -+ } -+ -+ kdbus_printf("\n"); -+ -+ return ret; -+} -+ -+void kdbus_msg_free(struct kdbus_msg *msg) -+{ -+ const struct kdbus_item *item; -+ int nfds, i; -+ -+ if (!msg) -+ return; -+ -+ KDBUS_ITEM_FOREACH(item, msg, items) { -+ switch (item->type) { -+ /* close all memfds */ -+ case KDBUS_ITEM_PAYLOAD_MEMFD: -+ close(item->memfd.fd); -+ break; -+ case KDBUS_ITEM_FDS: -+ nfds = (item->size - KDBUS_ITEM_HEADER_SIZE) / -+ sizeof(int); -+ -+ for (i = 0; i < nfds; i++) -+ close(item->fds[i]); -+ -+ break; -+ } -+ } -+} -+ -+int kdbus_msg_recv(struct kdbus_conn *conn, -+ struct kdbus_msg **msg_out, -+ uint64_t *offset) -+{ -+ struct kdbus_cmd_recv recv = {}; -+ struct kdbus_msg *msg; -+ int ret; -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv); -+ if (ret < 0) { -+ ret = -errno; -+ return ret; -+ } -+ -+ msg = (struct kdbus_msg *)(conn->buf + recv.offset); -+ ret = kdbus_msg_dump(conn, msg); -+ if (ret < 0) { -+ kdbus_msg_free(msg); -+ return ret; -+ } -+ -+ if (msg_out) { -+ *msg_out = msg; -+ -+ if (offset) -+ *offset = recv.offset; -+ } else { -+ kdbus_msg_free(msg); -+ -+ ret = kdbus_free(conn, recv.offset); -+ if (ret < 0) -+ return ret; -+ } -+ -+ return 0; -+} -+ -+/* -+ * Returns: 0 on success, negative errno on failure. -+ * -+ * We must return -ETIMEDOUT, -ECONNREST, -EAGAIN and other errors. -+ * We must return the result of kdbus_msg_recv() -+ */ -+int kdbus_msg_recv_poll(struct kdbus_conn *conn, -+ int timeout_ms, -+ struct kdbus_msg **msg_out, -+ uint64_t *offset) -+{ -+ int ret; -+ -+ do { -+ struct timeval before, after, diff; -+ struct pollfd fd; -+ -+ fd.fd = conn->fd; -+ fd.events = POLLIN | POLLPRI | POLLHUP; -+ fd.revents = 0; -+ -+ gettimeofday(&before, NULL); -+ ret = poll(&fd, 1, timeout_ms); -+ gettimeofday(&after, NULL); -+ -+ if (ret == 0) { -+ ret = -ETIMEDOUT; -+ break; -+ } -+ -+ if (ret > 0) { -+ if (fd.revents & POLLIN) -+ ret = kdbus_msg_recv(conn, msg_out, offset); -+ -+ if (fd.revents & (POLLHUP | POLLERR)) -+ ret = -ECONNRESET; -+ } -+ -+ if (ret == 0 || ret != -EAGAIN) -+ break; -+ -+ timersub(&after, &before, &diff); -+ timeout_ms -= diff.tv_sec * 1000UL + -+ diff.tv_usec / 1000UL; -+ } while (timeout_ms > 0); -+ -+ return ret; -+} -+ -+int kdbus_free(const struct kdbus_conn *conn, uint64_t offset) -+{ -+ struct kdbus_cmd_free cmd_free; -+ int ret; -+ -+ cmd_free.offset = offset; -+ cmd_free.flags = 0; -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_FREE, &cmd_free); -+ if (ret < 0) { -+ kdbus_printf("KDBUS_CMD_FREE failed: %d (%m)\n", ret); -+ return -errno; -+ } -+ -+ return 0; -+} -+ -+int kdbus_name_acquire(struct kdbus_conn *conn, -+ const char *name, uint64_t *flags) -+{ -+ struct kdbus_cmd_name *cmd_name; -+ size_t name_len = strlen(name) + 1; -+ uint64_t size = sizeof(*cmd_name) + KDBUS_ITEM_SIZE(name_len); -+ struct kdbus_item *item; -+ int ret; -+ -+ cmd_name = alloca(size); -+ -+ memset(cmd_name, 0, size); -+ -+ item = cmd_name->items; -+ item->size = KDBUS_ITEM_HEADER_SIZE + name_len; -+ item->type = KDBUS_ITEM_NAME; -+ strcpy(item->str, name); -+ -+ cmd_name->size = size; -+ if (flags) -+ cmd_name->flags = *flags; -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_NAME_ACQUIRE, cmd_name); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error aquiring name: %s\n", strerror(-ret)); -+ return ret; -+ } -+ -+ kdbus_printf("%s(): flags after call: 0x%llx\n", __func__, -+ cmd_name->flags); -+ -+ if (flags) -+ *flags = cmd_name->flags; -+ -+ return 0; -+} -+ -+int kdbus_name_release(struct kdbus_conn *conn, const char *name) -+{ -+ struct kdbus_cmd_name *cmd_name; -+ size_t name_len = strlen(name) + 1; -+ uint64_t size = sizeof(*cmd_name) + KDBUS_ITEM_SIZE(name_len); -+ struct kdbus_item *item; -+ int ret; -+ -+ cmd_name = alloca(size); -+ -+ memset(cmd_name, 0, size); -+ -+ item = cmd_name->items; -+ item->size = KDBUS_ITEM_HEADER_SIZE + name_len; -+ item->type = KDBUS_ITEM_NAME; -+ strcpy(item->str, name); -+ -+ cmd_name->size = size; -+ -+ kdbus_printf("conn %lld giving up name '%s'\n", -+ (unsigned long long) conn->id, name); -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_NAME_RELEASE, cmd_name); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error releasing name: %s\n", strerror(-ret)); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+int kdbus_name_list(struct kdbus_conn *conn, uint64_t flags) -+{ -+ struct kdbus_cmd_name_list cmd_list; -+ struct kdbus_name_list *list; -+ struct kdbus_name_info *name; -+ int ret; -+ -+ cmd_list.flags = flags; -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_NAME_LIST, &cmd_list); -+ if (ret < 0) { -+ kdbus_printf("error listing names: %d (%m)\n", ret); -+ return EXIT_FAILURE; -+ } -+ -+ kdbus_printf("REGISTRY:\n"); -+ list = (struct kdbus_name_list *)(conn->buf + cmd_list.offset); -+ KDBUS_ITEM_FOREACH(name, list, names) { -+ struct kdbus_item *item; -+ const char *n = "MISSING-NAME"; -+ -+ if (name->size == sizeof(struct kdbus_cmd_name)) -+ continue; -+ -+ KDBUS_ITEM_FOREACH(item, name, items) -+ if (item->type == KDBUS_ITEM_NAME) -+ n = item->str; -+ -+ kdbus_printf("%8llu flags=0x%08llx conn=0x%08llx '%s'\n", -+ name->owner_id, name->flags, name->conn_flags, n); -+ } -+ kdbus_printf("\n"); -+ -+ ret = kdbus_free(conn, cmd_list.offset); -+ -+ return ret; -+} -+ -+int kdbus_conn_update_attach_flags(struct kdbus_conn *conn, uint64_t flags) -+{ -+ int ret; -+ size_t size; -+ struct kdbus_cmd_update *update; -+ struct kdbus_item *item; -+ -+ size = sizeof(struct kdbus_cmd_update); -+ size += KDBUS_ITEM_SIZE(sizeof(uint64_t)); -+ -+ update = malloc(size); -+ if (!update) { -+ ret = -errno; -+ kdbus_printf("error malloc: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ memset(update, 0, size); -+ update->size = size; -+ -+ item = update->items; -+ -+ item->type = KDBUS_ITEM_ATTACH_FLAGS; -+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(uint64_t); -+ item->data64[0] = flags; -+ item = KDBUS_ITEM_NEXT(item); -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_CONN_UPDATE, update); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error conn update: %d (%m)\n", ret); -+ } -+ -+ free(update); -+ -+ return ret; -+} -+ -+int kdbus_conn_update_policy(struct kdbus_conn *conn, const char *name, -+ const struct kdbus_policy_access *access, -+ size_t num_access) -+{ -+ struct kdbus_cmd_update *update; -+ struct kdbus_item *item; -+ size_t i, size; -+ int ret; -+ -+ size = sizeof(struct kdbus_cmd_update); -+ size += KDBUS_ITEM_SIZE(strlen(name) + 1); -+ size += num_access * KDBUS_ITEM_SIZE(sizeof(struct kdbus_policy_access)); -+ -+ update = malloc(size); -+ if (!update) { -+ ret = -errno; -+ kdbus_printf("error malloc: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ memset(update, 0, size); -+ update->size = size; -+ -+ item = update->items; -+ -+ item->type = KDBUS_ITEM_NAME; -+ item->size = KDBUS_ITEM_HEADER_SIZE + strlen(name) + 1; -+ strcpy(item->str, name); -+ item = KDBUS_ITEM_NEXT(item); -+ -+ for (i = 0; i < num_access; i++) { -+ item->size = KDBUS_ITEM_HEADER_SIZE + -+ sizeof(struct kdbus_policy_access); -+ item->type = KDBUS_ITEM_POLICY_ACCESS; -+ -+ item->policy_access.type = access[i].type; -+ item->policy_access.access = access[i].access; -+ item->policy_access.id = access[i].id; -+ -+ item = KDBUS_ITEM_NEXT(item); -+ } -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_CONN_UPDATE, update); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error conn update: %d (%m)\n", ret); -+ } -+ -+ free(update); -+ -+ return ret; -+} -+ -+int kdbus_add_match_empty(struct kdbus_conn *conn) -+{ -+ struct { -+ struct kdbus_cmd_match cmd; -+ struct kdbus_item item; -+ } buf; -+ int ret; -+ -+ memset(&buf, 0, sizeof(buf)); -+ -+ buf.item.size = sizeof(uint64_t) * 3; -+ buf.item.type = KDBUS_ITEM_ID; -+ buf.item.id = KDBUS_MATCH_ID_ANY; -+ -+ buf.cmd.size = sizeof(buf.cmd) + buf.item.size; -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MATCH_ADD, &buf); -+ if (ret < 0) -+ kdbus_printf("--- error adding conn match: %d (%m)\n", ret); -+ -+ return ret; -+} -+ -+int drop_privileges(uid_t uid, gid_t gid) -+{ -+ int ret; -+ -+ ret = setgroups(0, NULL); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error setgroups: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ ret = setresgid(gid, gid, gid); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error setresgid: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ ret = setresuid(uid, uid, uid); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error setresuid: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ return ret; -+} -+ -+static int do_userns_map_id(pid_t pid, -+ const char *map_file, -+ const char *map_id) -+{ -+ int ret; -+ int fd; -+ -+ fd = open(map_file, O_RDWR); -+ if (fd < 0) { -+ ret = -errno; -+ kdbus_printf("error open %s: %d (%m)\n", -+ map_file, ret); -+ return ret; -+ } -+ -+ ret = write(fd, map_id, strlen(map_id)); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error write to %s: %d (%m)\n", -+ map_file, ret); -+ goto out; -+ } -+ -+ ret = 0; -+ -+out: -+ close(fd); -+ return ret; -+} -+ -+int userns_map_uid_gid(pid_t pid, -+ const char *map_uid, -+ const char *map_gid) -+{ -+ int ret; -+ char file_id[128] = {'\0'}; -+ -+ snprintf(file_id, sizeof(file_id), "/proc/%ld/uid_map", -+ (long) pid); -+ -+ ret = do_userns_map_id(pid, file_id, map_uid); -+ if (ret < 0) -+ return ret; -+ -+ snprintf(file_id, sizeof(file_id), "/proc/%ld/gid_map", -+ (long) pid); -+ -+ return do_userns_map_id(pid, file_id, map_gid); -+} -+ -+static int do_cap_get_flag(cap_t caps, cap_value_t cap) -+{ -+ int ret; -+ cap_flag_value_t flag_set; -+ -+ ret = cap_get_flag(caps, cap, CAP_EFFECTIVE, &flag_set); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error cap_get_flag(): %d (%m)\n", ret); -+ return ret; -+ } -+ -+ return (flag_set == CAP_SET); -+} -+ -+/* -+ * Returns: -+ * 1 in case all the requested effective capabilities are set. -+ * 0 in case we do not have the requested capabilities. This value -+ * will be used to abort tests with TEST_SKIP -+ * Negative errno on failure. -+ * -+ * Terminate args with a negative value. -+ */ -+int test_is_capable(int cap, ...) -+{ -+ int ret; -+ va_list ap; -+ cap_t caps; -+ -+ caps = cap_get_proc(); -+ if (!cap) { -+ ret = -errno; -+ kdbus_printf("error cap_get_proc(): %d (%m)\n", ret); -+ return ret; -+ } -+ -+ ret = do_cap_get_flag(caps, (cap_value_t)cap); -+ if (ret <= 0) -+ goto out; -+ -+ va_start(ap, cap); -+ while ((cap = va_arg(ap, int)) > 0) { -+ ret = do_cap_get_flag(caps, (cap_value_t)cap); -+ if (ret <= 0) -+ break; -+ } -+ va_end(ap); -+ -+out: -+ cap_free(caps); -+ return ret; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/kdbus-util.h -@@ -0,0 +1,139 @@ -+/* -+ * Copyright (C) 2013-2014 Kay Sievers -+ * Copyright (C) 2013-2014 Daniel Mack -+ * -+ * 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 -+ -+#define BIT(X) (1 << (X)) -+ -+#include <linux/kdbus.h> -+ -+#define _STRINGIFY(x) #x -+#define STRINGIFY(x) _STRINGIFY(x) -+#define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0])) -+ -+#define KDBUS_PTR(addr) ((void *)(uintptr_t)(addr)) -+ -+#define KDBUS_ALIGN8(l) (((l) + 7) & ~7) -+#define KDBUS_ITEM_HEADER_SIZE offsetof(struct kdbus_item, data) -+#define KDBUS_ITEM_SIZE(s) KDBUS_ALIGN8((s) + KDBUS_ITEM_HEADER_SIZE) -+ -+#define KDBUS_ITEM_NEXT(item) \ -+ (typeof(item))(((uint8_t *)item) + KDBUS_ALIGN8((item)->size)) -+#define KDBUS_ITEM_FOREACH(item, head, first) \ -+ for (item = (head)->first; \ -+ (uint8_t *)(item) < (uint8_t *)(head) + (head)->size; \ -+ item = KDBUS_ITEM_NEXT(item)) -+ -+#define POOL_SIZE (16 * 1024LU * 1024LU) -+ -+#define UNPRIV_UID 65534 -+#define UNPRIV_GID 65534 -+ -+/* Dump as user of process, useful for user namespace testing */ -+#define SUID_DUMP_USER 1 -+ -+extern int kdbus_util_verbose; -+ -+#define kdbus_printf(X...) \ -+ if (kdbus_util_verbose) \ -+ printf(X) -+ -+#define RUN_UNPRIVILEGED(child_uid, child_gid, _child_, _parent_) ({ \ -+ pid_t pid, rpid; \ -+ int ret; \ -+ \ -+ pid = fork(); \ -+ if (pid == 0) { \ -+ ret = drop_privileges(child_uid, child_gid); \ -+ if (ret < 0) \ -+ _exit(ret); \ -+ \ -+ _child_; \ -+ _exit(0); \ -+ } else if (pid > 0) { \ -+ _parent_; \ -+ rpid = waitpid(pid, &ret, 0); \ -+ ASSERT_RETURN(rpid == pid); \ -+ ASSERT_RETURN(WIFEXITED(ret)); \ -+ ASSERT_RETURN(WEXITSTATUS(ret) == 0); \ -+ ret = TEST_OK; \ -+ } else { \ -+ ret = pid; \ -+ } \ -+ \ -+ ret; \ -+ }) -+ -+#define RUN_UNPRIVILEGED_CONN(_var_, _bus_, _code_) \ -+ RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_GID, ({ \ -+ struct kdbus_conn *_var_; \ -+ _var_ = kdbus_hello(_bus_, 0, NULL, 0); \ -+ ASSERT_EXIT(_var_); \ -+ _code_; \ -+ kdbus_conn_free(_var_); \ -+ }), ({ 0; })) -+ -+/* Enums for parent if it should drop privs or not */ -+enum kdbus_drop_parent { -+ DO_NOT_DROP, -+ DROP_SAME_UNPRIV, -+ DROP_OTHER_UNPRIV, -+}; -+ -+struct kdbus_conn { -+ int fd; -+ uint64_t id; -+ void *buf; -+}; -+ -+int sys_memfd_create(const char *name, __u64 size); -+int sys_memfd_seal_set(int fd); -+off_t sys_memfd_get_size(int fd, off_t *size); -+ -+int kdbus_name_list(struct kdbus_conn *conn, uint64_t flags); -+int kdbus_name_release(struct kdbus_conn *conn, const char *name); -+int kdbus_name_acquire(struct kdbus_conn *conn, const char *name, -+ uint64_t *flags); -+void kdbus_msg_free(struct kdbus_msg *msg); -+int kdbus_msg_recv(struct kdbus_conn *conn, -+ struct kdbus_msg **msg, uint64_t *offset); -+int kdbus_msg_recv_poll(struct kdbus_conn *conn, int timeout_ms, -+ struct kdbus_msg **msg_out, uint64_t *offset); -+int kdbus_free(const struct kdbus_conn *conn, uint64_t offset); -+int kdbus_msg_dump(const struct kdbus_conn *conn, -+ const struct kdbus_msg *msg); -+int kdbus_create_bus(int control_fd, const char *name, char **path); -+int kdbus_msg_send(const struct kdbus_conn *conn, const char *name, -+ uint64_t cookie, uint64_t flags, uint64_t timeout, -+ int64_t priority, uint64_t dst_id); -+struct kdbus_conn *kdbus_hello(const char *path, uint64_t hello_flags, -+ const struct kdbus_item *item, -+ size_t item_size); -+struct kdbus_conn *kdbus_hello_registrar(const char *path, const char *name, -+ const struct kdbus_policy_access *access, -+ size_t num_access, uint64_t flags); -+struct kdbus_conn *kdbus_hello_activator(const char *path, const char *name, -+ const struct kdbus_policy_access *access, -+ size_t num_access); -+int kdbus_info(struct kdbus_conn *conn, uint64_t id, -+ const char *name, uint64_t *offset); -+void kdbus_conn_free(struct kdbus_conn *conn); -+int kdbus_conn_update_attach_flags(struct kdbus_conn *conn, uint64_t flags); -+int kdbus_conn_update_policy(struct kdbus_conn *conn, const char *name, -+ const struct kdbus_policy_access *access, -+ size_t num_access); -+ -+int kdbus_add_match_empty(struct kdbus_conn *conn); -+ -+int drop_privileges(uid_t uid, gid_t gid); -+ -+int userns_map_uid_gid(pid_t pid, -+ const char *map_uid, -+ const char *map_gid); -+int test_is_capable(int cap, ...); ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-activator.c -@@ -0,0 +1,317 @@ -+#include <stdio.h> -+#include <string.h> -+#include <time.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stdbool.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <poll.h> -+#include <sys/capability.h> -+#include <sys/types.h> -+#include <sys/ioctl.h> -+#include <sys/wait.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+static int kdbus_starter_poll(struct kdbus_conn *conn) -+{ -+ int ret; -+ struct pollfd fd; -+ -+ fd.fd = conn->fd; -+ fd.events = POLLIN | POLLPRI | POLLHUP; -+ fd.revents = 0; -+ -+ ret = poll(&fd, 1, 100); -+ if (ret == 0) -+ return -ETIMEDOUT; -+ else if (ret > 0) { -+ if (fd.revents & POLLIN) -+ return 0; -+ -+ if (fd.revents & (POLLHUP | POLLERR)) -+ ret = -ECONNRESET; -+ } -+ -+ return ret; -+} -+ -+/* Ensure that kdbus activator logic is safe */ -+static int kdbus_priv_activator(struct kdbus_test_env *env) -+{ -+ int ret; -+ struct kdbus_msg *msg = NULL; -+ uint64_t cookie = 0xdeadbeef; -+ uint64_t flags = KDBUS_NAME_REPLACE_EXISTING; -+ struct kdbus_conn *activator; -+ struct kdbus_conn *service; -+ struct kdbus_conn *client; -+ struct kdbus_conn *holder; -+ struct kdbus_policy_access *access; -+ -+ access = (struct kdbus_policy_access[]){ -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = getuid(), -+ .access = KDBUS_POLICY_OWN, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = getuid(), -+ .access = KDBUS_POLICY_TALK, -+ }, -+ }; -+ -+ activator = kdbus_hello_activator(env->buspath, "foo.priv.activator", -+ access, 2); -+ ASSERT_RETURN(activator); -+ -+ service = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(service); -+ -+ client = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(client); -+ -+ /* -+ * Make sure that other users can't TALK to the activator -+ */ -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ /* Try to talk using the ID */ -+ ret = kdbus_msg_send(unpriv, NULL, 0xdeadbeef, 0, 0, -+ 0, activator->id); -+ ASSERT_EXIT(ret == -ENXIO); -+ -+ /* Try to talk to the name */ -+ ret = kdbus_msg_send(unpriv, "foo.priv.activator", -+ 0xdeadbeef, 0, 0, 0, -+ KDBUS_DST_ID_NAME); -+ ASSERT_EXIT(ret == -EPERM); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* -+ * Make sure that we did not receive anything, so the -+ * service will not be started automatically -+ */ -+ -+ ret = kdbus_starter_poll(activator); -+ ASSERT_RETURN(ret == -ETIMEDOUT); -+ -+ /* -+ * Now try to emulate the starter/service logic and -+ * acquire the name. -+ */ -+ -+ cookie++; -+ ret = kdbus_msg_send(service, "foo.priv.activator", cookie, -+ 0, 0, 0, KDBUS_DST_ID_NAME); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_starter_poll(activator); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Policies are still checked, access denied */ -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "foo.priv.activator", -+ &flags); -+ ASSERT_RETURN(ret == -EPERM); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = kdbus_name_acquire(service, "foo.priv.activator", -+ &flags); -+ ASSERT_RETURN(ret == 0); -+ -+ /* We read our previous starter message */ -+ -+ ret = kdbus_msg_recv_poll(service, 100, NULL, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Try to talk, we still fail */ -+ -+ cookie++; -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ /* Try to talk to the name */ -+ ret = kdbus_msg_send(unpriv, "foo.priv.activator", -+ cookie, 0, 0, 0, -+ KDBUS_DST_ID_NAME); -+ ASSERT_EXIT(ret == -EPERM); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* Still nothing to read */ -+ -+ ret = kdbus_msg_recv_poll(service, 100, NULL, NULL); -+ ASSERT_RETURN(ret == -ETIMEDOUT); -+ -+ /* We receive every thing now */ -+ -+ cookie++; -+ ret = kdbus_msg_send(client, "foo.priv.activator", cookie, -+ 0, 0, 0, KDBUS_DST_ID_NAME); -+ ASSERT_RETURN(ret == 0); -+ ret = kdbus_msg_recv_poll(service, 100, &msg, NULL); -+ ASSERT_RETURN(ret == 0 && msg->cookie == cookie); -+ -+ kdbus_msg_free(msg); -+ kdbus_free(service, msg->offset_reply); -+ -+ /* Policies default to deny TALK now */ -+ kdbus_conn_free(activator); -+ -+ cookie++; -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ /* Try to talk to the name */ -+ ret = kdbus_msg_send(unpriv, "foo.priv.activator", -+ cookie, 0, 0, 0, -+ KDBUS_DST_ID_NAME); -+ ASSERT_EXIT(ret == -EPERM); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = kdbus_msg_recv_poll(service, 100, NULL, NULL); -+ ASSERT_RETURN(ret == -ETIMEDOUT); -+ -+ /* Same user is able to TALK */ -+ cookie++; -+ ret = kdbus_msg_send(client, "foo.priv.activator", cookie, -+ 0, 0, 0, KDBUS_DST_ID_NAME); -+ ASSERT_RETURN(ret == 0); -+ ret = kdbus_msg_recv_poll(service, 100, &msg, NULL); -+ ASSERT_RETURN(ret == 0 && msg->cookie == cookie); -+ -+ kdbus_msg_free(msg); -+ kdbus_free(service, msg->offset_reply); -+ -+ access = (struct kdbus_policy_access []){ -+ { -+ .type = KDBUS_POLICY_ACCESS_WORLD, -+ .id = getuid(), -+ .access = KDBUS_POLICY_TALK, -+ }, -+ }; -+ -+ holder = kdbus_hello_registrar(env->buspath, "foo.priv.activator", -+ access, 1, KDBUS_HELLO_POLICY_HOLDER); -+ ASSERT_RETURN(holder); -+ -+ /* Now we are able to TALK to the name */ -+ -+ cookie++; -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ /* Try to talk to the name */ -+ ret = kdbus_msg_send(unpriv, "foo.priv.activator", -+ cookie, 0, 0, 0, -+ KDBUS_DST_ID_NAME); -+ ASSERT_EXIT(ret == 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = kdbus_msg_recv_poll(service, 100, NULL, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "foo.priv.activator", -+ &flags); -+ ASSERT_RETURN(ret == -EPERM); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ kdbus_conn_free(service); -+ kdbus_conn_free(client); -+ kdbus_conn_free(holder); -+ -+ return 0; -+} -+ -+int kdbus_test_activator(struct kdbus_test_env *env) -+{ -+ int ret; -+ struct kdbus_conn *activator; -+ struct pollfd fds[2]; -+ bool activator_done = false; -+ struct kdbus_policy_access access[2]; -+ -+ access[0].type = KDBUS_POLICY_ACCESS_USER; -+ access[0].id = 1001; -+ access[0].access = KDBUS_POLICY_OWN; -+ -+ access[1].type = KDBUS_POLICY_ACCESS_WORLD; -+ access[1].access = KDBUS_POLICY_TALK; -+ -+ activator = kdbus_hello_activator(env->buspath, "foo.test.activator", -+ access, 2); -+ ASSERT_RETURN(activator); -+ -+ ret = kdbus_add_match_empty(env->conn); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_name_list(env->conn, KDBUS_NAME_LIST_NAMES | -+ KDBUS_NAME_LIST_UNIQUE | -+ KDBUS_NAME_LIST_ACTIVATORS | -+ KDBUS_NAME_LIST_QUEUED); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_send(env->conn, "foo.test.activator", 0xdeafbeef, -+ 0, 0, 0, KDBUS_DST_ID_NAME); -+ ASSERT_RETURN(ret == 0); -+ -+ fds[0].fd = activator->fd; -+ fds[1].fd = env->conn->fd; -+ -+ kdbus_printf("-- entering poll loop ...\n"); -+ -+ for (;;) { -+ int i, nfds = sizeof(fds) / sizeof(fds[0]); -+ -+ for (i = 0; i < nfds; i++) { -+ fds[i].events = POLLIN | POLLPRI; -+ fds[i].revents = 0; -+ } -+ -+ ret = poll(fds, nfds, 3000); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = kdbus_name_list(env->conn, KDBUS_NAME_LIST_NAMES); -+ ASSERT_RETURN(ret == 0); -+ -+ if ((fds[0].revents & POLLIN) && !activator_done) { -+ uint64_t flags = KDBUS_NAME_REPLACE_EXISTING; -+ -+ kdbus_printf("Starter was called back!\n"); -+ -+ ret = kdbus_name_acquire(env->conn, -+ "foo.test.activator", &flags); -+ ASSERT_RETURN(ret == 0); -+ -+ activator_done = true; -+ } -+ -+ if (fds[1].revents & POLLIN) { -+ kdbus_msg_recv(env->conn, NULL, NULL); -+ break; -+ } -+ } -+ -+ /* Check now capabilities, so we run the previous tests */ -+ ret = test_is_capable(CAP_SETUID, CAP_SETGID, -1); -+ ASSERT_RETURN(ret >= 0); -+ -+ if (!ret) -+ return TEST_SKIP; -+ -+ ret = kdbus_priv_activator(env); -+ ASSERT_RETURN(ret == 0); -+ -+ kdbus_conn_free(activator); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-benchmark.c -@@ -0,0 +1,417 @@ -+#include <stdio.h> -+#include <string.h> -+#include <time.h> -+#include <fcntl.h> -+#include <locale.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <stdbool.h> -+#include <errno.h> -+#include <assert.h> -+#include <poll.h> -+#include <sys/time.h> -+#include <sys/ioctl.h> -+#include <sys/mman.h> -+#include <sys/socket.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+#define SERVICE_NAME "foo.bar.echo" -+ -+/* -+ * To have a banchmark comparison with unix socket, set: -+ * user_memfd = false; -+ * compare_uds = true; -+ * attach_none = true; do not attached metadata -+ */ -+ -+static const bool use_memfd = true; /* transmit memfd? */ -+static const bool compare_uds = false; /* unix-socket comparison? */ -+static const bool attach_none = false; /* clear attach-flags? */ -+static char stress_payload[8192]; -+ -+struct stats { -+ uint64_t count; -+ uint64_t latency_acc; -+ uint64_t latency_low; -+ uint64_t latency_high; -+}; -+ -+static struct stats stats; -+ -+static uint64_t now(void) -+{ -+ struct timespec spec; -+ -+ clock_gettime(CLOCK_THREAD_CPUTIME_ID, &spec); -+ return spec.tv_sec * 1000ULL * 1000ULL * 1000ULL + spec.tv_nsec; -+} -+ -+static void reset_stats(void) -+{ -+ stats.count = 0; -+ stats.latency_acc = 0; -+ stats.latency_low = UINT64_MAX; -+ stats.latency_high = 0; -+} -+ -+static void dump_stats(bool is_uds) -+{ -+ if (stats.count > 0) { -+ kdbus_printf("stats %s: %'llu packets processed, latency (nsecs) min/max/avg %'7llu // %'7llu // %'7llu\n", -+ is_uds ? " (UNIX)" : "(KDBUS)", -+ (unsigned long long) stats.count, -+ (unsigned long long) stats.latency_low, -+ (unsigned long long) stats.latency_high, -+ (unsigned long long) (stats.latency_acc / stats.count)); -+ } else { -+ kdbus_printf("*** no packets received. bus stuck?\n"); -+ } -+} -+ -+static void add_stats(uint64_t prev) -+{ -+ uint64_t diff; -+ -+ diff = now() - prev; -+ -+ stats.count++; -+ stats.latency_acc += diff; -+ if (stats.latency_low > diff) -+ stats.latency_low = diff; -+ -+ if (stats.latency_high < diff) -+ stats.latency_high = diff; -+} -+ -+static int setup_simple_kdbus_msg(struct kdbus_conn *conn, -+ uint64_t dst_id, -+ struct kdbus_msg **msg_out) -+{ -+ struct kdbus_msg *msg; -+ struct kdbus_item *item; -+ uint64_t size; -+ -+ size = sizeof(struct kdbus_msg); -+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); -+ -+ msg = malloc(size); -+ ASSERT_RETURN_VAL(msg, -ENOMEM); -+ -+ memset(msg, 0, size); -+ msg->size = size; -+ msg->src_id = conn->id; -+ msg->dst_id = dst_id; -+ msg->payload_type = KDBUS_PAYLOAD_DBUS; -+ -+ item = msg->items; -+ -+ item->type = KDBUS_ITEM_PAYLOAD_VEC; -+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); -+ item->vec.address = (uintptr_t) stress_payload; -+ item->vec.size = sizeof(stress_payload); -+ item = KDBUS_ITEM_NEXT(item); -+ -+ *msg_out = msg; -+ -+ return 0; -+} -+ -+static int setup_memfd_kdbus_msg(struct kdbus_conn *conn, -+ uint64_t dst_id, -+ off_t *memfd_item_offset, -+ struct kdbus_msg **msg_out) -+{ -+ struct kdbus_msg *msg; -+ struct kdbus_item *item; -+ uint64_t size; -+ -+ size = sizeof(struct kdbus_msg); -+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); -+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd)); -+ -+ msg = malloc(size); -+ ASSERT_RETURN_VAL(msg, -ENOMEM); -+ -+ memset(msg, 0, size); -+ msg->size = size; -+ msg->src_id = conn->id; -+ msg->dst_id = dst_id; -+ msg->payload_type = KDBUS_PAYLOAD_DBUS; -+ -+ item = msg->items; -+ -+ item->type = KDBUS_ITEM_PAYLOAD_VEC; -+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); -+ item->vec.address = (uintptr_t) stress_payload; -+ item->vec.size = sizeof(stress_payload); -+ item = KDBUS_ITEM_NEXT(item); -+ -+ item->type = KDBUS_ITEM_PAYLOAD_MEMFD; -+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_memfd); -+ item->memfd.size = sizeof(uint64_t); -+ -+ *memfd_item_offset = (unsigned char *)item - (unsigned char *)msg; -+ *msg_out = msg; -+ -+ return 0; -+} -+ -+static int -+send_echo_request(struct kdbus_conn *conn, uint64_t dst_id, -+ void *kdbus_msg, off_t memfd_item_offset) -+{ -+ int memfd = -1; -+ int ret; -+ -+ if (use_memfd) { -+ uint64_t now_ns = now(); -+ struct kdbus_item *item = memfd_item_offset + kdbus_msg; -+ memfd = sys_memfd_create("memfd-name", 0); -+ ASSERT_RETURN_VAL(memfd >= 0, memfd); -+ -+ ret = write(memfd, &now_ns, sizeof(now_ns)); -+ ASSERT_RETURN_VAL(ret == sizeof(now_ns), -EAGAIN); -+ -+ ret = sys_memfd_seal_set(memfd); -+ ASSERT_RETURN_VAL(ret == 0, -errno); -+ -+ item->memfd.fd = memfd; -+ } -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, kdbus_msg); -+ ASSERT_RETURN_VAL(ret == 0, -errno); -+ -+ close(memfd); -+ -+ return 0; -+} -+ -+static int -+handle_echo_reply(struct kdbus_conn *conn, uint64_t send_ns) -+{ -+ int ret; -+ struct kdbus_cmd_recv recv = {}; -+ struct kdbus_msg *msg; -+ const struct kdbus_item *item; -+ bool has_memfd = false; -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv); -+ if (ret < 0 && errno == EAGAIN) -+ return -EAGAIN; -+ -+ ASSERT_RETURN_VAL(ret == 0, -errno); -+ -+ if (!use_memfd) -+ goto out; -+ -+ msg = (struct kdbus_msg *)(conn->buf + recv.offset); -+ -+ KDBUS_ITEM_FOREACH(item, msg, items) { -+ switch (item->type) { -+ case KDBUS_ITEM_PAYLOAD_MEMFD: { -+ char *buf; -+ -+ buf = mmap(NULL, item->memfd.size, PROT_READ, -+ MAP_PRIVATE, item->memfd.fd, 0); -+ ASSERT_RETURN_VAL(buf != MAP_FAILED, -EINVAL); -+ ASSERT_RETURN_VAL(item->memfd.size == sizeof(uint64_t), -+ -EINVAL); -+ -+ add_stats(*(uint64_t*)buf); -+ munmap(buf, item->memfd.size); -+ close(item->memfd.fd); -+ has_memfd = true; -+ break; -+ } -+ -+ case KDBUS_ITEM_PAYLOAD_OFF: -+ /* ignore */ -+ break; -+ } -+ } -+ -+out: -+ if (!has_memfd) -+ add_stats(send_ns); -+ -+ ret = kdbus_free(conn, recv.offset); -+ ASSERT_RETURN_VAL(ret == 0, -errno); -+ -+ return 0; -+} -+ -+int kdbus_test_benchmark(struct kdbus_test_env *env) -+{ -+ static char buf[sizeof(stress_payload)]; -+ struct kdbus_msg *kdbus_msg = NULL; -+ off_t memfd_cached_offset = 0; -+ int ret; -+ struct kdbus_conn *conn_a, *conn_b; -+ struct pollfd fds[2]; -+ uint64_t start, send_ns, now_ns, diff; -+ unsigned int i; -+ int uds[2]; -+ -+ setlocale(LC_ALL, ""); -+ -+ for (i = 0; i < sizeof(stress_payload); i++) -+ stress_payload[i] = i; -+ -+ /* setup kdbus pair */ -+ -+ conn_a = kdbus_hello(env->buspath, 0, NULL, 0); -+ conn_b = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn_a && conn_b); -+ -+ ret = kdbus_add_match_empty(conn_a); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_add_match_empty(conn_b); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_name_acquire(conn_a, SERVICE_NAME, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ if (attach_none) { -+ ret = kdbus_conn_update_attach_flags(conn_a, 0); -+ ASSERT_RETURN(ret == 0); -+ } -+ -+ /* setup UDS pair */ -+ -+ ret = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_NONBLOCK, 0, uds); -+ ASSERT_RETURN(ret == 0); -+ -+ /* setup a kdbus msg now */ -+ if (use_memfd) { -+ ret = setup_memfd_kdbus_msg(conn_b, conn_a->id, -+ &memfd_cached_offset, -+ &kdbus_msg); -+ ASSERT_RETURN(ret == 0); -+ } else { -+ ret = setup_simple_kdbus_msg(conn_b, conn_a->id, &kdbus_msg); -+ ASSERT_RETURN(ret == 0); -+ } -+ -+ /* start benchmark */ -+ -+ kdbus_printf("-- entering poll loop ...\n"); -+ -+ do { -+ /* run kdbus benchmark */ -+ fds[0].fd = conn_a->fd; -+ fds[1].fd = conn_b->fd; -+ -+ /* cancel any pending message */ -+ handle_echo_reply(conn_a, 0); -+ -+ start = now(); -+ reset_stats(); -+ -+ send_ns = now(); -+ ret = send_echo_request(conn_b, conn_a->id, -+ kdbus_msg, memfd_cached_offset); -+ ASSERT_RETURN(ret == 0); -+ -+ while (1) { -+ unsigned int nfds = sizeof(fds) / sizeof(fds[0]); -+ unsigned int i; -+ -+ for (i = 0; i < nfds; i++) { -+ fds[i].events = POLLIN | POLLPRI | POLLHUP; -+ fds[i].revents = 0; -+ } -+ -+ ret = poll(fds, nfds, 10); -+ if (ret < 0) -+ break; -+ -+ if (fds[0].revents & POLLIN) { -+ ret = handle_echo_reply(conn_a, send_ns); -+ ASSERT_RETURN(ret == 0); -+ -+ send_ns = now(); -+ ret = send_echo_request(conn_b, conn_a->id, -+ kdbus_msg, -+ memfd_cached_offset); -+ ASSERT_RETURN(ret == 0); -+ } -+ -+ now_ns = now(); -+ diff = now_ns - start; -+ if (diff > 1000000000ULL) { -+ start = now_ns; -+ -+ dump_stats(false); -+ break; -+ } -+ } -+ -+ if (!compare_uds) -+ continue; -+ -+ /* run unix-socket benchmark as comparison */ -+ -+ fds[0].fd = uds[0]; -+ fds[1].fd = uds[1]; -+ -+ /* cancel any pendign message */ -+ read(uds[1], buf, sizeof(buf)); -+ -+ start = now(); -+ reset_stats(); -+ -+ send_ns = now(); -+ ret = write(uds[0], stress_payload, sizeof(stress_payload)); -+ ASSERT_RETURN(ret == sizeof(stress_payload)); -+ -+ while (1) { -+ unsigned int nfds = sizeof(fds) / sizeof(fds[0]); -+ unsigned int i; -+ -+ for (i = 0; i < nfds; i++) { -+ fds[i].events = POLLIN | POLLPRI | POLLHUP; -+ fds[i].revents = 0; -+ } -+ -+ ret = poll(fds, nfds, 10); -+ if (ret < 0) -+ break; -+ -+ if (fds[1].revents & POLLIN) { -+ ret = read(uds[1], buf, sizeof(buf)); -+ ASSERT_RETURN(ret == sizeof(buf)); -+ -+ add_stats(send_ns); -+ -+ send_ns = now(); -+ ret = write(uds[0], buf, sizeof(buf)); -+ ASSERT_RETURN(ret == sizeof(buf)); -+ } -+ -+ now_ns = now(); -+ diff = now_ns - start; -+ if (diff > 1000000000ULL) { -+ start = now_ns; -+ -+ dump_stats(true); -+ break; -+ } -+ } -+ -+ } while (kdbus_util_verbose); -+ -+ kdbus_printf("-- closing bus connections\n"); -+ -+ free(kdbus_msg); -+ -+ kdbus_conn_free(conn_a); -+ kdbus_conn_free(conn_b); -+ -+ return (stats.count > 1) ? TEST_OK : TEST_ERR; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-bus.c -@@ -0,0 +1,117 @@ -+#include <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <limits.h> -+#include <sys/ioctl.h> -+#include <sys/mman.h> -+#include <stdbool.h> -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+#include "kdbus-test.h" -+ -+static int test_bus_creator_info(const char *bus_path) -+{ -+ int ret; -+ struct kdbus_conn *conn; -+ struct kdbus_cmd_info cmd = {}; -+ -+ cmd.size = sizeof(cmd); -+ -+ conn = kdbus_hello(bus_path, 0, NULL, 0); -+ ASSERT_RETURN(conn); -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_BUS_CREATOR_INFO, &cmd); -+ ASSERT_RETURN_VAL(ret == 0, ret); -+ -+ ret = kdbus_free(conn, cmd.offset); -+ ASSERT_RETURN_VAL(ret == 0, ret); -+ -+ return 0; -+} -+ -+int kdbus_test_bus_make(struct kdbus_test_env *env) -+{ -+ struct { -+ struct kdbus_cmd_make head; -+ -+ /* bloom size item */ -+ struct { -+ uint64_t size; -+ uint64_t type; -+ struct kdbus_bloom_parameter bloom; -+ } bs; -+ -+ /* name item */ -+ uint64_t n_size; -+ uint64_t n_type; -+ char name[64]; -+ } bus_make; -+ char s[PATH_MAX]; -+ int ret; -+ uid_t uid; -+ -+ env->control_fd = open("/dev/" KBUILD_MODNAME "/control", -+ O_RDWR|O_CLOEXEC); -+ ASSERT_RETURN(env->control_fd >= 0); -+ -+ memset(&bus_make, 0, sizeof(bus_make)); -+ -+ bus_make.bs.size = sizeof(bus_make.bs); -+ bus_make.bs.type = KDBUS_ITEM_BLOOM_PARAMETER; -+ bus_make.bs.bloom.size = 64; -+ bus_make.bs.bloom.n_hash = 1; -+ -+ bus_make.n_type = KDBUS_ITEM_MAKE_NAME; -+ -+ uid = getuid(); -+ -+ /* missing uid prefix */ -+ snprintf(bus_make.name, sizeof(bus_make.name), "foo"); -+ bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1; -+ bus_make.head.size = sizeof(struct kdbus_cmd_make) + -+ sizeof(bus_make.bs) + bus_make.n_size; -+ ret = ioctl(env->control_fd, KDBUS_CMD_BUS_MAKE, &bus_make); -+ ASSERT_RETURN(ret == -1 && errno == EINVAL); -+ -+ /* non alphanumeric character */ -+ snprintf(bus_make.name, sizeof(bus_make.name), "%u-blah@123", uid); -+ bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1; -+ bus_make.head.size = sizeof(struct kdbus_cmd_make) + -+ sizeof(bus_make.bs) + bus_make.n_size; -+ ret = ioctl(env->control_fd, KDBUS_CMD_BUS_MAKE, &bus_make); -+ ASSERT_RETURN(ret == -1 && errno == EINVAL); -+ -+ /* '-' at the end */ -+ snprintf(bus_make.name, sizeof(bus_make.name), "%u-blah-", uid); -+ bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1; -+ bus_make.head.size = sizeof(struct kdbus_cmd_make) + -+ sizeof(bus_make.bs) + bus_make.n_size; -+ ret = ioctl(env->control_fd, KDBUS_CMD_BUS_MAKE, &bus_make); -+ ASSERT_RETURN(ret == -1 && errno == EINVAL); -+ -+ /* create a new bus */ -+ snprintf(bus_make.name, sizeof(bus_make.name), "%u-blah-1", uid); -+ bus_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(bus_make.name) + 1; -+ bus_make.head.size = sizeof(struct kdbus_cmd_make) + -+ sizeof(bus_make.bs) + bus_make.n_size; -+ ret = ioctl(env->control_fd, KDBUS_CMD_BUS_MAKE, &bus_make); -+ ASSERT_RETURN(ret == 0); -+ snprintf(s, sizeof(s), "/dev/" KBUILD_MODNAME "/%u-blah-1/bus", uid); -+ ASSERT_RETURN(access(s, F_OK) == 0); -+ -+ ret = test_bus_creator_info(s); -+ ASSERT_RETURN(ret == 0); -+ -+ /* can't use the same fd for bus make twice */ -+ ret = ioctl(env->control_fd, KDBUS_CMD_BUS_MAKE, &bus_make); -+ ASSERT_RETURN(ret == -1 && errno == EBADFD); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-chat.c -@@ -0,0 +1,123 @@ -+#include <stdio.h> -+#include <string.h> -+#include <time.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <poll.h> -+#include <sys/ioctl.h> -+#include <stdbool.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+int kdbus_test_chat(struct kdbus_test_env *env) -+{ -+ int ret, cookie; -+ struct kdbus_conn *conn_a, *conn_b; -+ struct pollfd fds[2]; -+ uint64_t flags; -+ int count; -+ -+ conn_a = kdbus_hello(env->buspath, 0, NULL, 0); -+ conn_b = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn_a && conn_b); -+ -+ flags = KDBUS_NAME_ALLOW_REPLACEMENT; -+ ret = kdbus_name_acquire(conn_a, "foo.bar.test", &flags); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_name_acquire(conn_a, "foo.bar.baz", NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ flags = KDBUS_NAME_QUEUE; -+ ret = kdbus_name_acquire(conn_b, "foo.bar.baz", &flags); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_name_acquire(conn_a, "foo.bar.double", NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_name_acquire(conn_a, "foo.bar.double", NULL); -+ ASSERT_RETURN(ret == -EALREADY); -+ -+ ret = kdbus_name_release(conn_a, "foo.bar.double"); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_name_release(conn_a, "foo.bar.double"); -+ ASSERT_RETURN(ret == -ESRCH); -+ -+ ret = kdbus_name_list(conn_b, KDBUS_NAME_LIST_UNIQUE | -+ KDBUS_NAME_LIST_NAMES | -+ KDBUS_NAME_LIST_QUEUED | -+ KDBUS_NAME_LIST_ACTIVATORS); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_add_match_empty(conn_a); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_add_match_empty(conn_b); -+ ASSERT_RETURN(ret == 0); -+ -+ cookie = 0; -+ ret = kdbus_msg_send(conn_b, NULL, 0xc0000000 | cookie, 0, 0, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_RETURN(ret == 0); -+ -+ fds[0].fd = conn_a->fd; -+ fds[1].fd = conn_b->fd; -+ -+ kdbus_printf("-- entering poll loop ...\n"); -+ -+ for (count = 0;; count++) { -+ int i, nfds = sizeof(fds) / sizeof(fds[0]); -+ -+ for (i = 0; i < nfds; i++) { -+ fds[i].events = POLLIN | POLLPRI | POLLHUP; -+ fds[i].revents = 0; -+ } -+ -+ ret = poll(fds, nfds, 3000); -+ ASSERT_RETURN(ret >= 0); -+ -+ if (fds[0].revents & POLLIN) { -+ if (count > 2) -+ kdbus_name_release(conn_a, "foo.bar.baz"); -+ -+ ret = kdbus_msg_recv(conn_a, NULL, NULL); -+ ASSERT_RETURN(ret == 0); -+ ret = kdbus_msg_send(conn_a, NULL, -+ 0xc0000000 | cookie++, -+ 0, 0, 0, conn_b->id); -+ ASSERT_RETURN(ret == 0); -+ } -+ -+ if (fds[1].revents & POLLIN) { -+ ret = kdbus_msg_recv(conn_b, NULL, NULL); -+ ASSERT_RETURN(ret == 0); -+ ret = kdbus_msg_send(conn_b, NULL, -+ 0xc0000000 | cookie++, -+ 0, 0, 0, conn_a->id); -+ ASSERT_RETURN(ret == 0); -+ } -+ -+ ret = kdbus_name_list(conn_b, KDBUS_NAME_LIST_UNIQUE | -+ KDBUS_NAME_LIST_NAMES | -+ KDBUS_NAME_LIST_QUEUED | -+ KDBUS_NAME_LIST_ACTIVATORS); -+ ASSERT_RETURN(ret == 0); -+ -+ if (count > 10) -+ break; -+ } -+ -+ kdbus_printf("-- closing bus connections\n"); -+ kdbus_conn_free(conn_a); -+ kdbus_conn_free(conn_b); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-connection.c -@@ -0,0 +1,258 @@ -+#include <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <limits.h> -+#include <sys/ioctl.h> -+#include <sys/mman.h> -+#include <stdbool.h> -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+#include "kdbus-test.h" -+ -+int kdbus_test_hello(struct kdbus_test_env *env) -+{ -+ struct kdbus_cmd_hello hello; -+ int fd, ret; -+ -+ memset(&hello, 0, sizeof(hello)); -+ -+ fd = open(env->buspath, O_RDWR|O_CLOEXEC); -+ if (fd < 0) -+ return TEST_ERR; -+ -+ hello.flags = KDBUS_HELLO_ACCEPT_FD; -+ hello.attach_flags = _KDBUS_ATTACH_ALL; -+ hello.size = sizeof(struct kdbus_cmd_hello); -+ hello.pool_size = POOL_SIZE; -+ -+ /* an unaligned hello must result in -EFAULT */ -+ ret = ioctl(fd, KDBUS_CMD_HELLO, (char *) &hello + 1); -+ ASSERT_RETURN(ret == -1 && errno == EFAULT); -+ -+ /* a size of 0 must return EMSGSIZE */ -+ hello.size = 1; -+ hello.flags = KDBUS_HELLO_ACCEPT_FD; -+ ret = ioctl(fd, KDBUS_CMD_HELLO, &hello); -+ ASSERT_RETURN(ret == -1 && errno == EINVAL); -+ -+ hello.size = sizeof(struct kdbus_cmd_hello); -+ -+ /* check faulty flags */ -+ hello.flags = 1ULL << 32; -+ ret = ioctl(fd, KDBUS_CMD_HELLO, &hello); -+ ASSERT_RETURN(ret == -1 && errno == EINVAL); -+ -+ /* kernel must have set its bit in the ioctl buffer */ -+ ASSERT_RETURN(hello.kernel_flags & KDBUS_FLAG_KERNEL); -+ -+ /* check for faulty pool sizes */ -+ hello.pool_size = 0; -+ hello.flags = KDBUS_HELLO_ACCEPT_FD; -+ ret = ioctl(fd, KDBUS_CMD_HELLO, &hello); -+ ASSERT_RETURN(ret == -1 && errno == EFAULT); -+ -+ hello.pool_size = 4097; -+ ret = ioctl(fd, KDBUS_CMD_HELLO, &hello); -+ ASSERT_RETURN(ret == -1 && errno == EFAULT); -+ -+ hello.pool_size = POOL_SIZE; -+ -+ /* success test */ -+ ret = ioctl(fd, KDBUS_CMD_HELLO, &hello); -+ ASSERT_RETURN(ret == 0); -+ -+ close(fd); -+ -+ fd = open(env->buspath, O_RDWR|O_CLOEXEC); -+ ASSERT_RETURN(fd >= 0); -+ -+ /* no ACTIVATOR flag without a name */ -+ hello.flags = KDBUS_HELLO_ACTIVATOR; -+ ret = ioctl(fd, KDBUS_CMD_HELLO, &hello); -+ ASSERT_RETURN(ret == -1 && errno == EINVAL); -+ -+ close(fd); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_byebye(struct kdbus_test_env *env) -+{ -+ struct kdbus_conn *conn; -+ struct kdbus_cmd_recv recv = {}; -+ int ret; -+ -+ /* create a 2nd connection */ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn != NULL); -+ -+ ret = kdbus_add_match_empty(conn); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_add_match_empty(env->conn); -+ ASSERT_RETURN(ret == 0); -+ -+ /* send over 1st connection */ -+ ret = kdbus_msg_send(env->conn, NULL, 0, 0, 0, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_RETURN(ret == 0); -+ -+ /* say byebye on the 2nd, which must fail */ -+ ret = ioctl(conn->fd, KDBUS_CMD_BYEBYE, 0); -+ ASSERT_RETURN(ret == -1 && errno == EBUSY); -+ -+ /* receive the message */ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_free(conn, recv.offset); -+ ASSERT_RETURN(ret == 0); -+ -+ /* and try again */ -+ ret = ioctl(conn->fd, KDBUS_CMD_BYEBYE, 0); -+ ASSERT_RETURN(ret == 0); -+ -+ /* a 2nd try should result in -EALREADY */ -+ ret = ioctl(conn->fd, KDBUS_CMD_BYEBYE, 0); -+ ASSERT_RETURN(ret == -1 && errno == EALREADY); -+ -+ kdbus_conn_free(conn); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_conn_info(struct kdbus_test_env *env) -+{ -+ int ret; -+ struct { -+ struct kdbus_cmd_info cmd_info; -+ -+ struct { -+ uint64_t size; -+ uint64_t type; -+ char str[64]; -+ } name; -+ } buf; -+ -+ buf.cmd_info.size = sizeof(struct kdbus_cmd_info); -+ buf.cmd_info.flags = 0; -+ buf.cmd_info.id = env->conn->id; -+ -+ ret = ioctl(env->conn->fd, KDBUS_CMD_CONN_INFO, &buf); -+ ASSERT_RETURN(ret == 0); -+ -+ /* try to pass a name that is longer than the buffer's size */ -+ buf.name.size = KDBUS_ITEM_HEADER_SIZE + 1; -+ buf.name.type = KDBUS_ITEM_NAME; -+ strcpy(buf.name.str, "foo.bar.bla"); -+ -+ buf.cmd_info.id = 0; -+ buf.cmd_info.size = sizeof(buf.cmd_info) + buf.name.size; -+ ret = ioctl(env->conn->fd, KDBUS_CMD_CONN_INFO, &buf); -+ ASSERT_RETURN(ret == -1 && errno == EINVAL); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_conn_update(struct kdbus_test_env *env) -+{ -+ const struct kdbus_item *item; -+ struct kdbus_conn *conn; -+ struct kdbus_msg *msg; -+ int found = 0; -+ int ret; -+ -+ /* -+ * kdbus_hello() sets all attach flags. Receive a message by this -+ * connection, and make sure a timestamp item (just to pick one) is -+ * present. -+ */ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn); -+ -+ ret = kdbus_msg_send(env->conn, NULL, 0x12345678, 0, 0, 0, conn->id); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(conn, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ KDBUS_ITEM_FOREACH(item, msg, items) -+ if (item->type == KDBUS_ITEM_TIMESTAMP) -+ found = 1; -+ -+ kdbus_msg_free(msg); -+ -+ ASSERT_RETURN(found == 1); -+ -+ /* -+ * Now, modify the attach flags and repeat the action. The item must -+ * now be missing. -+ */ -+ found = 0; -+ -+ ret = kdbus_conn_update_attach_flags(conn, _KDBUS_ATTACH_ALL & -+ ~KDBUS_ATTACH_TIMESTAMP); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_send(env->conn, NULL, 0x12345678, 0, 0, 0, conn->id); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(conn, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ KDBUS_ITEM_FOREACH(item, msg, items) -+ if (item->type == KDBUS_ITEM_TIMESTAMP) -+ found = 1; -+ -+ ASSERT_RETURN(found == 0); -+ -+ kdbus_msg_free(msg); -+ -+ kdbus_conn_free(conn); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_writable_pool(struct kdbus_test_env *env) -+{ -+ struct kdbus_cmd_hello hello; -+ int fd, ret; -+ void *map; -+ -+ fd = open(env->buspath, O_RDWR | O_CLOEXEC); -+ ASSERT_RETURN(fd >= 0); -+ -+ memset(&hello, 0, sizeof(hello)); -+ hello.flags = KDBUS_HELLO_ACCEPT_FD; -+ hello.attach_flags = _KDBUS_ATTACH_ALL; -+ hello.size = sizeof(struct kdbus_cmd_hello); -+ hello.pool_size = POOL_SIZE; -+ -+ /* success test */ -+ ret = ioctl(fd, KDBUS_CMD_HELLO, &hello); -+ ASSERT_RETURN(ret == 0); -+ -+ /* pools cannot be mapped writable */ -+ map = mmap(NULL, POOL_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); -+ ASSERT_RETURN(map == MAP_FAILED); -+ -+ /* pools can always be mapped readable */ -+ map = mmap(NULL, POOL_SIZE, PROT_READ, MAP_SHARED, fd, 0); -+ ASSERT_RETURN(map != MAP_FAILED); -+ -+ /* make sure we cannot change protection masks to writable */ -+ ret = mprotect(map, POOL_SIZE, PROT_READ | PROT_WRITE); -+ ASSERT_RETURN(ret < 0); -+ -+ munmap(map, POOL_SIZE); -+ close(fd); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-daemon.c -@@ -0,0 +1,66 @@ -+#include <stdio.h> -+#include <string.h> -+#include <time.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <poll.h> -+#include <sys/ioctl.h> -+#include <stdbool.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+int kdbus_test_daemon(struct kdbus_test_env *env) -+{ -+ struct pollfd fds[2]; -+ int count; -+ int ret; -+ -+ /* This test doesn't make any sense in non-interactive mode */ -+ if (!kdbus_util_verbose) -+ return TEST_OK; -+ -+ printf("Created connection %llu on bus '%s'\n", -+ (unsigned long long) env->conn->id, env->buspath); -+ -+ ret = kdbus_name_acquire(env->conn, "com.example.kdbus-test", NULL); -+ ASSERT_RETURN(ret == 0); -+ printf(" Aquired name: com.example.kdbus-test\n"); -+ -+ fds[0].fd = env->conn->fd; -+ fds[1].fd = STDIN_FILENO; -+ -+ printf("Monitoring connections:\n"); -+ -+ for (count = 0;; count++) { -+ int i, nfds = sizeof(fds) / sizeof(fds[0]); -+ -+ for (i = 0; i < nfds; i++) { -+ fds[i].events = POLLIN | POLLPRI | POLLHUP; -+ fds[i].revents = 0; -+ } -+ -+ ret = poll(fds, nfds, -1); -+ if (ret <= 0) -+ break; -+ -+ if (fds[0].revents & POLLIN) { -+ ret = kdbus_msg_recv(env->conn, NULL, NULL); -+ ASSERT_RETURN(ret == 0); -+ } -+ -+ /* stdin */ -+ if (fds[1].revents & POLLIN) -+ break; -+ } -+ -+ printf("Closing bus connection\n"); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-domain.c -@@ -0,0 +1,65 @@ -+#include <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <sys/ioctl.h> -+#include <stdbool.h> -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+#include "kdbus-test.h" -+ -+int kdbus_test_domain_make(struct kdbus_test_env *env) -+{ -+ int fd, fd2; -+ struct { -+ struct kdbus_cmd_make head; -+ -+ /* name item */ -+ uint64_t n_size; -+ uint64_t n_type; -+ char name[64]; -+ } domain_make; -+ int ret; -+ -+ fd = open("/dev/" KBUILD_MODNAME "/control", O_RDWR|O_CLOEXEC); -+ ASSERT_RETURN(fd >= 0); -+ -+ memset(&domain_make, 0, sizeof(domain_make)); -+ -+ domain_make.n_type = KDBUS_ITEM_MAKE_NAME; -+ -+ /* create a new domain */ -+ snprintf(domain_make.name, sizeof(domain_make.name), "blah"); -+ domain_make.n_size = KDBUS_ITEM_HEADER_SIZE + strlen(domain_make.name) + 1; -+ domain_make.head.size = sizeof(struct kdbus_cmd_make) + domain_make.n_size; -+ domain_make.head.flags = 0; -+ ret = ioctl(fd, KDBUS_CMD_DOMAIN_MAKE, &domain_make); -+ if (ret < 0 && errno == EPERM) -+ return TEST_SKIP; -+ ASSERT_RETURN(ret == 0); -+ -+ ASSERT_RETURN(access("/dev/" KBUILD_MODNAME "/domain/blah/control", -+ F_OK) == 0); -+ -+ /* can't use the same fd for domain make twice */ -+ ret = ioctl(fd, KDBUS_CMD_DOMAIN_MAKE, &domain_make); -+ ASSERT_RETURN(ret == -1 && errno == EBADFD); -+ -+ /* can't register the same name twice */ -+ fd2 = open("/dev/" KBUILD_MODNAME "/control", O_RDWR|O_CLOEXEC); -+ ret = ioctl(fd2, KDBUS_CMD_DOMAIN_MAKE, &domain_make); -+ ASSERT_RETURN(ret == -1 && errno == EEXIST); -+ close(fd2); -+ -+ close(fd); -+ ASSERT_RETURN(access("/dev/" KBUILD_MODNAME "/domain/blah/control", -+ F_OK) < 0); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-endpoint.c -@@ -0,0 +1,221 @@ -+#include <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <libgen.h> -+#include <sys/ioctl.h> -+#include <stdbool.h> -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+#include "kdbus-test.h" -+ -+#define KDBUS_SYSNAME_MAX_LEN 63 -+ -+static int install_name_add_match(struct kdbus_conn *conn, const char *name) -+{ -+ struct { -+ struct kdbus_cmd_match cmd; -+ struct { -+ uint64_t size; -+ uint64_t type; -+ struct kdbus_notify_name_change chg; -+ } item; -+ char name[64]; -+ } buf; -+ int ret; -+ -+ /* install the match rule */ -+ memset(&buf, 0, sizeof(buf)); -+ buf.item.type = KDBUS_ITEM_NAME_ADD; -+ buf.item.chg.old_id.id = KDBUS_MATCH_ID_ANY; -+ buf.item.chg.new_id.id = KDBUS_MATCH_ID_ANY; -+ strncpy(buf.name, name, sizeof(buf.name) - 1); -+ buf.item.size = sizeof(buf.item) + strlen(buf.name) + 1; -+ buf.cmd.size = sizeof(buf.cmd) + buf.item.size; -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MATCH_ADD, &buf); -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} -+ -+static int create_endpoint(const char *buspath, const char *name) -+{ -+ struct { -+ struct kdbus_cmd_make head; -+ -+ /* name item */ -+ struct { -+ uint64_t size; -+ uint64_t type; -+ /* max should be KDBUS_SYSNAME_MAX_LEN */ -+ char str[128]; -+ } name; -+ } ep_make; -+ int fd, ret; -+ -+ fd = open(buspath, O_RDWR); -+ if (fd < 0) -+ return fd; -+ -+ memset(&ep_make, 0, sizeof(ep_make)); -+ -+ snprintf(ep_make.name.str, -+ /* Use the KDBUS_SYSNAME_MAX_LEN or sizeof(str) */ -+ KDBUS_SYSNAME_MAX_LEN > strlen(name) ? -+ KDBUS_SYSNAME_MAX_LEN : sizeof(ep_make.name.str), -+ "%u-%s", getuid(), name); -+ -+ ep_make.name.type = KDBUS_ITEM_MAKE_NAME; -+ ep_make.name.size = KDBUS_ITEM_HEADER_SIZE + -+ strlen(ep_make.name.str) + 1; -+ -+ ep_make.head.size = sizeof(ep_make.head) + -+ ep_make.name.size; -+ -+ ret = ioctl(fd, KDBUS_CMD_ENDPOINT_MAKE, &ep_make); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error creating endpoint: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ return fd; -+} -+ -+static int update_endpoint(int fd, const char *name) -+{ -+ int len = strlen(name) + 1; -+ struct { -+ struct kdbus_cmd_update head; -+ -+ /* name item */ -+ struct { -+ uint64_t size; -+ uint64_t type; -+ char str[KDBUS_ALIGN8(len)]; -+ } name; -+ -+ struct { -+ uint64_t size; -+ uint64_t type; -+ struct kdbus_policy_access access; -+ } access; -+ } ep_update; -+ int ret; -+ -+ memset(&ep_update, 0, sizeof(ep_update)); -+ -+ ep_update.name.size = KDBUS_ITEM_HEADER_SIZE + len; -+ ep_update.name.type = KDBUS_ITEM_NAME; -+ strncpy(ep_update.name.str, name, sizeof(ep_update.name.str) - 1); -+ -+ ep_update.access.size = sizeof(ep_update.access); -+ ep_update.access.type = KDBUS_ITEM_POLICY_ACCESS; -+ ep_update.access.access.type = KDBUS_POLICY_ACCESS_WORLD; -+ ep_update.access.access.access = KDBUS_POLICY_SEE; -+ -+ ep_update.head.size = sizeof(ep_update); -+ -+ ret = ioctl(fd, KDBUS_CMD_ENDPOINT_UPDATE, &ep_update); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error updating endpoint: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+int kdbus_test_custom_endpoint(struct kdbus_test_env *env) -+{ -+ char *ep, *tmp; -+ int ret, ep_fd; -+ struct kdbus_msg *msg; -+ struct kdbus_conn *ep_conn; -+ const char *name = "foo.bar.baz"; -+ const char *epname = "foo"; -+ char fake_ep[KDBUS_SYSNAME_MAX_LEN + 1] = {'\0'}; -+ -+ memset(fake_ep, 'X', sizeof(fake_ep) - 1); -+ -+ /* Try to create a custom endpoint with a long name */ -+ ret = create_endpoint(env->buspath, fake_ep); -+ ASSERT_RETURN(ret == -ENAMETOOLONG); -+ -+ /* create a custom endpoint, and open a connection on it */ -+ ep_fd = create_endpoint(env->buspath, "foo"); -+ ASSERT_RETURN(ep_fd >= 0); -+ -+ tmp = strdup(env->buspath); -+ ASSERT_RETURN(tmp); -+ -+ ret = asprintf(&ep, "%s/%u-%s", dirname(tmp), getuid(), epname); -+ free(tmp); -+ ASSERT_RETURN(ret >= 0); -+ -+ ep_conn = kdbus_hello(ep, 0, NULL, 0); -+ ASSERT_RETURN(ep_conn); -+ -+ /* -+ * Add a name add match on the endpoint connection, acquire name from -+ * the unfiltered connection, and make sure the filtered connection -+ * did not get the notification on the name owner change. Also, the -+ * endpoint connection may not be able to call conn_info, neither on -+ * the name nor on the ID. -+ */ -+ ret = install_name_add_match(ep_conn, name); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_name_acquire(env->conn, name, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(ep_conn, NULL, NULL); -+ ASSERT_RETURN(ret == -EAGAIN); -+ -+ ret = kdbus_info(ep_conn, 0, name, NULL); -+ ASSERT_RETURN(ret == -ENOENT); -+ -+ ret = kdbus_info(ep_conn, env->conn->id, NULL, NULL); -+ ASSERT_RETURN(ret == -ENOENT); -+ -+ /* -+ * Release the name again, update the custom endpoint policy, -+ * and try again. This time, the connection on the custom endpoint -+ * should have gotten it. -+ */ -+ ret = kdbus_name_release(env->conn, name); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = update_endpoint(ep_fd, name); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_name_acquire(env->conn, name, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(ep_conn, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_NAME_ADD); -+ ASSERT_RETURN(msg->items[0].name_change.old_id.id == 0); -+ ASSERT_RETURN(msg->items[0].name_change.new_id.id == env->conn->id); -+ ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0); -+ kdbus_msg_free(msg); -+ -+ ret = kdbus_info(ep_conn, 0, name, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_info(ep_conn, env->conn->id, NULL, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ kdbus_conn_free(ep_conn); -+ close(ep_fd); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-fd.c -@@ -0,0 +1,473 @@ -+#include <stdio.h> -+#include <string.h> -+#include <time.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stdbool.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <sys/types.h> -+#include <sys/ioctl.h> -+#include <sys/socket.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+#define KDBUS_MSG_MAX_ITEMS 128 -+#define KDBUS_MSG_MAX_FDS 253 -+#define KDBUS_USER_MAX_CONN 256 -+ -+static int make_msg_payload_dbus(uint64_t src_id, uint64_t dst_id, -+ uint64_t msg_size, -+ struct kdbus_msg **msg_dbus) -+{ -+ struct kdbus_msg *msg; -+ -+ msg = malloc(msg_size); -+ ASSERT_RETURN_VAL(msg, -ENOMEM); -+ -+ memset(msg, 0, msg_size); -+ msg->size = msg_size; -+ msg->src_id = src_id; -+ msg->dst_id = dst_id; -+ msg->payload_type = KDBUS_PAYLOAD_DBUS; -+ -+ *msg_dbus = msg; -+ -+ return 0; -+} -+ -+static void make_item_memfds(struct kdbus_item *item, -+ int *memfds, size_t memfd_size) -+{ -+ size_t i; -+ -+ for (i = 0; i < memfd_size; i++) { -+ item->type = KDBUS_ITEM_PAYLOAD_MEMFD; -+ item->size = KDBUS_ITEM_HEADER_SIZE + -+ sizeof(struct kdbus_memfd); -+ item->memfd.fd = memfds[i]; -+ item->memfd.size = sizeof(uint64_t); /* const size */ -+ item = KDBUS_ITEM_NEXT(item); -+ } -+} -+ -+static void make_item_fds(struct kdbus_item *item, -+ int *fd_array, size_t fd_size) -+{ -+ size_t i; -+ item->type = KDBUS_ITEM_FDS; -+ item->size = KDBUS_ITEM_HEADER_SIZE + (sizeof(int) * fd_size); -+ -+ for (i = 0; i < fd_size; i++) -+ item->fds[i] = fd_array[i]; -+} -+ -+static int memfd_write(const char *name, void *buf, size_t bufsize) -+{ -+ ssize_t ret; -+ int memfd; -+ -+ memfd = sys_memfd_create(name, 0); -+ ASSERT_RETURN_VAL(memfd >= 0, memfd); -+ -+ ret = write(memfd, buf, bufsize); -+ ASSERT_RETURN_VAL(ret == (ssize_t)bufsize, -EAGAIN); -+ -+ ret = sys_memfd_seal_set(memfd); -+ ASSERT_RETURN_VAL(ret == 0, -errno); -+ -+ return memfd; -+} -+ -+static int send_memfds(struct kdbus_conn *conn, uint64_t dst_id, -+ int *memfds_array, size_t memfd_count) -+{ -+ struct kdbus_item *item; -+ struct kdbus_msg *msg; -+ uint64_t size; -+ int ret; -+ -+ size = sizeof(struct kdbus_msg); -+ size += memfd_count * KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd)); -+ -+ if (dst_id == KDBUS_DST_ID_BROADCAST) -+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64; -+ -+ ret = make_msg_payload_dbus(conn->id, dst_id, size, &msg); -+ ASSERT_RETURN_VAL(ret == 0, ret); -+ -+ item = msg->items; -+ -+ if (dst_id == KDBUS_DST_ID_BROADCAST) { -+ item->type = KDBUS_ITEM_BLOOM_FILTER; -+ item->size = KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + 64; -+ item = KDBUS_ITEM_NEXT(item); -+ } -+ -+ make_item_memfds(item, memfds_array, memfd_count); -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, msg); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error sending message: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ free(msg); -+ return 0; -+} -+ -+static int send_fds(struct kdbus_conn *conn, uint64_t dst_id, -+ int *fd_array, size_t fd_count) -+{ -+ struct kdbus_item *item; -+ struct kdbus_msg *msg; -+ uint64_t size; -+ int ret; -+ -+ size = sizeof(struct kdbus_msg); -+ size += KDBUS_ITEM_SIZE(sizeof(int) * fd_count); -+ -+ ret = make_msg_payload_dbus(conn->id, dst_id, size, &msg); -+ ASSERT_RETURN_VAL(ret == 0, ret); -+ -+ item = msg->items; -+ -+ make_item_fds(item, fd_array, fd_count); -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, msg); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error sending message: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ free(msg); -+ return ret; -+} -+ -+static int send_fds_memfds(struct kdbus_conn *conn, uint64_t dst_id, -+ int *fds_array, size_t fd_count, -+ int *memfds_array, size_t memfd_count) -+{ -+ struct kdbus_item *item; -+ struct kdbus_msg *msg; -+ uint64_t size; -+ int ret; -+ -+ size = sizeof(struct kdbus_msg); -+ size += memfd_count * KDBUS_ITEM_SIZE(sizeof(struct kdbus_memfd)); -+ size += KDBUS_ITEM_SIZE(sizeof(int) * fd_count); -+ -+ ret = make_msg_payload_dbus(conn->id, dst_id, size, &msg); -+ ASSERT_RETURN_VAL(ret == 0, ret); -+ -+ item = msg->items; -+ -+ make_item_fds(item, fds_array, fd_count); -+ item = KDBUS_ITEM_NEXT(item); -+ make_item_memfds(item, memfds_array, memfd_count); -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, msg); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error sending message: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ free(msg); -+ return ret; -+} -+ -+/* Return the number of received fds */ -+static unsigned int kdbus_item_get_nfds(struct kdbus_msg *msg) -+{ -+ unsigned int fds = 0; -+ const struct kdbus_item *item; -+ -+ KDBUS_ITEM_FOREACH(item, msg, items) { -+ switch (item->type) { -+ case KDBUS_ITEM_FDS: { -+ fds += (item->size - KDBUS_ITEM_HEADER_SIZE) / -+ sizeof(int); -+ break; -+ } -+ -+ case KDBUS_ITEM_PAYLOAD_MEMFD: -+ fds++; -+ break; -+ -+ default: -+ break; -+ } -+ } -+ -+ return fds; -+} -+ -+static int kdbus_send_multiple_fds(struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst) -+{ -+ int ret, i; -+ unsigned int nfds; -+ int fds[KDBUS_MSG_MAX_FDS + 1]; -+ int memfds[KDBUS_MSG_MAX_ITEMS + 1]; -+ struct kdbus_msg *msg; -+ uint64_t dummy_value; -+ -+ dummy_value = time(NULL); -+ -+ for (i = 0; i < KDBUS_MSG_MAX_FDS + 1; i++) { -+ fds[i] = open("/dev/null", O_RDWR|O_CLOEXEC); -+ ASSERT_RETURN_VAL(fds[i] >= 0, -errno); -+ } -+ -+ /* Send KDBUS_MSG_MAX_FDS with one more fd */ -+ ret = send_fds(conn_src, conn_dst->id, fds, KDBUS_MSG_MAX_FDS + 1); -+ ASSERT_RETURN(ret == -EMFILE); -+ -+ /* Retry with the correct KDBUS_MSG_MAX_FDS */ -+ ret = send_fds(conn_src, conn_dst->id, fds, KDBUS_MSG_MAX_FDS); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(conn_dst, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Check we got the right number of fds */ -+ nfds = kdbus_item_get_nfds(msg); -+ ASSERT_RETURN(nfds == KDBUS_MSG_MAX_FDS); -+ -+ kdbus_msg_free(msg); -+ -+ for (i = 0; i < KDBUS_MSG_MAX_ITEMS + 1; i++, dummy_value++) { -+ memfds[i] = memfd_write("memfd-name", -+ &dummy_value, -+ sizeof(dummy_value)); -+ ASSERT_RETURN_VAL(memfds[i] >= 0, memfds[i]); -+ } -+ -+ /* Send KDBUS_MSG_MAX_FDS with one more memfd */ -+ ret = send_memfds(conn_src, conn_dst->id, -+ memfds, KDBUS_MSG_MAX_ITEMS + 1); -+ ASSERT_RETURN(ret == -E2BIG); -+ -+ /* Retry with the correct KDBUS_MSG_MAX_ITEMS */ -+ ret = send_memfds(conn_src, conn_dst->id, -+ memfds, KDBUS_MSG_MAX_ITEMS); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(conn_dst, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Check we got the right number of fds */ -+ nfds = kdbus_item_get_nfds(msg); -+ ASSERT_RETURN(nfds == KDBUS_MSG_MAX_ITEMS); -+ -+ kdbus_msg_free(msg); -+ -+ -+ /* Combine multiple 254 fds and 100 memfds */ -+ ret = send_fds_memfds(conn_src, conn_dst->id, -+ fds, KDBUS_MSG_MAX_FDS + 1, -+ memfds, 100); -+ ASSERT_RETURN(ret == -EMFILE); -+ -+ /* Combine multiple 253 fds and 128 + 1 memfds */ -+ ret = send_fds_memfds(conn_src, conn_dst->id, -+ fds, KDBUS_MSG_MAX_FDS, -+ memfds, KDBUS_MSG_MAX_ITEMS + 1); -+ ASSERT_RETURN(ret == -E2BIG); -+ -+ ret = send_fds_memfds(conn_src, conn_dst->id, -+ fds, 153, memfds, 100); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(conn_dst, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Check we got the right number of fds */ -+ nfds = kdbus_item_get_nfds(msg); -+ ASSERT_RETURN(nfds == 253); -+ -+ kdbus_msg_free(msg); -+ -+ for (i = 0; i < KDBUS_MSG_MAX_FDS + 1; i++) -+ close(fds[i]); -+ -+ for (i = 0; i < KDBUS_MSG_MAX_ITEMS + 1; i++) -+ close(memfds[i]); -+ -+ return 0; -+} -+ -+int kdbus_test_fd_passing(struct kdbus_test_env *env) -+{ -+ struct kdbus_conn *conn_src, *conn_dst, *conn_dummy; -+ const char *str = "stackenblocken"; -+ const struct kdbus_item *item; -+ struct kdbus_cmd_hello hello; -+ struct kdbus_msg *msg; -+ unsigned int i; -+ time_t now; -+ int fds_conn[2]; -+ int sock_pair[2]; -+ int fds[2]; -+ int memfd; -+ int ret, connfd; -+ -+ now = time(NULL); -+ -+ connfd = open(env->buspath, O_RDWR|O_CLOEXEC); -+ ASSERT_RETURN(connfd >= 0); -+ -+ conn_dummy = malloc(sizeof(*conn_dummy)); -+ ASSERT_RETURN(conn_dummy); -+ -+ /* -+ * Create dummy connection without KDBUS_HELLO_ACCEPT_FD -+ * to test if send fd operations are blocked -+ */ -+ memset(&hello, 0, sizeof(hello)); -+ hello.size = sizeof(struct kdbus_cmd_hello); -+ hello.pool_size = POOL_SIZE; -+ -+ ret = ioctl(connfd, KDBUS_CMD_HELLO, &hello); -+ if (ret < 0) { -+ kdbus_printf("--- error when saying hello: %d (%m)\n", ret); -+ return TEST_ERR; -+ } -+ -+ conn_dummy->fd = connfd; -+ conn_dummy->id = hello.id; -+ -+ /* create two connections */ -+ conn_src = kdbus_hello(env->buspath, 0, NULL, 0); -+ conn_dst = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn_src && conn_dst); -+ -+ fds_conn[0] = conn_src->fd; -+ fds_conn[1] = conn_dst->fd; -+ -+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, sock_pair); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Setup memfd */ -+ memfd = memfd_write("memfd-name", &now, sizeof(now)); -+ ASSERT_RETURN(memfd >= 0); -+ -+ /* Setup pipes */ -+ ret = pipe(fds); -+ ASSERT_RETURN(ret == 0); -+ -+ i = write(fds[1], str, strlen(str)); -+ ASSERT_RETURN(i == strlen(str)); -+ -+ /* -+ * Try to ass the handle of a connection as message payload. -+ * This must fail. -+ */ -+ ret = send_fds(conn_src, conn_dst->id, fds_conn, 2); -+ ASSERT_RETURN(ret == -ENOTSUP); -+ -+ ret = send_fds(conn_dst, conn_src->id, fds_conn, 2); -+ ASSERT_RETURN(ret == -ENOTSUP); -+ -+ ret = send_fds(conn_src, conn_dst->id, sock_pair, 2); -+ ASSERT_RETURN(ret == -ENOTSUP); -+ -+ /* -+ * Send fds to connection that do not accept fd passing -+ */ -+ ret = send_fds(conn_src, conn_dummy->id, fds, 1); -+ ASSERT_RETURN(ret == -ECOMM); -+ -+ ret = send_memfds(conn_src, conn_dummy->id, (int *)&memfd, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Try to broadcast file descriptors. This must fail. */ -+ ret = send_fds(conn_src, KDBUS_DST_ID_BROADCAST, fds, 1); -+ ASSERT_RETURN(ret == -ENOTUNIQ); -+ -+ /* Try to broadcast memfd. This must succeed. */ -+ ret = send_memfds(conn_src, KDBUS_DST_ID_BROADCAST, (int *)&memfd, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Open code this loop */ -+loop_send_fds: -+ -+ /* -+ * Send the read end of the pipe and close it. -+ */ -+ ret = send_fds(conn_src, conn_dst->id, fds, 1); -+ ASSERT_RETURN(ret == 0); -+ close(fds[0]); -+ -+ ret = kdbus_msg_recv(conn_dst, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ KDBUS_ITEM_FOREACH(item, msg, items) { -+ if (item->type == KDBUS_ITEM_FDS) { -+ char tmp[14]; -+ int nfds = (item->size - KDBUS_ITEM_HEADER_SIZE) / -+ sizeof(int); -+ ASSERT_RETURN(nfds == 1); -+ -+ i = read(item->fds[0], tmp, sizeof(tmp)); -+ if (i != 0) { -+ ASSERT_RETURN(i == sizeof(tmp)); -+ ASSERT_RETURN(memcmp(tmp, str, sizeof(tmp)) == 0); -+ -+ /* Write EOF */ -+ close(fds[1]); -+ -+ /* -+ * Resend the read end of the pipe, -+ * the receiver still holds a reference -+ * to it... -+ */ -+ goto loop_send_fds; -+ } -+ -+ /* Got EOF */ -+ -+ /* -+ * Close the last reference to the read end -+ * of the pipe, other references are -+ * automatically closed just after send. -+ */ -+ close(item->fds[0]); -+ } -+ } -+ -+ /* -+ * Try to resend the read end of the pipe. Must fail with -+ * -EBADF since both the sender and receiver closed their -+ * references to it. We assume the above since sender and -+ * receiver are on the same process. -+ */ -+ ret = send_fds(conn_src, conn_dst->id, fds, 1); -+ ASSERT_RETURN(ret == -EBADF); -+ -+ /* Then we clear out received any data... */ -+ kdbus_msg_free(msg); -+ -+ ret = kdbus_send_multiple_fds(conn_src, conn_dst); -+ ASSERT_RETURN(ret == 0); -+ -+ close(sock_pair[0]); -+ close(sock_pair[1]); -+ close(memfd); -+ -+ close(conn_dummy->fd); -+ free(conn_dummy); -+ -+ kdbus_conn_free(conn_src); -+ kdbus_conn_free(conn_dst); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-free.c -@@ -0,0 +1,34 @@ -+#include <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <sys/ioctl.h> -+#include <stdbool.h> -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+#include "kdbus-test.h" -+ -+int kdbus_test_free(struct kdbus_test_env *env) -+{ -+ int ret; -+ struct kdbus_cmd_free cmd_free; -+ -+ /* free an unallocated buffer */ -+ cmd_free.flags = 0; -+ cmd_free.offset = 0; -+ ret = ioctl(env->conn->fd, KDBUS_CMD_FREE, &cmd_free); -+ ASSERT_RETURN(ret == -1 && errno == ENXIO); -+ -+ /* free a buffer out of the pool's bounds */ -+ cmd_free.offset = POOL_SIZE + 1; -+ ret = ioctl(env->conn->fd, KDBUS_CMD_FREE, &cmd_free); -+ ASSERT_RETURN(ret == -1 && errno == ENXIO); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-match.c -@@ -0,0 +1,385 @@ -+#include <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <sys/ioctl.h> -+#include <stdbool.h> -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+#include "kdbus-test.h" -+ -+int kdbus_test_match_id_add(struct kdbus_test_env *env) -+{ -+ struct { -+ struct kdbus_cmd_match cmd; -+ struct { -+ uint64_t size; -+ uint64_t type; -+ struct kdbus_notify_id_change chg; -+ } item; -+ } buf; -+ struct kdbus_conn *conn; -+ struct kdbus_msg *msg; -+ int ret; -+ -+ memset(&buf, 0, sizeof(buf)); -+ -+ buf.cmd.size = sizeof(buf); -+ buf.cmd.cookie = 0xdeafbeefdeaddead; -+ buf.item.size = sizeof(buf.item); -+ buf.item.type = KDBUS_ITEM_ID_ADD; -+ buf.item.chg.id = KDBUS_MATCH_ID_ANY; -+ -+ /* match on id add */ -+ ret = ioctl(env->conn->fd, KDBUS_CMD_MATCH_ADD, &buf); -+ ASSERT_RETURN(ret == 0); -+ -+ /* create 2nd connection */ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn != NULL); -+ -+ /* 1st connection should have received a notification */ -+ ret = kdbus_msg_recv(env->conn, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_ID_ADD); -+ ASSERT_RETURN(msg->items[0].id_change.id == conn->id); -+ -+ kdbus_conn_free(conn); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_match_id_remove(struct kdbus_test_env *env) -+{ -+ struct { -+ struct kdbus_cmd_match cmd; -+ struct { -+ uint64_t size; -+ uint64_t type; -+ struct kdbus_notify_id_change chg; -+ } item; -+ } buf; -+ struct kdbus_conn *conn; -+ struct kdbus_msg *msg; -+ size_t id; -+ int ret; -+ -+ /* create 2nd connection */ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ id = conn->id; -+ ASSERT_RETURN(conn != NULL); -+ -+ memset(&buf, 0, sizeof(buf)); -+ buf.cmd.size = sizeof(buf); -+ buf.cmd.cookie = 0xdeafbeefdeaddead; -+ buf.item.size = sizeof(buf.item); -+ buf.item.type = KDBUS_ITEM_ID_REMOVE; -+ buf.item.chg.id = id; -+ -+ /* register match on 2nd connection */ -+ ret = ioctl(env->conn->fd, KDBUS_CMD_MATCH_ADD, &buf); -+ ASSERT_RETURN(ret == 0); -+ -+ /* remove 2nd connection again */ -+ kdbus_conn_free(conn); -+ -+ /* 1st connection should have received a notification */ -+ ret = kdbus_msg_recv(env->conn, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_ID_REMOVE); -+ ASSERT_RETURN(msg->items[0].id_change.id == id); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_match_name_add(struct kdbus_test_env *env) -+{ -+ struct { -+ struct kdbus_cmd_match cmd; -+ struct { -+ uint64_t size; -+ uint64_t type; -+ struct kdbus_notify_name_change chg; -+ } item; -+ char name[64]; -+ } buf; -+ struct kdbus_msg *msg; -+ char *name; -+ int ret; -+ -+ name = "foo.bla.blaz"; -+ -+ /* install the match rule */ -+ memset(&buf, 0, sizeof(buf)); -+ buf.item.type = KDBUS_ITEM_NAME_ADD; -+ buf.item.chg.old_id.id = KDBUS_MATCH_ID_ANY; -+ buf.item.chg.new_id.id = KDBUS_MATCH_ID_ANY; -+ strncpy(buf.name, name, sizeof(buf.name) - 1); -+ buf.item.size = sizeof(buf.item) + strlen(buf.name) + 1; -+ buf.cmd.size = sizeof(buf.cmd) + buf.item.size; -+ -+ ret = ioctl(env->conn->fd, KDBUS_CMD_MATCH_ADD, &buf); -+ ASSERT_RETURN(ret == 0); -+ -+ /* acquire the name */ -+ ret = kdbus_name_acquire(env->conn, name, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ /* we should have received a notification */ -+ ret = kdbus_msg_recv(env->conn, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_NAME_ADD); -+ ASSERT_RETURN(msg->items[0].name_change.old_id.id == 0); -+ ASSERT_RETURN(msg->items[0].name_change.new_id.id == env->conn->id); -+ ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_match_name_remove(struct kdbus_test_env *env) -+{ -+ struct { -+ struct kdbus_cmd_match cmd; -+ struct { -+ uint64_t size; -+ uint64_t type; -+ struct kdbus_notify_name_change chg; -+ } item; -+ char name[64]; -+ } buf; -+ struct kdbus_msg *msg; -+ char *name; -+ int ret; -+ -+ name = "foo.bla.blaz"; -+ -+ /* acquire the name */ -+ ret = kdbus_name_acquire(env->conn, name, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ /* install the match rule */ -+ memset(&buf, 0, sizeof(buf)); -+ buf.item.type = KDBUS_ITEM_NAME_REMOVE; -+ buf.item.chg.old_id.id = KDBUS_MATCH_ID_ANY; -+ buf.item.chg.new_id.id = KDBUS_MATCH_ID_ANY; -+ strncpy(buf.name, name, sizeof(buf.name) - 1); -+ buf.item.size = sizeof(buf.item) + strlen(buf.name) + 1; -+ buf.cmd.size = sizeof(buf.cmd) + buf.item.size; -+ -+ ret = ioctl(env->conn->fd, KDBUS_CMD_MATCH_ADD, &buf); -+ ASSERT_RETURN(ret == 0); -+ -+ /* release the name again */ -+ kdbus_name_release(env->conn, name); -+ ASSERT_RETURN(ret == 0); -+ -+ /* we should have received a notification */ -+ ret = kdbus_msg_recv(env->conn, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_NAME_REMOVE); -+ ASSERT_RETURN(msg->items[0].name_change.old_id.id == env->conn->id); -+ ASSERT_RETURN(msg->items[0].name_change.new_id.id == 0); -+ ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_match_name_change(struct kdbus_test_env *env) -+{ -+ struct { -+ struct kdbus_cmd_match cmd; -+ struct { -+ uint64_t size; -+ uint64_t type; -+ struct kdbus_notify_name_change chg; -+ } item; -+ char name[64]; -+ } buf; -+ struct kdbus_conn *conn; -+ struct kdbus_msg *msg; -+ uint64_t flags; -+ char *name = "foo.bla.baz"; -+ int ret; -+ -+ /* acquire the name */ -+ ret = kdbus_name_acquire(env->conn, name, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ /* install the match rule */ -+ memset(&buf, 0, sizeof(buf)); -+ buf.item.type = KDBUS_ITEM_NAME_CHANGE; -+ buf.item.chg.old_id.id = KDBUS_MATCH_ID_ANY; -+ buf.item.chg.new_id.id = KDBUS_MATCH_ID_ANY; -+ strncpy(buf.name, name, sizeof(buf.name) - 1); -+ buf.item.size = sizeof(buf.item) + strlen(buf.name) + 1; -+ buf.cmd.size = sizeof(buf.cmd) + buf.item.size; -+ -+ ret = ioctl(env->conn->fd, KDBUS_CMD_MATCH_ADD, &buf); -+ ASSERT_RETURN(ret == 0); -+ -+ /* create a 2nd connection */ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn != NULL); -+ -+ /* allow the new connection to own the same name */ -+ /* queue the 2nd connection as waiting owner */ -+ flags = KDBUS_NAME_QUEUE; -+ ret = kdbus_name_acquire(conn, name, &flags); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(flags & KDBUS_NAME_IN_QUEUE); -+ -+ /* release name from 1st connection */ -+ ret = kdbus_name_release(env->conn, name); -+ ASSERT_RETURN(ret == 0); -+ -+ /* we should have received a notification */ -+ ret = kdbus_msg_recv(env->conn, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ASSERT_RETURN(msg->items[0].type == KDBUS_ITEM_NAME_CHANGE); -+ ASSERT_RETURN(msg->items[0].name_change.old_id.id == env->conn->id); -+ ASSERT_RETURN(msg->items[0].name_change.new_id.id == conn->id); -+ ASSERT_RETURN(strcmp(msg->items[0].name_change.name, name) == 0); -+ -+ kdbus_conn_free(conn); -+ -+ return TEST_OK; -+} -+ -+static int send_bloom_filter(const struct kdbus_conn *conn, -+ uint64_t cookie, -+ const uint8_t *filter, -+ size_t filter_size, -+ uint64_t filter_generation) -+{ -+ struct kdbus_msg *msg; -+ struct kdbus_item *item; -+ uint64_t size; -+ int ret; -+ -+ size = sizeof(struct kdbus_msg); -+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + filter_size; -+ -+ msg = alloca(size); -+ -+ memset(msg, 0, size); -+ msg->size = size; -+ msg->src_id = conn->id; -+ msg->dst_id = KDBUS_DST_ID_BROADCAST; -+ msg->payload_type = KDBUS_PAYLOAD_DBUS; -+ msg->cookie = cookie; -+ -+ item = msg->items; -+ item->type = KDBUS_ITEM_BLOOM_FILTER; -+ item->size = KDBUS_ITEM_SIZE(sizeof(struct kdbus_bloom_filter)) + -+ filter_size; -+ -+ item->bloom_filter.generation = filter_generation; -+ memcpy(item->bloom_filter.data, filter, filter_size); -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, msg); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error sending message: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ return 0; -+} -+ -+int kdbus_test_match_bloom(struct kdbus_test_env *env) -+{ -+ struct { -+ struct kdbus_cmd_match cmd; -+ struct { -+ uint64_t size; -+ uint64_t type; -+ uint8_t data_gen0[64]; -+ uint8_t data_gen1[64]; -+ } item; -+ } buf; -+ struct kdbus_conn *conn; -+ struct kdbus_msg *msg; -+ uint64_t cookie = 0xf000f00f; -+ uint8_t filter[64]; -+ int ret; -+ -+ /* install the match rule */ -+ memset(&buf, 0, sizeof(buf)); -+ buf.cmd.size = sizeof(buf); -+ -+ buf.item.size = sizeof(buf.item); -+ buf.item.type = KDBUS_ITEM_BLOOM_MASK; -+ buf.item.data_gen0[0] = 0x55; -+ buf.item.data_gen0[63] = 0x80; -+ -+ buf.item.data_gen1[1] = 0xaa; -+ buf.item.data_gen1[9] = 0x02; -+ -+ ret = ioctl(env->conn->fd, KDBUS_CMD_MATCH_ADD, &buf); -+ ASSERT_RETURN(ret == 0); -+ -+ /* create a 2nd connection */ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn != NULL); -+ -+ /* a message with a 0'ed out filter must not reach the other peer */ -+ memset(filter, 0, sizeof(filter)); -+ ret = send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 0); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(env->conn, &msg, NULL); -+ ASSERT_RETURN(ret == -EAGAIN); -+ -+ /* now set the filter to the connection's mask and expect success */ -+ filter[0] = 0x55; -+ filter[63] = 0x80; -+ ret = send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 0); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(env->conn, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(msg->cookie == cookie); -+ -+ /* broaden the filter and try again. this should also succeed. */ -+ filter[0] = 0xff; -+ filter[8] = 0xff; -+ filter[63] = 0xff; -+ ret = send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 0); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(env->conn, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(msg->cookie == cookie); -+ -+ /* the same filter must not match against bloom generation 1 */ -+ ret = send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(env->conn, &msg, NULL); -+ ASSERT_RETURN(ret == -EAGAIN); -+ -+ /* set a different filter and try again */ -+ filter[1] = 0xaa; -+ filter[9] = 0x02; -+ ret = send_bloom_filter(conn, ++cookie, filter, sizeof(filter), 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv(env->conn, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(msg->cookie == cookie); -+ -+ kdbus_conn_free(conn); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-message.c -@@ -0,0 +1,126 @@ -+#include <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <sys/ioctl.h> -+#include <stdbool.h> -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+#include "kdbus-test.h" -+ -+int kdbus_test_message_basic(struct kdbus_test_env *env) -+{ -+ struct kdbus_conn *conn; -+ struct kdbus_msg *msg; -+ uint64_t cookie = 0x1234abcd5678eeff; -+ uint64_t offset; -+ int ret; -+ -+ /* create a 2nd connection */ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn != NULL); -+ -+ ret = kdbus_add_match_empty(conn); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_add_match_empty(env->conn); -+ ASSERT_RETURN(ret == 0); -+ -+ /* send over 1st connection */ -+ ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_RETURN(ret == 0); -+ -+ /* ... and receive on the 2nd */ -+ ret = kdbus_msg_recv_poll(conn, 100, &msg, &offset); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(msg->cookie == cookie); -+ -+ kdbus_msg_free(msg); -+ -+ ret = kdbus_free(conn, offset); -+ ASSERT_RETURN(ret == 0); -+ -+ kdbus_conn_free(conn); -+ -+ return TEST_OK; -+} -+ -+static int msg_recv_prio(struct kdbus_conn *conn, -+ int64_t requested_prio, -+ int64_t expected_prio) -+{ -+ struct kdbus_cmd_recv recv = { -+ .flags = KDBUS_RECV_USE_PRIORITY, -+ .priority = requested_prio, -+ }; -+ struct kdbus_msg *msg; -+ int ret; -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv); -+ if (ret < 0) { -+ kdbus_printf("error receiving message: %d (%m)\n", -errno); -+ return -errno; -+ } -+ -+ msg = (struct kdbus_msg *)(conn->buf + recv.offset); -+ kdbus_msg_dump(conn, msg); -+ -+ if (msg->priority != expected_prio) { -+ kdbus_printf("expected message prio %lld, got %lld\n", -+ (unsigned long long) expected_prio, -+ (unsigned long long) msg->priority); -+ return -EINVAL; -+ } -+ -+ kdbus_msg_free(msg); -+ ret = kdbus_free(conn, recv.offset); -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} -+ -+int kdbus_test_message_prio(struct kdbus_test_env *env) -+{ -+ struct kdbus_conn *a, *b; -+ uint64_t cookie = 0; -+ -+ a = kdbus_hello(env->buspath, 0, NULL, 0); -+ b = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(a && b); -+ -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 25, a->id) == 0); -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -600, a->id) == 0); -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 10, a->id) == 0); -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -35, a->id) == 0); -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -100, a->id) == 0); -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 20, a->id) == 0); -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -15, a->id) == 0); -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -800, a->id) == 0); -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -150, a->id) == 0); -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, 10, a->id) == 0); -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -800, a->id) == 0); -+ ASSERT_RETURN(kdbus_msg_send(b, NULL, ++cookie, 0, 0, -10, a->id) == 0); -+ -+ ASSERT_RETURN(msg_recv_prio(a, -200, -800) == 0); -+ ASSERT_RETURN(msg_recv_prio(a, -100, -800) == 0); -+ ASSERT_RETURN(msg_recv_prio(a, -400, -600) == 0); -+ ASSERT_RETURN(msg_recv_prio(a, -400, -600) == -ENOMSG); -+ ASSERT_RETURN(msg_recv_prio(a, 10, -150) == 0); -+ ASSERT_RETURN(msg_recv_prio(a, 10, -100) == 0); -+ -+ kdbus_printf("--- get priority (all)\n"); -+ ASSERT_RETURN(kdbus_msg_recv(a, NULL, NULL) == 0); -+ -+ kdbus_conn_free(a); -+ kdbus_conn_free(b); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-metadata-ns.c -@@ -0,0 +1,236 @@ -+/* Test metadata in new namespaces */ -+ -+#include <stdio.h> -+#include <string.h> -+#include <sched.h> -+#include <time.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <signal.h> -+#include <sys/wait.h> -+#include <sys/ioctl.h> -+#include <sys/prctl.h> -+#include <sys/eventfd.h> -+#include <sys/syscall.h> -+#include <sys/capability.h> -+#include <linux/sched.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+static int __kdbus_clone_userns_test(const char *bus, struct kdbus_conn *conn) -+{ -+ int efd = -1; -+ pid_t pid; -+ int ret; -+ int status; -+ unsigned int uid = 65534; -+ int test_status = TEST_ERR; -+ -+ ret = drop_privileges(uid, uid); -+ if (ret < 0) -+ goto out; -+ -+ /** -+ * Since we just dropped privileges, the dumpable flag was just -+ * cleared which makes the /proc/$clone_child/uid_map to be -+ * owned by root, hence any userns uid mapping will fail with -+ * -EPERM since the mapping will be done by uid 65534. -+ * -+ * To avoid this set the dumpable flag again which makes procfs -+ * update the /proc/$clone_child/ inodes owner to 65534. -+ * -+ * Using this we will be able write to /proc/$clone_child/uid_map -+ * as uid 65534 and map the uid 65534 to 0 inside the user -+ * namespace. -+ */ -+ ret = prctl(PR_SET_DUMPABLE, SUID_DUMP_USER); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error prctl: %d (%m)\n", ret); -+ goto out; -+ } -+ -+ /* sync with parent */ -+ efd = eventfd(0, EFD_CLOEXEC); -+ if (efd < 0) { -+ ret = -errno; -+ kdbus_printf("error eventfd: %d (%m)\n", ret); -+ goto out; -+ } -+ -+ pid = syscall(__NR_clone, SIGCHLD | CLONE_NEWUSER, NULL); -+ if (pid < 0) { -+ ret = -errno; -+ kdbus_printf("error clone: %d (%m)\n", ret); -+ -+ /* Unprivileged can't create user namespace ? */ -+ if (ret == -EPERM) { -+ kdbus_printf("-- CLONE_NEWUSER TEST Failed for " -+ "uid: %u\n -- Make sure that your kernel " -+ "do not allow CLONE_NEWUSER for " -+ "unprivileged users\n", -+ uid); -+ test_status = TEST_SKIP; -+ } -+ -+ goto out; -+ } -+ -+ if (pid == 0) { -+ struct kdbus_conn *conn_src; -+ eventfd_t event_status = 0; -+ -+ setbuf(stdout, NULL); -+ ret = prctl(PR_SET_PDEATHSIG, SIGKILL); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error prctl: %d (%m)\n", ret); -+ _exit(TEST_ERR); -+ } -+ -+ ret = eventfd_read(efd, &event_status); -+ if (ret < 0 || event_status != 1) -+ _exit(TEST_ERR); -+ -+ /* ping connection from the new user namespace */ -+ conn_src = kdbus_hello(bus, 0, NULL, 0); -+ ASSERT_EXIT(conn_src); -+ -+ ret = kdbus_add_match_empty(conn_src); -+ ASSERT_EXIT(ret == 0); -+ -+ ret = kdbus_msg_send(conn_src, NULL, 0xabcd1234, -+ 0, 0, 0, conn->id); -+ ASSERT_EXIT(ret == 0); -+ -+ kdbus_conn_free(conn_src); -+ _exit(TEST_OK); -+ } -+ -+ ret = userns_map_uid_gid(pid, "0 65534 1", "0 65534 1"); -+ if (ret < 0) { -+ /* send error to child */ -+ eventfd_write(efd, 2); -+ kdbus_printf("error mapping uid/gid in new user namespace\n"); -+ goto out; -+ } -+ -+ ret = eventfd_write(efd, 1); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error eventfd_write: %d (%m)\n", ret); -+ goto out; -+ } -+ -+ ret = waitpid(pid, &status, 0); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error waitpid: %d (%m)\n", ret); -+ goto out; -+ } -+ -+ if (WIFEXITED(status)) -+ test_status = WEXITSTATUS(status); -+ -+out: -+ if (efd != -1) -+ close(efd); -+ -+ return test_status; -+} -+ -+static int kdbus_clone_userns_test(const char *bus, struct kdbus_conn *conn) -+{ -+ int ret; -+ pid_t pid; -+ int status; -+ struct kdbus_msg *msg; -+ const struct kdbus_item *item; -+ -+ kdbus_printf("STARTING TEST 'chat' in a new user namespace.\n"); -+ -+ setbuf(stdout, NULL); -+ pid = fork(); -+ ASSERT_RETURN_VAL(pid >= 0, -errno); -+ -+ if (pid == 0) { -+ ret = prctl(PR_SET_PDEATHSIG, SIGKILL); -+ ASSERT_EXIT_VAL(ret == 0, -errno); -+ -+ ret = __kdbus_clone_userns_test(bus, conn); -+ _exit(ret); -+ } -+ -+ /* Receive in the original (root privileged) user namespace */ -+ ret = kdbus_msg_recv_poll(conn, 100, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ KDBUS_ITEM_FOREACH(item, msg, items) -+ if (item->type >= _KDBUS_ITEM_ATTACH_BASE && -+ item->type < _KDBUS_ITEM_POLICY_BASE) { -+ kdbus_printf("Unexpected item of type %llx\n", -+ item->type); -+ ASSERT_RETURN_VAL(0, -EINVAL); -+ } -+ -+ kdbus_msg_free(msg); -+ ret = waitpid(pid, &status, 0); -+ ASSERT_RETURN(ret >= 0); -+ -+ if (WIFEXITED(status)) -+ return WEXITSTATUS(status); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_metadata_ns(struct kdbus_test_env *env) -+{ -+ int ret; -+ struct kdbus_conn *holder, *conn; -+ struct kdbus_policy_access policy_access = { -+ /* Allow world so we can inspect metadata in namespace */ -+ .type = KDBUS_POLICY_ACCESS_WORLD, -+ .id = geteuid(), -+ .access = KDBUS_POLICY_TALK, -+ }; -+ -+ /* we require user-namespaces */ -+ if (access("/proc/self/uid_map", F_OK) != 0) -+ return TEST_SKIP; -+ -+ ret = test_is_capable(CAP_SETUID, CAP_SETGID, CAP_SYS_ADMIN, -1); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* no enough privileges, SKIP test */ -+ if (!ret) -+ return TEST_SKIP; -+ -+ holder = kdbus_hello_registrar(env->buspath, "com.example.metadata", -+ &policy_access, 1, -+ KDBUS_HELLO_POLICY_HOLDER); -+ ASSERT_RETURN(holder); -+ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn); -+ -+ ret = kdbus_add_match_empty(conn); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_name_acquire(conn, "com.example.metadata", NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ ret = kdbus_clone_userns_test(env->buspath, conn); -+ ASSERT_RETURN(ret == 0); -+ -+ kdbus_conn_free(holder); -+ kdbus_conn_free(conn); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-monitor.c -@@ -0,0 +1,156 @@ -+#include <stdio.h> -+#include <string.h> -+#include <time.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <stdbool.h> -+#include <errno.h> -+#include <assert.h> -+#include <signal.h> -+#include <sys/time.h> -+#include <sys/ioctl.h> -+#include <sys/mman.h> -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+#include "kdbus-test.h" -+ -+static bool kdbus_item_in_message(struct kdbus_msg *msg, -+ uint64_t type) -+{ -+ const struct kdbus_item *item; -+ -+ KDBUS_ITEM_FOREACH(item, msg, items) -+ if (item->type == type) -+ return true; -+ -+ return false; -+} -+ -+int kdbus_test_monitor(struct kdbus_test_env *env) -+{ -+ struct kdbus_conn *monitor, *conn; -+ unsigned int cookie = 0xdeadbeef; -+ struct kdbus_msg *msg; -+ uint64_t offset = 0; -+ int ret; -+ -+ monitor = kdbus_hello(env->buspath, KDBUS_HELLO_MONITOR, NULL, 0); -+ ASSERT_RETURN(monitor); -+ -+ /* check that we can acquire a name */ -+ ret = kdbus_name_acquire(monitor, "foo.bar.baz", NULL); -+ ASSERT_RETURN(ret == -EOPNOTSUPP); -+ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn); -+ -+ ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0, 0, conn->id); -+ ASSERT_RETURN(ret == 0); -+ -+ /* the recipient should have got the message */ -+ ret = kdbus_msg_recv(conn, &msg, &offset); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(msg->cookie == cookie); -+ kdbus_msg_free(msg); -+ kdbus_free(conn, offset); -+ -+ /* and so should the monitor */ -+ ret = kdbus_msg_recv(monitor, &msg, &offset); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(msg->cookie == cookie); -+ -+ kdbus_msg_free(msg); -+ kdbus_free(monitor, offset); -+ -+ cookie++; -+ ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_RETURN(ret == 0); -+ -+ /* The monitor did not install matches, this will timeout */ -+ ret = kdbus_msg_recv_poll(monitor, 100, NULL, NULL); -+ ASSERT_RETURN(ret == -ETIMEDOUT); -+ -+ /* Install empty match for monitor */ -+ ret = kdbus_add_match_empty(monitor); -+ ASSERT_RETURN(ret == 0); -+ -+ cookie++; -+ ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_RETURN(ret == 0); -+ -+ /* The monitor should get the message now. */ -+ ret = kdbus_msg_recv_poll(monitor, 100, &msg, &offset); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(msg->cookie == cookie); -+ -+ kdbus_msg_free(msg); -+ kdbus_free(monitor, offset); -+ -+ /* -+ * Since we are the only monitor, update the attach flags -+ * and tell we are not interessted in attach flags -+ */ -+ -+ ret = kdbus_conn_update_attach_flags(monitor, 0); -+ ASSERT_RETURN(ret == 0); -+ -+ cookie++; -+ ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv_poll(monitor, 100, &msg, &offset); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(msg->cookie == cookie); -+ -+ ret = kdbus_item_in_message(msg, KDBUS_ITEM_TIMESTAMP); -+ ASSERT_RETURN(ret == 0); -+ -+ kdbus_msg_free(msg); -+ kdbus_free(monitor, offset); -+ -+ /* -+ * Now we are interested in KDBUS_ITEM_TIMESTAMP and -+ * KDBUS_ITEM_CREDS -+ */ -+ ret = kdbus_conn_update_attach_flags(monitor, -+ KDBUS_ATTACH_TIMESTAMP | -+ KDBUS_ATTACH_CREDS); -+ ASSERT_RETURN(ret == 0); -+ -+ cookie++; -+ ret = kdbus_msg_send(env->conn, NULL, cookie, 0, 0, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv_poll(monitor, 100, &msg, &offset); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(msg->cookie == cookie); -+ -+ ret = kdbus_item_in_message(msg, KDBUS_ITEM_TIMESTAMP); -+ ASSERT_RETURN(ret == 1); -+ -+ ret = kdbus_item_in_message(msg, KDBUS_ITEM_CREDS); -+ ASSERT_RETURN(ret == 1); -+ -+ /* the KDBUS_ITEM_PID_COMM was not requested */ -+ ret = kdbus_item_in_message(msg, KDBUS_ITEM_PID_COMM); -+ ASSERT_RETURN(ret == 0); -+ -+ kdbus_msg_free(msg); -+ kdbus_free(monitor, offset); -+ -+ kdbus_conn_free(monitor); -+ kdbus_conn_free(conn); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-names.c -@@ -0,0 +1,184 @@ -+#include <stdio.h> -+#include <string.h> -+#include <time.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <limits.h> -+#include <sys/ioctl.h> -+#include <getopt.h> -+#include <stdbool.h> -+ -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+#include "kdbus-test.h" -+ -+static int conn_is_name_owner(const struct kdbus_conn *conn, -+ const char *needle) -+{ -+ struct kdbus_cmd_name_list cmd_list; -+ struct kdbus_name_list *list; -+ struct kdbus_name_info *name; -+ bool found = false; -+ int ret; -+ -+ cmd_list.flags = KDBUS_NAME_LIST_NAMES; -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_NAME_LIST, &cmd_list); -+ ASSERT_RETURN(ret == 0); -+ -+ list = (struct kdbus_name_list *)(conn->buf + cmd_list.offset); -+ KDBUS_ITEM_FOREACH(name, list, names) { -+ struct kdbus_item *item; -+ const char *n = NULL; -+ -+ KDBUS_ITEM_FOREACH(item, name, items) -+ if (item->type == KDBUS_ITEM_NAME) -+ n = item->str; -+ -+ if (name->owner_id == conn->id && -+ n && strcmp(needle, n) == 0) { -+ found = true; -+ break; -+ } -+ } -+ -+ ret = kdbus_free(conn, cmd_list.offset); -+ ASSERT_RETURN(ret == 0); -+ -+ return found ? 0 : -1; -+} -+ -+int kdbus_test_name_basic(struct kdbus_test_env *env) -+{ -+ char *name, *dot_name, *invalid_name, *wildcard_name; -+ int ret; -+ -+ name = "foo.bla.blaz"; -+ dot_name = ".bla.blaz"; -+ invalid_name = "foo"; -+ wildcard_name = "foo.bla.bl.*"; -+ -+ /* Name is not valid, must fail */ -+ ret = kdbus_name_acquire(env->conn, dot_name, NULL); -+ ASSERT_RETURN(ret == -EINVAL); -+ -+ ret = kdbus_name_acquire(env->conn, invalid_name, NULL); -+ ASSERT_RETURN(ret == -EINVAL); -+ -+ ret = kdbus_name_acquire(env->conn, wildcard_name, NULL); -+ ASSERT_RETURN(ret == -EINVAL); -+ -+ /* check that we can acquire a name */ -+ ret = kdbus_name_acquire(env->conn, name, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = conn_is_name_owner(env->conn, name); -+ ASSERT_RETURN(ret == 0); -+ -+ /* ... and release it again */ -+ ret = kdbus_name_release(env->conn, name); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = conn_is_name_owner(env->conn, name); -+ ASSERT_RETURN(ret != 0); -+ -+ /* check that we can't release it again */ -+ ret = kdbus_name_release(env->conn, name); -+ ASSERT_RETURN(ret == -ESRCH); -+ -+ /* check that we can't release a name that we don't own */ -+ ret = kdbus_name_release(env->conn, "foo.bar.xxx"); -+ ASSERT_RETURN(ret == -ESRCH); -+ -+ /* Name is not valid, must fail */ -+ ret = kdbus_name_release(env->conn, dot_name); -+ ASSERT_RETURN(ret == -EINVAL); -+ -+ ret = kdbus_name_release(env->conn, invalid_name); -+ ASSERT_RETURN(ret == -EINVAL); -+ -+ ret = kdbus_name_release(env->conn, wildcard_name); -+ ASSERT_RETURN(ret == -EINVAL); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_name_conflict(struct kdbus_test_env *env) -+{ -+ struct kdbus_conn *conn; -+ char *name; -+ int ret; -+ -+ name = "foo.bla.blaz"; -+ -+ /* create a 2nd connection */ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn != NULL); -+ -+ /* allow the new connection to own the same name */ -+ /* acquire name from the 1st connection */ -+ ret = kdbus_name_acquire(env->conn, name, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = conn_is_name_owner(env->conn, name); -+ ASSERT_RETURN(ret == 0); -+ -+ /* check that we can't acquire it again from the 1st connection */ -+ ret = kdbus_name_acquire(env->conn, name, NULL); -+ ASSERT_RETURN(ret == -EALREADY); -+ -+ /* check that we also can't acquire it again from the 2nd connection */ -+ ret = kdbus_name_acquire(conn, name, NULL); -+ ASSERT_RETURN(ret == -EEXIST); -+ -+ kdbus_conn_free(conn); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_name_queue(struct kdbus_test_env *env) -+{ -+ struct kdbus_conn *conn; -+ const char *name; -+ uint64_t flags; -+ int ret; -+ -+ name = "foo.bla.blaz"; -+ -+ flags = KDBUS_NAME_ALLOW_REPLACEMENT; -+ -+ /* create a 2nd connection */ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn != NULL); -+ -+ /* allow the new connection to own the same name */ -+ /* acquire name from the 1st connection */ -+ ret = kdbus_name_acquire(env->conn, name, &flags); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = conn_is_name_owner(env->conn, name); -+ ASSERT_RETURN(ret == 0); -+ -+ /* queue the 2nd connection as waiting owner */ -+ flags = KDBUS_NAME_QUEUE; -+ ret = kdbus_name_acquire(conn, name, &flags); -+ ASSERT_RETURN(ret == 0); -+ ASSERT_RETURN(flags & KDBUS_NAME_IN_QUEUE); -+ -+ /* release name from 1st connection */ -+ ret = kdbus_name_release(env->conn, name); -+ ASSERT_RETURN(ret == 0); -+ -+ /* now the name should be owned by the 2nd connection */ -+ ret = conn_is_name_owner(conn, name); -+ ASSERT_RETURN(ret == 0); -+ -+ kdbus_conn_free(conn); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-policy-ns.c -@@ -0,0 +1,578 @@ -+/* -+ * Copyright (C) 2014 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 <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <pthread.h> -+#include <sched.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <stdint.h> -+#include <stdbool.h> -+#include <unistd.h> -+#include <errno.h> -+#include <signal.h> -+#include <sys/wait.h> -+#include <sys/prctl.h> -+#include <sys/ioctl.h> -+#include <sys/eventfd.h> -+#include <sys/syscall.h> -+#include <linux/sched.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+#define MAX_CONN 64 -+#define POLICY_NAME "foo.test.policy-test" -+ -+/** -+ * The purpose of these tests: -+ * 1) Check KDBUS_POLICY_TALK -+ * 2) Check the cache state: kdbus_policy_db->talk_access_hash -+ * Should be extended -+ */ -+ -+/** -+ * Check a list of connections against conn_db[0] -+ * conn_db[0] will own the name "foo.test.policy-test" and the -+ * policy holder connection for this name will update the policy -+ * entries, so different use cases can be tested. -+ */ -+static struct kdbus_conn **conn_db; -+ -+static void *kdbus_recv_echo(void *ptr) -+{ -+ int ret; -+ struct kdbus_conn *conn = ptr; -+ -+ ret = kdbus_msg_recv_poll(conn, 100, NULL, NULL); -+ -+ return (void *)(long)ret; -+} -+ -+/* Trigger kdbus_policy_set() */ -+static int kdbus_set_policy_talk(struct kdbus_conn *conn, -+ const char *name, -+ uid_t id, unsigned int type) -+{ -+ int ret; -+ struct kdbus_policy_access access = { -+ .type = type, -+ .id = id, -+ .access = KDBUS_POLICY_TALK, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn, name, &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ return TEST_OK; -+} -+ -+/* return TEST_OK or TEST_ERR on failure */ -+static int kdbus_register_same_activator(char *bus, const char *name, -+ struct kdbus_conn **c) -+{ -+ int ret; -+ struct kdbus_conn *activator; -+ -+ activator = kdbus_hello_activator(bus, name, NULL, 0); -+ if (activator) { -+ *c = activator; -+ fprintf(stderr, "--- error was able to register name twice '%s'.\n", -+ name); -+ return TEST_ERR; -+ } -+ -+ ret = -errno; -+ /* -EEXIST means test succeeded */ -+ if (ret == -EEXIST) -+ return TEST_OK; -+ -+ return TEST_ERR; -+} -+ -+/* return TEST_OK or TEST_ERR on failure */ -+static int kdbus_register_policy_holder(char *bus, const char *name, -+ struct kdbus_conn **conn) -+{ -+ struct kdbus_conn *c; -+ struct kdbus_policy_access access[2]; -+ -+ access[0].type = KDBUS_POLICY_ACCESS_USER; -+ access[0].access = KDBUS_POLICY_OWN; -+ access[0].id = geteuid(); -+ -+ access[1].type = KDBUS_POLICY_ACCESS_WORLD; -+ access[1].access = KDBUS_POLICY_TALK; -+ access[1].id = geteuid(); -+ -+ c = kdbus_hello_registrar(bus, name, access, 2, -+ KDBUS_HELLO_POLICY_HOLDER); -+ ASSERT_RETURN(c); -+ -+ *conn = c; -+ -+ return TEST_OK; -+} -+ -+/** -+ * Create new threads for receiving from multiple senders, -+ * The 'conn_db' will be populated by newly created connections. -+ * Caller should free all allocated connections. -+ * -+ * return 0 on success, negative errno on failure. -+ */ -+static int kdbus_recv_in_threads(const char *bus, const char *name, -+ struct kdbus_conn **conn_db) -+{ -+ int ret; -+ unsigned int i, tid; -+ unsigned long dst_id; -+ unsigned long cookie = 1; -+ unsigned int thread_nr = MAX_CONN - 1; -+ pthread_t thread_id[MAX_CONN - 1] = {'\0'}; -+ -+ dst_id = name ? KDBUS_DST_ID_NAME : conn_db[0]->id; -+ -+ for (tid = 0, i = 1; tid < thread_nr; tid++, i++) { -+ ret = pthread_create(&thread_id[tid], NULL, -+ kdbus_recv_echo, (void *)conn_db[0]); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error pthread_create: %d err %d (%m)\n", -+ ret, errno); -+ break; -+ } -+ -+ /* just free before re-using */ -+ kdbus_conn_free(conn_db[i]); -+ conn_db[i] = NULL; -+ -+ /* We need to create connections here */ -+ conn_db[i] = kdbus_hello(bus, 0, NULL, 0); -+ if (!conn_db[i]) { -+ ret = -errno; -+ break; -+ } -+ -+ ret = kdbus_add_match_empty(conn_db[i]); -+ if (ret < 0) -+ break; -+ -+ ret = kdbus_msg_send(conn_db[i], name, cookie++, -+ 0, 0, 0, dst_id); -+ if (ret < 0) -+ break; -+ } -+ -+ for (tid = 0; tid < thread_nr; tid++) { -+ int thread_ret = 0; -+ -+ if (thread_id[tid]) { -+ pthread_join(thread_id[tid], (void *)&thread_ret); -+ if (thread_ret < 0 && ret == 0) -+ ret = thread_ret; -+ } -+ } -+ -+ return ret; -+} -+ -+/* Return: TEST_OK or TEST_ERR on failure */ -+static int kdbus_normal_test(const char *bus, const char *name, -+ struct kdbus_conn **conn_db) -+{ -+ int ret; -+ -+ ret = kdbus_recv_in_threads(bus, name, conn_db); -+ ASSERT_RETURN(ret >= 0); -+ -+ return TEST_OK; -+} -+ -+static int kdbus_fork_test_by_id(const char *bus, -+ struct kdbus_conn **conn_db, -+ int parent_status, int child_status) -+{ -+ int ret; -+ pid_t pid; -+ uint64_t cookie = 0x9876ecba; -+ struct kdbus_msg *msg = NULL; -+ uint64_t offset = 0; -+ int status = 0; -+ -+ /* -+ * If the child_status is not EXIT_SUCCESS, then we expect -+ * that sending from the child will fail, thus receiving -+ * from parent must error with -ETIMEDOUT, and vice versa. -+ */ -+ bool parent_timedout = !!child_status; -+ bool child_timedout = !!parent_status; -+ -+ pid = fork(); -+ ASSERT_RETURN_VAL(pid >= 0, pid); -+ -+ if (pid == 0) { -+ struct kdbus_conn *conn_src; -+ -+ ret = prctl(PR_SET_PDEATHSIG, SIGKILL); -+ ASSERT_EXIT(ret == 0); -+ -+ ret = drop_privileges(65534, 65534); -+ ASSERT_EXIT(ret == 0); -+ -+ conn_src = kdbus_hello(bus, 0, NULL, 0); -+ ASSERT_EXIT(conn_src); -+ -+ ret = kdbus_add_match_empty(conn_src); -+ ASSERT_EXIT(ret == 0); -+ -+ /* -+ * child_status is always checked against send -+ * operations, in case it fails always return -+ * EXIT_FAILURE. -+ */ -+ ret = kdbus_msg_send(conn_src, NULL, cookie, -+ 0, 0, 0, conn_db[0]->id); -+ ASSERT_EXIT(ret == child_status); -+ -+ ret = kdbus_msg_recv_poll(conn_src, 100, NULL, NULL); -+ -+ kdbus_conn_free(conn_src); -+ -+ /* -+ * Child kdbus_msg_recv_poll() should timeout since -+ * the parent_status was set to a non EXIT_SUCCESS -+ * value. -+ */ -+ if (child_timedout) -+ _exit(ret == -ETIMEDOUT ? EXIT_SUCCESS : EXIT_FAILURE); -+ -+ _exit(ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE); -+ } -+ -+ ret = kdbus_msg_recv_poll(conn_db[0], 100, &msg, &offset); -+ /* -+ * If parent_timedout is set then this should fail with -+ * -ETIMEDOUT since the child_status was set to a non -+ * EXIT_SUCCESS value. Otherwise, assume -+ * that kdbus_msg_recv_poll() has succeeded. -+ */ -+ if (parent_timedout) { -+ ASSERT_RETURN_VAL(ret == -ETIMEDOUT, TEST_ERR); -+ -+ /* timedout no need to continue, we don't have the -+ * child connection ID, so just terminate. */ -+ goto out; -+ } else { -+ ASSERT_RETURN_VAL(ret == 0, ret); -+ } -+ -+ ret = kdbus_msg_send(conn_db[0], NULL, ++cookie, -+ 0, 0, 0, msg->src_id); -+ /* -+ * parent_status is checked against send operations, -+ * on failures always return TEST_ERR. -+ */ -+ ASSERT_RETURN_VAL(ret == parent_status, TEST_ERR); -+ -+ kdbus_msg_free(msg); -+ kdbus_free(conn_db[0], offset); -+ -+out: -+ ret = waitpid(pid, &status, 0); -+ ASSERT_RETURN_VAL(ret >= 0, ret); -+ -+ return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR; -+} -+ -+/* -+ * Return: TEST_OK, TEST_ERR or TEST_SKIP -+ * we return TEST_OK only if the childs return with the expected -+ * 'expected_status' that is specified as an argument. -+ */ -+static int kdbus_fork_test(const char *bus, const char *name, -+ struct kdbus_conn **conn_db, int expected_status) -+{ -+ pid_t pid; -+ int ret = 0; -+ int status = 0; -+ -+ setbuf(stdout, NULL); -+ -+ pid = fork(); -+ ASSERT_RETURN_VAL(pid >= 0, pid); -+ -+ if (pid == 0) { -+ ret = prctl(PR_SET_PDEATHSIG, SIGKILL); -+ ASSERT_EXIT(ret == 0); -+ -+ ret = drop_privileges(65534, 65534); -+ ASSERT_EXIT(ret == 0); -+ -+ ret = kdbus_recv_in_threads(bus, name, conn_db); -+ _exit(ret == expected_status ? EXIT_SUCCESS : EXIT_FAILURE); -+ } -+ -+ ret = waitpid(pid, &status, 0); -+ ASSERT_RETURN(ret >= 0); -+ -+ return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR; -+} -+ -+/* Return EXIT_SUCCESS, EXIT_FAILURE or negative errno */ -+static int __kdbus_clone_userns_test(const char *bus, -+ const char *name, -+ struct kdbus_conn **conn_db, -+ int expected_status) -+{ -+ int efd; -+ pid_t pid; -+ int ret = 0; -+ unsigned int uid = 65534; -+ int status; -+ -+ ret = drop_privileges(uid, uid); -+ ASSERT_RETURN_VAL(ret == 0, ret); -+ -+ /* -+ * Since we just dropped privileges, the dumpable flag was just -+ * cleared which makes the /proc/$clone_child/uid_map to be -+ * owned by root, hence any userns uid mapping will fail with -+ * -EPERM since the mapping will be done by uid 65534. -+ * -+ * To avoid this set the dumpable flag again which makes procfs -+ * update the /proc/$clone_child/ inodes owner to 65534. -+ * -+ * Using this we will be able write to /proc/$clone_child/uid_map -+ * as uid 65534 and map the uid 65534 to 0 inside the user -+ * namespace. -+ */ -+ ret = prctl(PR_SET_DUMPABLE, SUID_DUMP_USER); -+ ASSERT_RETURN_VAL(ret == 0, ret); -+ -+ /* sync parent/child */ -+ efd = eventfd(0, EFD_CLOEXEC); -+ ASSERT_RETURN_VAL(efd >= 0, efd); -+ -+ pid = syscall(__NR_clone, SIGCHLD|CLONE_NEWUSER, NULL); -+ if (pid < 0) { -+ ret = -errno; -+ kdbus_printf("error clone: %d (%m)\n", ret); -+ /* -+ * Normal user not allowed to create userns, -+ * so nothing to worry about ? -+ */ -+ if (ret == -EPERM) { -+ kdbus_printf("-- CLONE_NEWUSER TEST Failed for uid: %u\n" -+ "-- Make sure that your kernel do not allow " -+ "CLONE_NEWUSER for unprivileged users\n" -+ "-- Upstream Commit: " -+ "https://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=5eaf563e\n", -+ uid); -+ ret = 0; -+ } -+ -+ return ret; -+ } -+ -+ if (pid == 0) { -+ struct kdbus_conn *conn_src; -+ eventfd_t event_status = 0; -+ -+ setbuf(stdout, NULL); -+ ret = prctl(PR_SET_PDEATHSIG, SIGKILL); -+ ASSERT_EXIT(ret == 0); -+ -+ ret = eventfd_read(efd, &event_status); -+ ASSERT_EXIT(ret >= 0 && event_status == 1); -+ -+ /* ping connection from the new user namespace */ -+ conn_src = kdbus_hello(bus, 0, NULL, 0); -+ ASSERT_EXIT(conn_src); -+ -+ ret = kdbus_add_match_empty(conn_src); -+ ASSERT_EXIT(ret == 0); -+ -+ ret = kdbus_msg_send(conn_src, name, 0xabcd1234, -+ 0, 0, 0, KDBUS_DST_ID_NAME); -+ kdbus_conn_free(conn_src); -+ -+ _exit(ret == expected_status ? EXIT_SUCCESS : EXIT_FAILURE); -+ } -+ -+ ret = userns_map_uid_gid(pid, "0 65534 1", "0 65534 1"); -+ ASSERT_RETURN_VAL(ret == 0, ret); -+ -+ /* Tell child we are ready */ -+ ret = eventfd_write(efd, 1); -+ ASSERT_RETURN_VAL(ret == 0, ret); -+ -+ ret = waitpid(pid, &status, 0); -+ ASSERT_RETURN_VAL(ret >= 0, ret); -+ -+ close(efd); -+ -+ return status == EXIT_SUCCESS ? TEST_OK : TEST_ERR; -+} -+ -+static int kdbus_clone_userns_test(const char *bus, -+ const char *name, -+ struct kdbus_conn **conn_db, -+ int expected_status) -+{ -+ pid_t pid; -+ int ret = 0; -+ int status; -+ -+ setbuf(stdout, NULL); -+ -+ pid = fork(); -+ ASSERT_RETURN_VAL(pid >= 0, -errno); -+ -+ if (pid == 0) { -+ ret = prctl(PR_SET_PDEATHSIG, SIGKILL); -+ if (ret < 0) -+ _exit(EXIT_FAILURE); -+ -+ ret = __kdbus_clone_userns_test(bus, name, conn_db, -+ expected_status); -+ _exit(ret); -+ } -+ -+ /* -+ * Receive in the original (root privileged) user namespace, -+ * must fail with -ETIMEDOUT. -+ */ -+ ret = kdbus_msg_recv_poll(conn_db[0], 100, NULL, NULL); -+ ASSERT_RETURN_VAL(ret == -ETIMEDOUT, ret); -+ -+ ret = waitpid(pid, &status, 0); -+ ASSERT_RETURN_VAL(ret >= 0, ret); -+ -+ return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR; -+} -+ -+int kdbus_test_policy_ns(struct kdbus_test_env *env) -+{ -+ int i; -+ int ret; -+ struct kdbus_conn *activator = NULL; -+ struct kdbus_conn *policy_holder = NULL; -+ char *bus = env->buspath; -+ -+ if (geteuid() > 0) { -+ kdbus_printf("error geteuid() != 0, %s() needs root\n", -+ __func__); -+ return TEST_SKIP; -+ } -+ -+ /* we require user-namespaces */ -+ if (access("/proc/self/uid_map", F_OK) != 0) -+ return TEST_SKIP; -+ -+ conn_db = calloc(MAX_CONN, sizeof(struct kdbus_conn *)); -+ ASSERT_RETURN(conn_db); -+ -+ memset(conn_db, 0, MAX_CONN * sizeof(struct kdbus_conn *)); -+ -+ conn_db[0] = kdbus_hello(bus, 0, NULL, 0); -+ ASSERT_RETURN(conn_db[0]); -+ -+ ret = kdbus_add_match_empty(conn_db[0]); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_fork_test_by_id(bus, conn_db, -EPERM, -EPERM); -+ ASSERT_EXIT(ret == 0); -+ -+ ret = kdbus_register_policy_holder(bus, POLICY_NAME, -+ &policy_holder); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Try to register the same name with an activator */ -+ ret = kdbus_register_same_activator(bus, POLICY_NAME, -+ &activator); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Acquire POLICY_NAME */ -+ ret = kdbus_name_acquire(conn_db[0], POLICY_NAME, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_normal_test(bus, POLICY_NAME, conn_db); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_name_list(conn_db[0], KDBUS_NAME_LIST_NAMES | -+ KDBUS_NAME_LIST_UNIQUE | -+ KDBUS_NAME_LIST_ACTIVATORS | -+ KDBUS_NAME_LIST_QUEUED); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_fork_test(bus, POLICY_NAME, conn_db, EXIT_SUCCESS); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * childs connections are able to talk to conn_db[0] since -+ * current POLICY_NAME TALK type is KDBUS_POLICY_ACCESS_WORLD, -+ * so expect EXIT_SUCCESS when sending from child. However, -+ * since the child's connection does not own any well-known -+ * name, The parent connection conn_db[0] should fail with -+ * -EPERM but since it is a privileged bus user the TALK is -+ * allowed. -+ */ -+ ret = kdbus_fork_test_by_id(bus, conn_db, -+ EXIT_SUCCESS, EXIT_SUCCESS); -+ ASSERT_EXIT(ret == 0); -+ -+ /* -+ * Connections that can talk are perhaps being destroyed now. -+ * Restrict the policy and purge cache entries where the -+ * conn_db[0] is the destination. -+ * -+ * Now only connections with uid == 0 are allowed to talk. -+ */ -+ ret = kdbus_set_policy_talk(policy_holder, POLICY_NAME, -+ geteuid(), KDBUS_POLICY_ACCESS_USER); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Testing connections (FORK+DROP) again: -+ * After setting the policy re-check connections -+ * we expect the childs to fail with -EPERM -+ */ -+ ret = kdbus_fork_test(bus, POLICY_NAME, conn_db, -EPERM); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Now expect that both parent and child to fail. -+ * -+ * Child should fail with -EPERM since we just restricted -+ * the POLICY_NAME TALK to uid 0 and its uid is 65534. -+ * -+ * Since the parent's connection will timeout when receiving -+ * from the child, we never continue. FWIW just put -EPERM. -+ */ -+ ret = kdbus_fork_test_by_id(bus, conn_db, -EPERM, -EPERM); -+ ASSERT_EXIT(ret == 0); -+ -+ /* Check if the name can be reached in a new userns */ -+ ret = kdbus_clone_userns_test(bus, POLICY_NAME, conn_db, -EPERM); -+ ASSERT_RETURN(ret == 0); -+ -+ for (i = 0; i < MAX_CONN; i++) -+ kdbus_conn_free(conn_db[i]); -+ -+ kdbus_conn_free(activator); -+ kdbus_conn_free(policy_holder); -+ -+ free(conn_db); -+ -+ return ret; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-policy-priv.c -@@ -0,0 +1,1168 @@ -+#include <errno.h> -+#include <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stdint.h> -+#include <stdbool.h> -+#include <unistd.h> -+#include <time.h> -+#include <sys/capability.h> -+#include <sys/eventfd.h> -+#include <sys/ioctl.h> -+#include <sys/wait.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+static int test_policy_priv_by_id(const char *bus, -+ struct kdbus_conn *conn_dst, -+ bool drop_second_user, -+ int parent_status, -+ int child_status) -+{ -+ int ret; -+ uint64_t expected_cookie = time(NULL) ^ 0xdeadbeef; -+ -+ ASSERT_RETURN(conn_dst); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, bus, ({ -+ ret = kdbus_msg_send(unpriv, NULL, -+ expected_cookie, 0, 0, 0, -+ conn_dst->id); -+ ASSERT_EXIT(ret == child_status); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = kdbus_msg_recv_poll(conn_dst, 100, NULL, NULL); -+ ASSERT_RETURN(ret == parent_status); -+ -+ return 0; -+} -+ -+static int test_policy_priv_by_broadcast(const char *bus, -+ struct kdbus_conn *conn_dst, -+ int drop_second_user, -+ int parent_status, -+ int child_status) -+{ -+ int ret; -+ int efd; -+ eventfd_t event_status = 0; -+ struct kdbus_msg *msg = NULL; -+ uid_t second_uid = UNPRIV_UID; -+ gid_t second_gid = UNPRIV_GID; -+ struct kdbus_conn *child_2 = conn_dst; -+ uint64_t expected_cookie = time(NULL) ^ 0xdeadbeef; -+ -+ /* Drop to another unprivileged user other than UNPRIV_UID */ -+ if (drop_second_user == DROP_OTHER_UNPRIV) { -+ second_uid = UNPRIV_UID - 1; -+ second_gid = UNPRIV_GID - 1; -+ } -+ -+ /* child will signal parent to send broadcast */ -+ efd = eventfd(0, EFD_CLOEXEC); -+ ASSERT_RETURN_VAL(efd >= 0, efd); -+ -+ ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_GID, ({ -+ struct kdbus_conn *child; -+ -+ child = kdbus_hello(bus, 0, NULL, 0); -+ ASSERT_EXIT(child); -+ -+ ret = kdbus_add_match_empty(child); -+ ASSERT_EXIT(ret == 0); -+ -+ /* signal parent */ -+ ret = eventfd_write(efd, 1); -+ ASSERT_EXIT(ret == 0); -+ -+ ret = kdbus_msg_recv_poll(child, 300, &msg, NULL); -+ ASSERT_EXIT(ret == child_status); -+ -+ /* -+ * If we expect the child to get the broadcast -+ * message, then check the received cookie. -+ */ -+ if (ret == 0) { -+ ASSERT_EXIT(expected_cookie == msg->cookie); -+ } -+ -+ /* Use expected_cookie since 'msg' might be NULL */ -+ ret = kdbus_msg_send(child, NULL, expected_cookie + 1, -+ 0, 0, 0, KDBUS_DST_ID_BROADCAST); -+ ASSERT_EXIT(ret == 0); -+ -+ kdbus_msg_free(msg); -+ kdbus_conn_free(child); -+ }), -+ ({ -+ if (drop_second_user == DO_NOT_DROP) { -+ ASSERT_RETURN(child_2); -+ -+ ret = eventfd_read(efd, &event_status); -+ ASSERT_RETURN(ret >= 0 && event_status == 1); -+ -+ ret = kdbus_msg_send(child_2, NULL, -+ expected_cookie, 0, 0, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv_poll(child_2, 300, -+ &msg, NULL); -+ ASSERT_RETURN(ret == parent_status); -+ -+ /* -+ * Check returned cookie in case we expect -+ * success. -+ */ -+ if (ret == 0) { -+ ASSERT_RETURN(msg->cookie == -+ expected_cookie + 1); -+ } -+ -+ kdbus_msg_free(msg); -+ } else { -+ /* -+ * Two unprivileged users will try to -+ * communicate using broadcast. -+ */ -+ ret = RUN_UNPRIVILEGED(second_uid, second_gid, ({ -+ child_2 = kdbus_hello(bus, 0, NULL, 0); -+ ASSERT_EXIT(child_2); -+ -+ ret = kdbus_add_match_empty(child_2); -+ ASSERT_EXIT(ret == 0); -+ -+ ret = eventfd_read(efd, &event_status); -+ ASSERT_RETURN(ret >= 0 && event_status == 1); -+ -+ ret = kdbus_msg_send(child_2, NULL, -+ expected_cookie, 0, 0, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_EXIT(ret == 0); -+ -+ ret = kdbus_msg_recv_poll(child_2, 100, -+ &msg, NULL); -+ ASSERT_EXIT(ret == parent_status); -+ -+ /* -+ * Check returned cookie in case we expect -+ * success. -+ */ -+ if (ret == 0) { -+ ASSERT_EXIT(msg->cookie == -+ expected_cookie + 1); -+ } -+ -+ kdbus_msg_free(msg); -+ kdbus_conn_free(child_2); -+ }), -+ ({ 0; })); -+ } -+ })); -+ -+ close(efd); -+ -+ return ret; -+} -+ -+static void nosig(int sig) -+{ -+} -+ -+static int test_priv_before_policy_upload(struct kdbus_test_env *env) -+{ -+ int ret; -+ struct kdbus_conn *conn; -+ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn); -+ -+ /* -+ * Make sure unprivileged bus user cannot acquire names -+ * before registring any policy holder. -+ */ -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "com.example.a", NULL); -+ ASSERT_EXIT(ret < 0); -+ })); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Make sure unprivileged bus users cannot talk by default -+ * to privileged ones, unless a policy holder that allows -+ * this was uploaded. -+ */ -+ -+ ret = test_policy_priv_by_id(env->buspath, conn, false, -+ -ETIMEDOUT, -EPERM); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Activate matching for a privileged connection */ -+ ret = kdbus_add_match_empty(conn); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * First make sure that BROADCAST with msg flag -+ * KDBUS_MSG_FLAGS_EXPECT_REPLY will fail with -ENOTUNIQ -+ */ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_msg_send(unpriv, NULL, 0xdeadbeef, -+ KDBUS_MSG_FLAGS_EXPECT_REPLY, -+ 5000000000ULL, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_EXIT(ret == -ENOTUNIQ); -+ })); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Test broadcast with a privileged connection. -+ * -+ * The first receiver should get the broadcast message since -+ * the sender is a privileged connection. -+ * -+ * The privileged connection should not get the broadcast -+ * message since the sender is an unprivileged connection. -+ * It will fail with -ETIMEDOUT. -+ * -+ */ -+ -+ ret = test_policy_priv_by_broadcast(env->buspath, conn, -+ DO_NOT_DROP, -+ -ETIMEDOUT, EXIT_SUCCESS); -+ ASSERT_RETURN(ret == 0); -+ -+ -+ /* -+ * Test broadcast with two unprivileged connections running -+ * under the same user. -+ * -+ * Both connections should succeed. -+ */ -+ -+ ret = test_policy_priv_by_broadcast(env->buspath, NULL, -+ DROP_SAME_UNPRIV, -+ EXIT_SUCCESS, EXIT_SUCCESS); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Test broadcast with two unprivileged connections running -+ * under different users. -+ * -+ * Both connections will fail with -ETIMEDOUT. -+ */ -+ -+ ret = test_policy_priv_by_broadcast(env->buspath, NULL, -+ DROP_OTHER_UNPRIV, -+ -ETIMEDOUT, -ETIMEDOUT); -+ ASSERT_RETURN(ret == 0); -+ -+ kdbus_conn_free(conn); -+ -+ return ret; -+} -+ -+static int test_broadcast_after_policy_upload(struct kdbus_test_env *env) -+{ -+ int ret; -+ int efd; -+ eventfd_t event_status = 0; -+ struct kdbus_msg *msg = NULL; -+ struct kdbus_conn *owner_a, *owner_b; -+ struct kdbus_conn *holder_a, *holder_b; -+ struct kdbus_policy_access access = {}; -+ uint64_t expected_cookie = time(NULL) ^ 0xdeadbeef; -+ -+ owner_a = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(owner_a); -+ -+ ret = kdbus_name_acquire(owner_a, "com.example.broadcastA", NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ /* -+ * Make sure unprivileged bus users cannot talk by default -+ * to privileged ones, unless a policy holder that allows -+ * this was uploaded. -+ */ -+ -+ ret = test_policy_priv_by_id(env->buspath, owner_a, false, -+ -ETIMEDOUT, -EPERM); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Make sure that conn wont receive broadcasts unless it -+ * installs a match. -+ * -+ * At same time check that the unprivileged connection will -+ * receive the broadcast message from the privileged one. -+ */ -+ -+ ret = test_policy_priv_by_broadcast(env->buspath, owner_a, -+ DO_NOT_DROP, -+ -ETIMEDOUT, EXIT_SUCCESS); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Activate matching for a privileged connection */ -+ ret = kdbus_add_match_empty(owner_a); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Redo the previous test. The privileged conn won't receive -+ * broadcast messages from the unprivileged one. -+ */ -+ -+ ret = test_policy_priv_by_broadcast(env->buspath, owner_a, -+ DO_NOT_DROP, -+ -ETIMEDOUT, EXIT_SUCCESS); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Test that broadcast between two unprivileged users running -+ * under the same user still succeed. -+ */ -+ -+ ret = test_policy_priv_by_broadcast(env->buspath, NULL, -+ DROP_SAME_UNPRIV, -+ EXIT_SUCCESS, EXIT_SUCCESS); -+ ASSERT_RETURN(ret == 0); -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = geteuid(), -+ .access = KDBUS_POLICY_OWN, -+ }; -+ -+ holder_a = kdbus_hello_registrar(env->buspath, -+ "com.example.broadcastA", -+ &access, 1, -+ KDBUS_HELLO_POLICY_HOLDER); -+ ASSERT_RETURN(holder_a); -+ -+ holder_b = kdbus_hello_registrar(env->buspath, -+ "com.example.broadcastB", -+ &access, 1, -+ KDBUS_HELLO_POLICY_HOLDER); -+ ASSERT_RETURN(holder_b); -+ -+ owner_b = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(owner_b); -+ -+ ret = kdbus_name_acquire(owner_b, "com.example.broadcastB", NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ /* Activate matching for a privileged connection */ -+ ret = kdbus_add_match_empty(owner_b); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Test that even if "com.example.broadcastA" and -+ * "com.example.broadcastB" do restrict TALK access by default -+ * they are able to signal each other using broadcast due to -+ * the fact they are privileged connections. -+ */ -+ -+ ret = kdbus_msg_send(owner_a, NULL, 0xdeadbeef, 0, 0, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_recv_poll(owner_b, 100, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Check src ID */ -+ ASSERT_RETURN(msg->src_id == owner_a->id); -+ -+ kdbus_msg_free(msg); -+ kdbus_free(owner_b, msg->offset_reply); -+ -+ -+ /* Release name "com.example.broadcastB" */ -+ -+ ret = kdbus_name_release(owner_b, "com.example.broadcastB"); -+ ASSERT_EXIT(ret >= 0); -+ -+ /* KDBUS_POLICY_OWN for unprivileged connections */ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_WORLD, -+ .id = geteuid(), -+ .access = KDBUS_POLICY_OWN, -+ }; -+ -+ /* Update the policy so unprivileged will own the name */ -+ -+ ret = kdbus_conn_update_policy(holder_b, -+ "com.example.broadcastB", -+ &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Test broadcasts from an unprivileged connection that -+ * owns a name. -+ * -+ * We'll have four destinations here: -+ * -+ * owner_a: privileged connection that owns -+ * "com.example.broadcastA". TALK access are subject to policy -+ * rules and they are stricted so it should not receive -+ * the signal. Should fail with -ETIMEDOUT -+ * -+ * owner_b: privileged connection (running under a different -+ * uid) that do not own names, but with an empty broadcast -+ * match, so it will receive broadcasts. Should get the -+ * message. -+ * -+ * unpriv_a: unpriv connection that do not own any name. -+ * It will receive the broadcast since it is running under -+ * the same user of the one broadcasting and did install -+ * matches. It should get the message. -+ * -+ * unpriv_b: unpriv connection is not interested in broadcast -+ * messages, so it did not install broadcast matches. Should -+ * fail with -ETIMEDOUT -+ */ -+ -+ ++expected_cookie; -+ efd = eventfd(0, EFD_CLOEXEC); -+ ASSERT_RETURN_VAL(efd >= 0, efd); -+ -+ ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_UID, ({ -+ struct kdbus_conn *unpriv_owner; -+ struct kdbus_conn *unpriv_a, *unpriv_b; -+ -+ unpriv_owner = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_EXIT(unpriv_owner); -+ -+ unpriv_a = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_EXIT(unpriv_a); -+ -+ unpriv_b = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_EXIT(unpriv_b); -+ -+ ret = kdbus_name_acquire(unpriv_owner, -+ "com.example.broadcastB", -+ NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ ret = kdbus_add_match_empty(unpriv_a); -+ ASSERT_EXIT(ret == 0); -+ -+ /* Signal that we are doing broadcasts */ -+ ret = eventfd_write(efd, 1); -+ ASSERT_EXIT(ret == 0); -+ -+ /* -+ * Do broadcast from a connection that owns the -+ * names "com.example.broadcastB". -+ */ -+ ret = kdbus_msg_send(unpriv_owner, NULL, -+ expected_cookie, -+ 0, 0, 0, -+ KDBUS_DST_ID_BROADCAST); -+ ASSERT_EXIT(ret == 0); -+ -+ /* -+ * Unprivileged connection running under the same -+ * user. It should succeed. -+ */ -+ ret = kdbus_msg_recv_poll(unpriv_a, 100, &msg, NULL); -+ ASSERT_EXIT(ret == 0 && msg->cookie == expected_cookie); -+ -+ /* Not interested in broadcast */ -+ ret = kdbus_msg_recv_poll(unpriv_b, 100, NULL, NULL); -+ ASSERT_EXIT(ret == -ETIMEDOUT); -+ }), -+ ({ -+ ret = eventfd_read(efd, &event_status); -+ ASSERT_RETURN(ret >= 0 && event_status == 1); -+ -+ /* -+ * owner_a must fail with -ETIMEDOUT, since it owns -+ * name "com.example.broadcastA" and its TALK -+ * access is restriced. -+ */ -+ ret = kdbus_msg_recv_poll(owner_a, 100, NULL, NULL); -+ ASSERT_RETURN(ret == -ETIMEDOUT); -+ -+ /* -+ * owner_b got the broadcast from an unprivileged -+ * connection. -+ */ -+ ret = kdbus_msg_recv_poll(owner_b, 100, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ /* confirm the received cookie */ -+ ASSERT_RETURN(msg->cookie == expected_cookie); -+ -+ kdbus_msg_free(msg); -+ kdbus_free(owner_b, msg->offset_reply); -+ -+ -+ })); -+ ASSERT_RETURN(ret == 0); -+ -+ close(efd); -+ -+ /* -+ * Test broadcast with two unprivileged connections running -+ * under different users. -+ * -+ * Both connections will fail with -ETIMEDOUT. -+ */ -+ -+ ret = test_policy_priv_by_broadcast(env->buspath, NULL, -+ DROP_OTHER_UNPRIV, -+ -ETIMEDOUT, -ETIMEDOUT); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Perform last tests, allow others to talk to name -+ * "com.example.broadcastA". So now broadcasting to that -+ * connection should succeed since the policy allow it. -+ */ -+ -+ /* KDBUS_POLICY_OWN for unprivileged connections */ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_WORLD, -+ .id = geteuid(), -+ .access = KDBUS_POLICY_TALK, -+ }; -+ -+ ret = kdbus_conn_update_policy(holder_a, -+ "com.example.broadcastA", -+ &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ++expected_cookie; -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "com.example.broadcastB", -+ NULL); -+ ASSERT_EXIT(ret >= 0); -+ ret = kdbus_msg_send(unpriv, NULL, expected_cookie, -+ 0, 0, 0, KDBUS_DST_ID_BROADCAST); -+ ASSERT_EXIT(ret == 0); -+ })); -+ ASSERT_RETURN(ret == 0); -+ -+ /* owner_a will get the broadcast now. */ -+ ret = kdbus_msg_recv_poll(owner_a, 100, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ /* confirm the received cookie */ -+ ASSERT_RETURN(msg->cookie == expected_cookie); -+ -+ kdbus_msg_free(msg); -+ kdbus_free(owner_a, msg->offset_reply); -+ -+ /* -+ * owner_a released name "com.example.broadcastA". It should -+ * receive broadcasts, no more policies and it has a match. -+ * -+ * Unprivileged connection will own a name and will try to -+ * signal to the privileged connection. It should succeeded. -+ */ -+ -+ ret = kdbus_name_release(owner_a, "com.example.broadcastA"); -+ ASSERT_EXIT(ret >= 0); -+ -+ ++expected_cookie; -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "com.example.broadcastB", -+ NULL); -+ ASSERT_EXIT(ret >= 0); -+ ret = kdbus_msg_send(unpriv, NULL, expected_cookie, -+ 0, 0, 0, KDBUS_DST_ID_BROADCAST); -+ ASSERT_EXIT(ret == 0); -+ })); -+ ASSERT_RETURN(ret == 0); -+ -+ /* owner_a will get the broadcast now. */ -+ ret = kdbus_msg_recv_poll(owner_a, 100, &msg, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ /* confirm the received cookie */ -+ ASSERT_RETURN(msg->cookie == expected_cookie); -+ -+ kdbus_msg_free(msg); -+ kdbus_free(owner_a, msg->offset_reply); -+ -+ kdbus_conn_free(owner_a); -+ kdbus_conn_free(owner_b); -+ kdbus_conn_free(holder_a); -+ kdbus_conn_free(holder_b); -+ -+ return 0; -+} -+ -+static int test_policy_priv(struct kdbus_test_env *env) -+{ -+ struct kdbus_conn *conn_a, *conn_b, *conn, *owner; -+ struct kdbus_policy_access access, *acc; -+ sigset_t sset; -+ size_t num; -+ int ret; -+ -+ /* -+ * Make sure we have CAP_SETUID/SETGID so we can drop privileges -+ */ -+ -+ ret = test_is_capable(CAP_SETUID, CAP_SETGID, -1); -+ ASSERT_RETURN(ret >= 0); -+ -+ if (!ret) -+ return TEST_SKIP; -+ -+ /* -+ * Setup: -+ * conn_a: policy holder for com.example.a -+ * conn_b: name holder of com.example.b -+ */ -+ -+ signal(SIGUSR1, nosig); -+ sigemptyset(&sset); -+ sigaddset(&sset, SIGUSR1); -+ sigprocmask(SIG_BLOCK, &sset, NULL); -+ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn); -+ -+ /* -+ * Before registering any policy holder, make sure that the -+ * bus is secure by default. This test is necessary, it catches -+ * several cases where old D-Bus was vulnerable. -+ */ -+ -+ ret = test_priv_before_policy_upload(env); -+ ASSERT_RETURN(ret == 0); -+ -+ /* Register policy holder */ -+ -+ conn_a = kdbus_hello_registrar(env->buspath, "com.example.a", -+ NULL, 0, KDBUS_HELLO_POLICY_HOLDER); -+ ASSERT_RETURN(conn_a); -+ -+ conn_b = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn_b); -+ -+ ret = kdbus_name_acquire(conn_b, "com.example.b", NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ /* -+ * Make sure bus-owners can always acquire names. -+ */ -+ ret = kdbus_name_acquire(conn, "com.example.a", NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ kdbus_conn_free(conn); -+ -+ /* -+ * Make sure unprivileged users cannot acquire names with default -+ * policy assigned. -+ */ -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "com.example.a", NULL); -+ ASSERT_EXIT(ret < 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* -+ * Make sure unprivileged users can acquire names if we make them -+ * world-accessible. -+ */ -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_WORLD, -+ .id = 0, -+ .access = KDBUS_POLICY_OWN, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "com.example.a", NULL); -+ ASSERT_EXIT(ret >= 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* -+ * Make sure unprivileged users can acquire names if we make them -+ * gid-accessible. But only if the gid matches. -+ */ -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_GROUP, -+ .id = UNPRIV_GID, -+ .access = KDBUS_POLICY_OWN, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "com.example.a", NULL); -+ ASSERT_EXIT(ret >= 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_GROUP, -+ .id = 1, -+ .access = KDBUS_POLICY_OWN, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "com.example.a", NULL); -+ ASSERT_EXIT(ret < 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* -+ * Make sure unprivileged users can acquire names if we make them -+ * uid-accessible. But only if the uid matches. -+ */ -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = UNPRIV_UID, -+ .access = KDBUS_POLICY_OWN, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "com.example.a", NULL); -+ ASSERT_EXIT(ret >= 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = 1, -+ .access = KDBUS_POLICY_OWN, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "com.example.a", NULL); -+ ASSERT_EXIT(ret < 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* -+ * Make sure unprivileged users cannot acquire names if no owner-policy -+ * matches, even if SEE/TALK policies match. -+ */ -+ -+ num = 4; -+ acc = (struct kdbus_policy_access[]){ -+ { -+ .type = KDBUS_POLICY_ACCESS_GROUP, -+ .id = UNPRIV_GID, -+ .access = KDBUS_POLICY_SEE, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = UNPRIV_UID, -+ .access = KDBUS_POLICY_TALK, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_WORLD, -+ .id = 0, -+ .access = KDBUS_POLICY_TALK, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_WORLD, -+ .id = 0, -+ .access = KDBUS_POLICY_SEE, -+ }, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.a", acc, num); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "com.example.a", NULL); -+ ASSERT_EXIT(ret < 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* -+ * Make sure unprivileged users can acquire names if the only matching -+ * policy is somewhere in the middle. -+ */ -+ -+ num = 5; -+ acc = (struct kdbus_policy_access[]){ -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = 1, -+ .access = KDBUS_POLICY_OWN, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = 2, -+ .access = KDBUS_POLICY_OWN, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = UNPRIV_UID, -+ .access = KDBUS_POLICY_OWN, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = 3, -+ .access = KDBUS_POLICY_OWN, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = 4, -+ .access = KDBUS_POLICY_OWN, -+ }, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.a", acc, num); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_name_acquire(unpriv, "com.example.a", NULL); -+ ASSERT_EXIT(ret >= 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* -+ * Clear policies -+ */ -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.a", NULL, 0); -+ ASSERT_RETURN(ret == 0); -+ -+ /* -+ * Make sure privileged bus users can _always_ talk to others. -+ */ -+ -+ conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn); -+ -+ ret = kdbus_msg_send(conn, "com.example.b", 0xdeadbeef, 0, 0, 0, 0); -+ ASSERT_EXIT(ret >= 0); -+ ret = kdbus_msg_recv_poll(conn_b, 100, NULL, NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ kdbus_conn_free(conn); -+ -+ /* -+ * Make sure unprivileged bus users cannot talk by default. -+ */ -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0, -+ 0, 0); -+ ASSERT_EXIT(ret == -EPERM); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* -+ * Make sure unprivileged bus users can talk to equals, even without -+ * policy. -+ */ -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = UNPRIV_UID, -+ .access = KDBUS_POLICY_OWN, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.c", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ struct kdbus_conn *owner; -+ -+ owner = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(owner); -+ -+ ret = kdbus_name_acquire(owner, "com.example.c", NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ ret = kdbus_msg_send(unpriv, "com.example.c", 0xdeadbeef, 0, 0, -+ 0, 0); -+ ASSERT_EXIT(ret >= 0); -+ ret = kdbus_msg_recv_poll(owner, 100, NULL, NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ kdbus_conn_free(owner); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* -+ * Make sure unprivileged bus users can talk to privileged users if a -+ * suitable UID policy is set. -+ */ -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = UNPRIV_UID, -+ .access = KDBUS_POLICY_TALK, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.b", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0, -+ 0, 0); -+ ASSERT_EXIT(ret >= 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = kdbus_msg_recv_poll(conn_b, 100, NULL, NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ /* -+ * Make sure unprivileged bus users can talk to privileged users if a -+ * suitable GID policy is set. -+ */ -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_GROUP, -+ .id = UNPRIV_GID, -+ .access = KDBUS_POLICY_TALK, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.b", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0, -+ 0, 0); -+ ASSERT_EXIT(ret >= 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = kdbus_msg_recv_poll(conn_b, 100, NULL, NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ /* -+ * Make sure unprivileged bus users can talk to privileged users if a -+ * suitable WORLD policy is set. -+ */ -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_WORLD, -+ .id = 0, -+ .access = KDBUS_POLICY_TALK, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.b", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0, -+ 0, 0); -+ ASSERT_EXIT(ret >= 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = kdbus_msg_recv_poll(conn_b, 100, NULL, NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ /* -+ * Make sure unprivileged bus users cannot talk to privileged users if -+ * no suitable policy is set. -+ */ -+ -+ num = 5; -+ acc = (struct kdbus_policy_access[]){ -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = 0, -+ .access = KDBUS_POLICY_OWN, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = 1, -+ .access = KDBUS_POLICY_TALK, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = UNPRIV_UID, -+ .access = KDBUS_POLICY_SEE, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = 3, -+ .access = KDBUS_POLICY_TALK, -+ }, -+ { -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = 4, -+ .access = KDBUS_POLICY_TALK, -+ }, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.b", acc, num); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0, -+ 0, 0); -+ ASSERT_EXIT(ret == -EPERM); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* -+ * Make sure unprivileged bus users can talk to privileged users if a -+ * suitable OWN privilege overwrites TALK. -+ */ -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_WORLD, -+ .id = 0, -+ .access = KDBUS_POLICY_OWN, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.b", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0, -+ 0, 0); -+ ASSERT_EXIT(ret >= 0); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = kdbus_msg_recv_poll(conn_b, 100, NULL, NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ /* -+ * Make sure the TALK cache is reset correctly when policies are -+ * updated. -+ */ -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_WORLD, -+ .id = 0, -+ .access = KDBUS_POLICY_TALK, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.b", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = RUN_UNPRIVILEGED_CONN(unpriv, env->buspath, ({ -+ ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0, -+ 0, 0); -+ ASSERT_EXIT(ret >= 0); -+ -+ ret = kdbus_msg_recv_poll(conn_b, 100, NULL, NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.b", -+ NULL, 0); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_send(unpriv, "com.example.b", 0xdeadbeef, 0, 0, -+ 0, 0); -+ ASSERT_EXIT(ret == -EPERM); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ /* -+ * Make sure the TALK cache is reset correctly when policy holders -+ * disconnect. -+ */ -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_WORLD, -+ .id = 0, -+ .access = KDBUS_POLICY_OWN, -+ }; -+ -+ conn = kdbus_hello_registrar(env->buspath, "com.example.c", -+ NULL, 0, KDBUS_HELLO_POLICY_HOLDER); -+ ASSERT_RETURN(conn); -+ -+ ret = kdbus_conn_update_policy(conn, "com.example.c", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ owner = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(owner); -+ -+ ret = kdbus_name_acquire(owner, "com.example.c", NULL); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = RUN_UNPRIVILEGED(UNPRIV_UID, UNPRIV_GID, ({ -+ struct kdbus_conn *unpriv; -+ -+ /* wait for parent to be finished */ -+ sigemptyset(&sset); -+ ret = sigsuspend(&sset); -+ ASSERT_RETURN(ret == -1 && errno == EINTR); -+ -+ unpriv = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(unpriv); -+ -+ ret = kdbus_msg_send(unpriv, "com.example.c", 0xdeadbeef, 0, 0, -+ 0, 0); -+ ASSERT_EXIT(ret >= 0); -+ -+ ret = kdbus_msg_recv_poll(owner, 100, NULL, NULL); -+ ASSERT_EXIT(ret >= 0); -+ -+ /* free policy holder */ -+ kdbus_conn_free(conn); -+ -+ ret = kdbus_msg_send(unpriv, "com.example.c", 0xdeadbeef, 0, 0, -+ 0, 0); -+ ASSERT_EXIT(ret == -EPERM); -+ -+ kdbus_conn_free(unpriv); -+ }), ({ -+ /* make sure policy holder is only valid in child */ -+ kdbus_conn_free(conn); -+ kill(pid, SIGUSR1); -+ })); -+ ASSERT_RETURN(ret >= 0); -+ -+ -+ /* -+ * The following tests are necessary. -+ */ -+ -+ ret = test_broadcast_after_policy_upload(env); -+ ASSERT_RETURN(ret == 0); -+ -+ kdbus_conn_free(owner); -+ -+ /* -+ * cleanup resources -+ */ -+ -+ kdbus_conn_free(conn_b); -+ kdbus_conn_free(conn_a); -+ -+ return TEST_OK; -+} -+ -+int kdbus_test_policy_priv(struct kdbus_test_env *env) -+{ -+ pid_t pid; -+ int ret; -+ -+ /* make sure to exit() if a child returns from fork() */ -+ pid = getpid(); -+ ret = test_policy_priv(env); -+ if (pid != getpid()) -+ exit(1); -+ -+ return ret; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-policy.c -@@ -0,0 +1,81 @@ -+#include <errno.h> -+#include <stdio.h> -+#include <string.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stdint.h> -+#include <stdbool.h> -+#include <unistd.h> -+#include <sys/ioctl.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+int kdbus_test_policy(struct kdbus_test_env *env) -+{ -+ struct kdbus_conn *conn_a, *conn_b; -+ struct kdbus_policy_access access; -+ int ret; -+ -+ /* Invalid name */ -+ conn_a = kdbus_hello_registrar(env->buspath, ".example.a", -+ NULL, 0, KDBUS_HELLO_POLICY_HOLDER); -+ ASSERT_RETURN(conn_a == NULL); -+ -+ conn_a = kdbus_hello_registrar(env->buspath, "example", -+ NULL, 0, KDBUS_HELLO_POLICY_HOLDER); -+ ASSERT_RETURN(conn_a == NULL); -+ -+ conn_a = kdbus_hello_registrar(env->buspath, "com.example.a", -+ NULL, 0, KDBUS_HELLO_POLICY_HOLDER); -+ ASSERT_RETURN(conn_a); -+ -+ conn_b = kdbus_hello_registrar(env->buspath, "com.example.b", -+ NULL, 0, KDBUS_HELLO_POLICY_HOLDER); -+ ASSERT_RETURN(conn_b); -+ -+ /* -+ * Verify there cannot be any duplicate entries, except for specific vs. -+ * wildcard entries. -+ */ -+ -+ access = (struct kdbus_policy_access){ -+ .type = KDBUS_POLICY_ACCESS_USER, -+ .id = geteuid(), -+ .access = KDBUS_POLICY_SEE, -+ }; -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.a", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_conn_update_policy(conn_b, "com.example.a", &access, 1); -+ ASSERT_RETURN(ret == -EEXIST); -+ -+ ret = kdbus_conn_update_policy(conn_b, "com.example.a.*", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.a.*", &access, 1); -+ ASSERT_RETURN(ret == -EEXIST); -+ -+ ret = kdbus_conn_update_policy(conn_a, "com.example.*", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_conn_update_policy(conn_b, "com.example.a", &access, 1); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_conn_update_policy(conn_b, "com.example.*", &access, 1); -+ ASSERT_RETURN(ret == -EEXIST); -+ -+ /* Invalid name */ -+ ret = kdbus_conn_update_policy(conn_b, ".example.*", &access, 1); -+ ASSERT_RETURN(ret == -EINVAL); -+ -+ ret = kdbus_conn_update_policy(conn_b, "example", &access, 1); -+ ASSERT_RETURN(ret == -EINVAL); -+ -+ kdbus_conn_free(conn_b); -+ kdbus_conn_free(conn_a); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-race.c -@@ -0,0 +1,313 @@ -+#include <stdio.h> -+#include <string.h> -+#include <time.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <sys/ioctl.h> -+#include <pthread.h> -+#include <stdbool.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+struct race_thread { -+ pthread_spinlock_t lock; -+ pthread_t thread; -+ int (*fn) (struct kdbus_test_env *env, void *ctx); -+ struct kdbus_test_env *env; -+ void *ctx; -+ int ret; -+}; -+ -+static void *race_thread_fn(void *data) -+{ -+ struct race_thread *thread = data; -+ int ret; -+ -+ ret = pthread_spin_lock(&thread->lock); -+ if (ret < 0) -+ goto error; -+ -+ ret = thread->fn(thread->env, thread->ctx); -+ pthread_spin_unlock(&thread->lock); -+ -+error: -+ return (void*)(long)ret; -+} -+ -+static int race_thread_init(struct race_thread *thread) -+{ -+ int ret; -+ -+ ret = pthread_spin_init(&thread->lock, PTHREAD_PROCESS_PRIVATE); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = pthread_spin_lock(&thread->lock); -+ ASSERT_RETURN(ret >= 0); -+ -+ ret = pthread_create(&thread->thread, NULL, race_thread_fn, thread); -+ ASSERT_RETURN(ret >= 0); -+ -+ return TEST_OK; -+} -+ -+static void race_thread_run(struct race_thread *thread, -+ int (*fn)(struct kdbus_test_env *env, void *ctx), -+ struct kdbus_test_env *env, void *ctx) -+{ -+ int ret; -+ -+ thread->fn = fn; -+ thread->env = env; -+ thread->ctx = ctx; -+ -+ ret = pthread_spin_unlock(&thread->lock); -+ if (ret < 0) -+ abort(); -+} -+ -+static int race_thread_join(struct race_thread *thread) -+{ -+ void *val = (void*)(long)-EFAULT; -+ int ret; -+ -+ ret = pthread_join(thread->thread, &val); -+ ASSERT_RETURN(ret >= 0); -+ -+ thread->ret = (long)val; -+ -+ return TEST_OK; -+} -+ -+static void shuffle(size_t *array, size_t n) -+{ -+ size_t i, j, t; -+ -+ if (n <= 1) -+ return; -+ -+ for (i = 0; i < n - 1; i++) { -+ j = i + rand() / (RAND_MAX / (n - i) + 1); -+ t = array[j]; -+ array[j] = array[i]; -+ array[i] = t; -+ } -+} -+ -+static int race_thread(int (*init_fn) (struct kdbus_test_env *env, void *ctx), -+ int (*exit_fn) (struct kdbus_test_env *env, void *ctx, -+ int *ret, size_t n_ret), -+ int (*verify_fn) (struct kdbus_test_env *env, void *ctx), -+ int (**fns) (struct kdbus_test_env *env, void *ctx), -+ size_t n_fns, struct kdbus_test_env *env, void *ctx, -+ size_t runs) -+{ -+ struct race_thread *t; -+ size_t i, num, *order; -+ int *ret, r; -+ -+ t = calloc(sizeof(*t), n_fns); -+ ASSERT_RETURN(t != NULL); -+ -+ ret = calloc(sizeof(*ret), n_fns); -+ ASSERT_RETURN(ret != NULL); -+ -+ order = calloc(sizeof(*order), n_fns); -+ ASSERT_RETURN(order != NULL); -+ -+ for (num = 0; num < runs; ++num) { -+ ASSERT_RETURN(init_fn(env, ctx) == TEST_OK); -+ -+ for (i = 0; i < n_fns; ++i) { -+ ASSERT_RETURN(race_thread_init(&t[i]) == TEST_OK); -+ order[i] = i; -+ } -+ -+ /* random order */ -+ shuffle(order, n_fns); -+ for (i = 0; i < n_fns; ++i) -+ race_thread_run(&t[order[i]], fns[order[i]], env, ctx); -+ -+ for (i = 0; i < n_fns; ++i) { -+ ASSERT_RETURN(race_thread_join(&t[i]) == TEST_OK); -+ ret[i] = t[i].ret; -+ } -+ -+ ASSERT_RETURN(exit_fn(env, ctx, ret, n_fns) == TEST_OK); -+ } -+ -+ r = verify_fn(env, ctx); -+ free(order); -+ free(ret); -+ free(t); -+ return r; -+} -+ -+#define ASSERT_RACE(env, ctx, runs, init_fn, exit_fn, verify_fn, ...) ({\ -+ int (*fns[])(struct kdbus_test_env*, void*) = { \ -+ __VA_ARGS__ \ -+ }; \ -+ size_t cnt = sizeof(fns) / sizeof(*fns); \ -+ race_thread(init_fn, exit_fn, verify_fn, \ -+ fns, cnt, env, ctx, runs); \ -+ }) -+ -+#define TEST_RACE2(_name_, _runs_, _ctx_, _a_, _b_, _init_, _exit_, _verify_)\ -+ static int _name_ ## ___a(struct kdbus_test_env *env, void *_ctx)\ -+ { \ -+ __attribute__((__unused__)) _ctx_ *ctx = _ctx; \ -+ _a_; \ -+ return TEST_OK; \ -+ } \ -+ static int _name_ ## ___b(struct kdbus_test_env *env, void *_ctx)\ -+ { \ -+ __attribute__((__unused__)) _ctx_ *ctx = _ctx; \ -+ _b_; \ -+ return TEST_OK; \ -+ } \ -+ static int _name_ ## ___init(struct kdbus_test_env *env, \ -+ void *_ctx) \ -+ { \ -+ __attribute__((__unused__)) _ctx_ *ctx = _ctx; \ -+ _init_; \ -+ return TEST_OK; \ -+ } \ -+ static int _name_ ## ___exit(struct kdbus_test_env *env, \ -+ void *_ctx, int *ret, size_t n_ret) \ -+ { \ -+ __attribute__((__unused__)) _ctx_ *ctx = _ctx; \ -+ _exit_; \ -+ return TEST_OK; \ -+ } \ -+ static int _name_ ## ___verify(struct kdbus_test_env *env, \ -+ void *_ctx) \ -+ { \ -+ __attribute__((__unused__)) _ctx_ *ctx = _ctx; \ -+ _verify_; \ -+ return TEST_OK; \ -+ } \ -+ int _name_ (struct kdbus_test_env *env) { \ -+ _ctx_ ctx; \ -+ memset(&ctx, 0, sizeof(ctx)); \ -+ return ASSERT_RACE(env, &ctx, _runs_, \ -+ _name_ ## ___init, \ -+ _name_ ## ___exit, \ -+ _name_ ## ___verify, \ -+ _name_ ## ___a, \ -+ _name_ ## ___b); \ -+ } -+ -+/* -+ * Race Testing -+ * This file provides some rather trivial helpers to run multiple threads in -+ * parallel and test for races. You can define races with TEST_RACEX(), whereas -+ * 'X' is the number of threads you want. The arguments to this function should -+ * be code-blocks that are executed in the threads. Each code-block, if it -+ * does not contain a "return" statement, will implicitly return TEST_OK. -+ * -+ * The arguments are: -+ * @arg1: The name of the test to define -+ * @arg2: The number of runs -+ * @arg3: The datatype used as context across all test runs -+ * @arg4-@argN: The code-blocks for the threads to run. -+ * @argN+1: The code-block that is run before each test-run. Use it to -+ * initialize your contexts. -+ * @argN+2: The code-block that is run after each test-run. Use it to verify -+ * everything went as expected. -+ * @argN+3: The code-block that is executed after all runs are finished. Use it -+ * to verify the sum of results. -+ * -+ * Each function has "env" and "ctx" as variables implicitly defined. -+ * Furthermore, the function executed after the tests were run can access "ret", -+ * which is an array of return values of all threads. "n_ret" is the number of -+ * threads. -+ * -+ * Race testing is kinda nasty if you cannot place breakpoints yourself. -+ * Therefore, we run each thread multiple times and allow you to verify the -+ * results of all test-runs after we're finished. Usually, we try to verify all -+ * possible outcomes happened. However, no-one can predict how the scheduler -+ * ran each thread, even if we run 10k times. Furthermore, the execution of all -+ * threads is randomized by us, so we cannot predict how they're run. Therefore, -+ * we only return TEST_SKIP in those cases. This is not a hard-failure, but -+ * signals test-runners that something went unexpected. -+ */ -+ -+/* -+ * We run BYEBYE in parallel in two threads. Only one of them is allowed to -+ * succeed, the other one *MUST* return -EALREADY. -+ */ -+TEST_RACE2(kdbus_test_race_byebye, 100, int, -+ ({ -+ return ioctl(env->conn->fd, KDBUS_CMD_BYEBYE, 0) ? -errno : 0; -+ }), -+ ({ -+ return ioctl(env->conn->fd, KDBUS_CMD_BYEBYE, 0) ? -errno : 0; -+ }), -+ ({ -+ env->conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(env->conn); -+ }), -+ ({ -+ ASSERT_RETURN((ret[0] == 0 && ret[1] == -EALREADY) || -+ (ret[1] == 0 && ret[0] == -EALREADY)); -+ kdbus_conn_free(env->conn); -+ env->conn = NULL; -+ }), -+ ({ -+ })) -+ -+/* -+ * Run BYEBYE against MATCH_REMOVE. If BYEBYE is first, it returns 0 and -+ * MATCH_REMOVE must fail with ECONNRESET. If BYEBYE is last, it still succeeds -+ * but MATCH_REMOVE does, too. -+ * Run 10k times; at least on my machine it takes usually about ~100 runs to -+ * trigger ECONNRESET races. -+ */ -+TEST_RACE2(kdbus_test_race_byebye_match, 10000, -+ struct { -+ bool res1 : 1; -+ bool res2 : 1; -+ }, -+ ({ -+ return ioctl(env->conn->fd, KDBUS_CMD_BYEBYE, 0) ? -errno : 0; -+ }), -+ ({ -+ struct kdbus_cmd_match cmd = { }; -+ int ret; -+ -+ cmd.size = sizeof(cmd); -+ cmd.cookie = 0xdeadbeef; -+ ret = ioctl(env->conn->fd, KDBUS_CMD_MATCH_REMOVE, &cmd); -+ if (ret == 0 || errno == ENOENT) -+ return 0; -+ -+ return -errno; -+ }), -+ ({ -+ env->conn = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(env->conn); -+ }), -+ ({ -+ if (ret[0] == 0 && ret[1] == 0) { -+ /* MATCH_REMOVE ran first, then BYEBYE */ -+ ctx->res1 = true; -+ } else if (ret[0] == 0 && ret[1] == -ECONNRESET) { -+ /* BYEBYE ran first, then MATCH_REMOVE failed */ -+ ctx->res2 = true; -+ } else { -+ ASSERT_RETURN(0); -+ } -+ -+ kdbus_conn_free(env->conn); -+ env->conn = NULL; -+ }), -+ ({ -+ if (!ctx->res1 || !ctx->res2) -+ return TEST_SKIP; -+ })) ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-sync.c -@@ -0,0 +1,241 @@ -+#include <stdio.h> -+#include <string.h> -+#include <time.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <sys/ioctl.h> -+#include <pthread.h> -+#include <stdbool.h> -+#include <signal.h> -+#include <sys/wait.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+static struct kdbus_conn *conn_a, *conn_b; -+static unsigned int cookie = 0xdeadbeef; -+ -+static void nop_handler(int sig) {} -+ -+static int send_reply(const struct kdbus_conn *conn, -+ uint64_t reply_cookie, -+ uint64_t dst_id) -+{ -+ struct kdbus_msg *msg; -+ const char ref1[1024 * 128 + 3] = "0123456789_0"; -+ struct kdbus_item *item; -+ uint64_t size; -+ int ret; -+ -+ size = sizeof(struct kdbus_msg); -+ size += KDBUS_ITEM_SIZE(sizeof(struct kdbus_vec)); -+ -+ msg = malloc(size); -+ if (!msg) { -+ ret = -errno; -+ kdbus_printf("unable to malloc()!?\n"); -+ return ret; -+ } -+ -+ memset(msg, 0, size); -+ msg->size = size; -+ msg->src_id = conn->id; -+ msg->dst_id = dst_id; -+ msg->cookie_reply = reply_cookie; -+ msg->payload_type = KDBUS_PAYLOAD_DBUS; -+ -+ item = msg->items; -+ -+ item->type = KDBUS_ITEM_PAYLOAD_VEC; -+ item->size = KDBUS_ITEM_HEADER_SIZE + sizeof(struct kdbus_vec); -+ item->vec.address = (uintptr_t)&ref1; -+ item->vec.size = sizeof(ref1); -+ item = KDBUS_ITEM_NEXT(item); -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_SEND, msg); -+ if (ret < 0) { -+ ret = -errno; -+ kdbus_printf("error sending message: %d (%m)\n", ret); -+ return ret; -+ } -+ -+ free(msg); -+ -+ return 0; -+} -+ -+static int interrupt_sync(struct kdbus_conn *conn_src, -+ struct kdbus_conn *conn_dst) -+{ -+ pid_t pid; -+ int ret, status; -+ struct kdbus_msg *msg = NULL; -+ struct sigaction sa = { -+ .sa_handler = nop_handler, -+ .sa_flags = SA_NOCLDSTOP|SA_RESTART, -+ }; -+ -+ cookie++; -+ pid = fork(); -+ ASSERT_RETURN_VAL(pid >= 0, pid); -+ -+ if (pid == 0) { -+ ret = sigaction(SIGINT, &sa, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = kdbus_msg_send(conn_dst, NULL, cookie, -+ KDBUS_MSG_FLAGS_EXPECT_REPLY | -+ KDBUS_MSG_FLAGS_SYNC_REPLY, -+ 100000000ULL, 0, conn_src->id); -+ ASSERT_EXIT(ret == -ETIMEDOUT); -+ -+ _exit(EXIT_SUCCESS); -+ } -+ -+ ret = kdbus_msg_recv_poll(conn_src, 100, &msg, NULL); -+ ASSERT_RETURN(ret == 0 && msg->cookie == cookie); -+ -+ kdbus_msg_free(msg); -+ -+ ret = kill(pid, SIGINT); -+ ASSERT_RETURN_VAL(ret == 0, ret); -+ -+ ret = waitpid(pid, &status, 0); -+ ASSERT_RETURN_VAL(ret >= 0, ret); -+ -+ if (WIFSIGNALED(status)) -+ return TEST_ERR; -+ -+ ret = kdbus_msg_recv_poll(conn_src, 100, NULL, NULL); -+ ASSERT_RETURN(ret == -ETIMEDOUT); -+ -+ return (status == EXIT_SUCCESS) ? TEST_OK : TEST_ERR; -+} -+ -+static void *run_thread_reply(void *data) -+{ -+ int ret; -+ -+ ret = kdbus_msg_recv_poll(conn_a, 3000, NULL, NULL); -+ if (ret == 0) { -+ kdbus_printf("Thread received message, sending reply ...\n"); -+ send_reply(conn_a, cookie, conn_b->id); -+ } -+ -+ pthread_exit(NULL); -+ return NULL; -+} -+ -+int kdbus_test_sync_reply(struct kdbus_test_env *env) -+{ -+ pthread_t thread; -+ int ret; -+ -+ conn_a = kdbus_hello(env->buspath, 0, NULL, 0); -+ conn_b = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn_a && conn_b); -+ -+ pthread_create(&thread, NULL, run_thread_reply, NULL); -+ -+ ret = kdbus_msg_send(conn_b, NULL, cookie, -+ KDBUS_MSG_FLAGS_EXPECT_REPLY | -+ KDBUS_MSG_FLAGS_SYNC_REPLY, -+ 5000000000ULL, 0, conn_a->id); -+ -+ pthread_join(thread, NULL); -+ ASSERT_RETURN(ret == 0); -+ -+ ret = interrupt_sync(conn_a, conn_b); -+ ASSERT_RETURN(ret == 0); -+ -+ kdbus_printf("-- closing bus connections\n"); -+ -+ kdbus_conn_free(conn_a); -+ kdbus_conn_free(conn_b); -+ -+ return TEST_OK; -+} -+ -+#define BYEBYE_ME ((void*)0L) -+#define BYEBYE_THEM ((void*)1L) -+ -+static void *run_thread_byebye(void *data) -+{ -+ int ret; -+ -+ ret = kdbus_msg_recv_poll(conn_a, 3000, NULL, NULL); -+ if (ret == 0) { -+ kdbus_printf("Thread received message, invoking BYEBYE ...\n"); -+ kdbus_msg_recv(conn_a, NULL, NULL); -+ if (data == BYEBYE_ME) -+ ioctl(conn_b->fd, KDBUS_CMD_BYEBYE, 0); -+ else if (data == BYEBYE_THEM) -+ ioctl(conn_a->fd, KDBUS_CMD_BYEBYE, 0); -+ } -+ -+ pthread_exit(NULL); -+ return NULL; -+} -+ -+int kdbus_test_sync_byebye(struct kdbus_test_env *env) -+{ -+ pthread_t thread; -+ int ret; -+ -+ /* -+ * This sends a synchronous message to a thread, which waits until it -+ * received the message and then invokes BYEBYE on the *ORIGINAL* -+ * connection. That is, on the same connection that synchronously waits -+ * for an reply. -+ * This should properly wake the connection up and cause ECONNRESET as -+ * the connection is disconnected now. -+ * -+ * The second time, we do the same but invoke BYEBYE on the *TARGET* -+ * connection. This should also wake up the synchronous sender as the -+ * reply cannot be sent by a disconnected target. -+ */ -+ -+ conn_a = kdbus_hello(env->buspath, 0, NULL, 0); -+ conn_b = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn_a && conn_b); -+ -+ pthread_create(&thread, NULL, run_thread_byebye, BYEBYE_ME); -+ -+ ret = kdbus_msg_send(conn_b, NULL, cookie, -+ KDBUS_MSG_FLAGS_EXPECT_REPLY | -+ KDBUS_MSG_FLAGS_SYNC_REPLY, -+ 5000000000ULL, 0, conn_a->id); -+ -+ ASSERT_RETURN(ret == -ECONNRESET); -+ -+ pthread_join(thread, NULL); -+ -+ kdbus_conn_free(conn_a); -+ kdbus_conn_free(conn_b); -+ -+ conn_a = kdbus_hello(env->buspath, 0, NULL, 0); -+ conn_b = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn_a && conn_b); -+ -+ pthread_create(&thread, NULL, run_thread_byebye, BYEBYE_THEM); -+ -+ ret = kdbus_msg_send(conn_b, NULL, cookie, -+ KDBUS_MSG_FLAGS_EXPECT_REPLY | -+ KDBUS_MSG_FLAGS_SYNC_REPLY, -+ 5000000000ULL, 0, conn_a->id); -+ -+ ASSERT_RETURN(ret == -EPIPE); -+ -+ pthread_join(thread, NULL); -+ -+ kdbus_conn_free(conn_a); -+ kdbus_conn_free(conn_b); -+ -+ return TEST_OK; -+} ---- /dev/null -+++ b/tools/testing/selftests/kdbus/test-timeout.c -@@ -0,0 +1,97 @@ -+#include <stdio.h> -+#include <string.h> -+#include <time.h> -+#include <fcntl.h> -+#include <stdlib.h> -+#include <stddef.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <errno.h> -+#include <assert.h> -+#include <poll.h> -+#include <sys/ioctl.h> -+#include <stdbool.h> -+ -+#include "kdbus-test.h" -+#include "kdbus-util.h" -+#include "kdbus-enum.h" -+ -+int timeout_msg_recv(struct kdbus_conn *conn, uint64_t *expected) -+{ -+ struct kdbus_cmd_recv recv = {}; -+ struct kdbus_msg *msg; -+ int ret; -+ -+ ret = ioctl(conn->fd, KDBUS_CMD_MSG_RECV, &recv); -+ if (ret < 0) { -+ kdbus_printf("error receiving message: %d (%m)\n", ret); -+ return -errno; -+ } -+ -+ msg = (struct kdbus_msg *)(conn->buf + recv.offset); -+ -+ ASSERT_RETURN_VAL(msg->payload_type == KDBUS_PAYLOAD_KERNEL, -EINVAL); -+ ASSERT_RETURN_VAL(msg->src_id == KDBUS_SRC_ID_KERNEL, -EINVAL); -+ ASSERT_RETURN_VAL(msg->dst_id == conn->id, -EINVAL); -+ -+ *expected &= ~(1ULL << msg->cookie_reply); -+ kdbus_printf("Got message timeout for cookie %llu\n", -+ msg->cookie_reply); -+ -+ ret = kdbus_free(conn, recv.offset); -+ if (ret < 0) -+ return ret; -+ -+ return 0; -+} -+ -+int kdbus_test_timeout(struct kdbus_test_env *env) -+{ -+ struct kdbus_conn *conn_a, *conn_b; -+ struct pollfd fd; -+ int ret, i, n_msgs = 4; -+ uint64_t expected = 0; -+ -+ conn_a = kdbus_hello(env->buspath, 0, NULL, 0); -+ conn_b = kdbus_hello(env->buspath, 0, NULL, 0); -+ ASSERT_RETURN(conn_a && conn_b); -+ -+ fd.fd = conn_b->fd; -+ -+ /* -+ * send messages that expect a reply (within 100 msec), -+ * but never answer it. -+ */ -+ for (i = 0; i < n_msgs; i++) { -+ kdbus_printf("Sending message with cookie %u ...\n", i); -+ ASSERT_RETURN(kdbus_msg_send(conn_b, NULL, i, -+ KDBUS_MSG_FLAGS_EXPECT_REPLY, -+ (i + 1) * 100ULL * 1000000ULL, 0, -+ conn_a->id) == 0); -+ expected |= 1ULL << i; -+ } -+ -+ for (;;) { -+ fd.events = POLLIN | POLLPRI | POLLHUP; -+ fd.revents = 0; -+ -+ ret = poll(&fd, 1, (n_msgs + 1) * 100); -+ if (ret == 0) -+ kdbus_printf("--- timeout\n"); -+ if (ret <= 0) -+ break; -+ -+ if (fd.revents & POLLIN) -+ ASSERT_RETURN(!timeout_msg_recv(conn_b, &expected)); -+ -+ if (expected == 0) -+ break; -+ } -+ -+ ASSERT_RETURN(expected == 0); -+ -+ kdbus_conn_free(conn_a); -+ kdbus_conn_free(conn_b); -+ -+ return TEST_OK; -+} diff --git a/d01.patch b/d01.patch index 40c12ee930460b..395a7ff46da241 100644 --- a/d01.patch +++ b/d01.patch @@ -11,7 +11,7 @@ --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c -@@ -1985,7 +1985,7 @@ struct hid_dynid { +@@ -1986,7 +1986,7 @@ struct hid_dynid { * Adds a new dynamic hid device ID to this driver, * and causes the driver to probe for all devices again. */ @@ -20,7 +20,7 @@ size_t count) { struct hid_driver *hdrv = container_of(drv, struct hid_driver, driver); -@@ -2017,7 +2017,13 @@ static ssize_t store_new_id(struct devic +@@ -2018,7 +2018,13 @@ static ssize_t store_new_id(struct devic return ret ? : count; } @@ -35,7 +35,7 @@ static void hid_free_dynids(struct hid_driver *hdrv) { -@@ -2177,6 +2183,7 @@ static int hid_uevent(struct device *dev +@@ -2178,6 +2184,7 @@ static int hid_uevent(struct device *dev static struct bus_type hid_bus_type = { .name = "hid", .dev_groups = hid_dev_groups, @@ -43,7 +43,7 @@ .match = hid_bus_match, .probe = hid_device_probe, .remove = hid_device_remove, -@@ -2624,8 +2631,6 @@ EXPORT_SYMBOL_GPL(hid_destroy_device); +@@ -2625,8 +2632,6 @@ EXPORT_SYMBOL_GPL(hid_destroy_device); int __hid_register_driver(struct hid_driver *hdrv, struct module *owner, const char *mod_name) { @@ -52,7 +52,7 @@ hdrv->driver.name = hdrv->name; hdrv->driver.bus = &hid_bus_type; hdrv->driver.owner = owner; -@@ -2634,21 +2639,12 @@ int __hid_register_driver(struct hid_dri +@@ -2635,21 +2640,12 @@ int __hid_register_driver(struct hid_dri INIT_LIST_HEAD(&hdrv->dyn_list); spin_lock_init(&hdrv->dyn_lock); diff --git a/pci-fix-name-of-enable-sysfs-file.patch b/pci-fix-name-of-enable-sysfs-file.patch deleted file mode 100644 index e22c174afcf7c7..00000000000000 --- a/pci-fix-name-of-enable-sysfs-file.patch +++ /dev/null @@ -1,57 +0,0 @@ -From foo@baz Thu Oct 30 09:25:38 PDT 2014 -Date: Thu, 30 Oct 2014 09:25:38 -0700 -To: Greg KH <gregkh@linuxfoundation.org> -From: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -Subject: [PATCH] PCI: fix name of 'enable' sysfs file - -Back in commit 5136b2da770d ("PCI: convert bus code to use dev_groups"), -I misstyped the 'enable' sysfs file as 'enabled', which broke the -userspace api. This patch fixes that issue by renaming the file back. - -Reported-by: Jeff Epler <jepler@unpythonic.net> -Cc: stable <stable@vger.kernel.org> # 3.13 -Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> - - ---- - drivers/pci/pci-sysfs.c | 8 ++++---- - 1 file changed, 4 insertions(+), 4 deletions(-) - ---- a/drivers/pci/pci-sysfs.c -+++ b/drivers/pci/pci-sysfs.c -@@ -185,7 +185,7 @@ static ssize_t modalias_show(struct devi - } - static DEVICE_ATTR_RO(modalias); - --static ssize_t enabled_store(struct device *dev, struct device_attribute *attr, -+static ssize_t enable_store(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) - { - struct pci_dev *pdev = to_pci_dev(dev); -@@ -210,7 +210,7 @@ static ssize_t enabled_store(struct devi - return result < 0 ? result : count; - } - --static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, -+static ssize_t enable_show(struct device *dev, struct device_attribute *attr, - char *buf) - { - struct pci_dev *pdev; -@@ -218,7 +218,7 @@ static ssize_t enabled_show(struct devic - pdev = to_pci_dev(dev); - return sprintf(buf, "%u\n", atomic_read(&pdev->enable_cnt)); - } --static DEVICE_ATTR_RW(enabled); -+static DEVICE_ATTR_RW(enable); - - #ifdef CONFIG_NUMA - static ssize_t numa_node_show(struct device *dev, struct device_attribute *attr, -@@ -563,7 +563,7 @@ static struct attribute *pci_dev_attrs[] - #endif - &dev_attr_dma_mask_bits.attr, - &dev_attr_consistent_dma_mask_bits.attr, -- &dev_attr_enabled.attr, -+ &dev_attr_enable.attr, - &dev_attr_broken_parity_status.attr, - &dev_attr_msi_bus.attr, - #if defined(CONFIG_PM_RUNTIME) && defined(CONFIG_ACPI) @@ -1,16 +1,3 @@ -pci-fix-name-of-enable-sysfs-file.patch -0001-kdbus-add-documentation.patch -0002-kdbus-add-header-file.patch -0003-kdbus-add-driver-skeleton-ioctl-entry-points-and-uti.patch -0004-kdbus-add-connection-pool-implementation.patch -0005-kdbus-add-connection-queue-handling-and-message-vali.patch -0006-kdbus-add-code-to-gather-metadata.patch -0007-kdbus-add-code-for-notifications-and-matches.patch -0008-kdbus-add-code-for-buses-domains-and-endpoints.patch -0009-kdbus-add-name-registry-implementation.patch -0010-kdbus-add-policy-database-implementation.patch -0011-kdbus-add-Makefile-Kconfig-and-MAINTAINERS-entry.patch -0012-kdbus-add-selftests.patch csdio2.patch input-xpad-use-proper-endpoint-type.patch input-xpad-set-the-leds-properly-on-xbox-wireless-controllers.patch |